Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractContent
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 23
992
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getModel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 checkModelID
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getContentHandler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getContentHandlerFactory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefaultFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSupportedFormats
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isSupportedFormat
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 checkFormat
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 serialize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNativeData
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 isEmpty
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isValid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 equals
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 equalsInternal
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRedirectTarget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isRedirect
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 updateRedirect
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSection
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 replaceSection
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addSectionHeader
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 matchMagicWord
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 convert
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * A content object represents page content, e.g. the text to show on a page.
4 * Content objects have no knowledge about how they relate to Wiki pages.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
20 *
21 * @since 1.21
22 *
23 * @file
24 * @ingroup Content
25 *
26 * @author Daniel Kinzler
27 */
28
29namespace MediaWiki\Content;
30
31use LogicException;
32use MediaWiki\HookContainer\HookRunner;
33use MediaWiki\MediaWikiServices;
34use MediaWiki\Parser\MagicWord;
35use MediaWiki\Title\Title;
36use MWException;
37
38/**
39 * Base implementation for content objects.
40 *
41 * @stable to extend
42 *
43 * @ingroup Content
44 */
45abstract class AbstractContent implements Content {
46    /**
47     * Name of the content model this Content object represents.
48     * Use with CONTENT_MODEL_XXX constants
49     *
50     * @since 1.21
51     *
52     * @var string
53     */
54    protected $model_id;
55
56    /**
57     * @stable to call
58     *
59     * @param string|null $modelId
60     *
61     * @since 1.21
62     */
63    public function __construct( $modelId = null ) {
64        $this->model_id = $modelId;
65    }
66
67    /**
68     * @since 1.21
69     *
70     * @see Content::getModel
71     * @return string
72     */
73    public function getModel() {
74        return $this->model_id;
75    }
76
77    /**
78     * @since 1.21
79     *
80     * @param string $modelId The model to check
81     *
82     * @throws MWException If the provided ID is not the ID of the content model supported by this
83     * Content object.
84     */
85    protected function checkModelID( $modelId ) {
86        if ( $modelId !== $this->model_id ) {
87            throw new MWException(
88                "Bad content model: " .
89                "expected {$this->model_id} " .
90                "but got $modelId."
91            );
92        }
93    }
94
95    /**
96     * @since 1.21
97     *
98     * @see Content::getContentHandler
99     * @return ContentHandler
100     */
101    public function getContentHandler() {
102        return $this->getContentHandlerFactory()->getContentHandler( $this->getModel() );
103    }
104
105    /**
106     * @return IContentHandlerFactory
107     */
108    protected function getContentHandlerFactory(): IContentHandlerFactory {
109        return MediaWikiServices::getInstance()->getContentHandlerFactory();
110    }
111
112    /**
113     * @since 1.21
114     *
115     * @see Content::getDefaultFormat
116     * @return string
117     */
118    public function getDefaultFormat() {
119        return $this->getContentHandler()->getDefaultFormat();
120    }
121
122    /**
123     * @since 1.21
124     *
125     * @see Content::getSupportedFormats
126     * @return string[]
127     */
128    public function getSupportedFormats() {
129        return $this->getContentHandler()->getSupportedFormats();
130    }
131
132    /**
133     * @since 1.21
134     *
135     * @param string $format
136     *
137     * @return bool
138     *
139     * @see Content::isSupportedFormat
140     */
141    public function isSupportedFormat( $format ) {
142        if ( !$format ) {
143            return true; // this means "use the default"
144        }
145
146        return $this->getContentHandler()->isSupportedFormat( $format );
147    }
148
149    /**
150     * @since 1.21
151     *
152     * @param string $format The serialization format to check.
153     *
154     * @throws MWException If the format is not supported by this content handler.
155     */
156    protected function checkFormat( $format ) {
157        if ( !$this->isSupportedFormat( $format ) ) {
158            throw new MWException(
159                "Format $format is not supported for content model " .
160                $this->getModel()
161            );
162        }
163    }
164
165    /**
166     * @stable to override
167     * @since 1.21
168     *
169     * @param string|null $format
170     *
171     * @return string
172     *
173     * @see Content::serialize
174     */
175    public function serialize( $format = null ) {
176        return $this->getContentHandler()->serializeContent( $this, $format );
177    }
178
179    /**
180     * Returns native representation of the data. Interpretation depends on
181     * the data model used, as given by getDataModel().
182     *
183     * @stable to override
184     * @since 1.21
185     *
186     * @deprecated since 1.33. Use getText() for TextContent instances.
187     *             For other content models, use specialized getters.
188     *             Emitting deprecation warnings since 1.41.
189     *
190     * @return mixed The native representation of the content. Could be a
191     *    string, a nested array structure, an object, a binary blob...
192     *    anything, really.
193     * @throws LogicException
194     *
195     * @note Caller must be aware of content model!
196     */
197    public function getNativeData() {
198        wfDeprecated( __METHOD__, '1.33' );
199        throw new LogicException( __METHOD__ . ': not implemented' );
200    }
201
202    /**
203     * @stable to override
204     * @since 1.21
205     *
206     * @return bool
207     *
208     * @see Content::isEmpty
209     */
210    public function isEmpty() {
211        return $this->getSize() === 0;
212    }
213
214    /**
215     * Subclasses may override this to implement (light weight) validation.
216     *
217     * @stable to override
218     * @since 1.21
219     *
220     * @return bool Always true.
221     *
222     * @see Content::isValid
223     */
224    public function isValid() {
225        return true;
226    }
227
228    /**
229     * Decides whether two Content objects are equal.
230     * Two Content objects MUST not be considered equal if they do not share the same content model.
231     * Two Content objects that are equal SHOULD have the same serialization.
232     *
233     * This default implementation relies on equalsInternal() to determine whether the
234     * Content objects are logically equivalent. Subclasses that need to implement a custom
235     * equality check should consider overriding equalsInternal(). Subclasses that override
236     * equals() itself MUST make sure that the implementation returns false for $that === null,
237     * and true for $that === this. It MUST also return false if $that does not have the same
238     * content model.
239     *
240     * @stable to override
241     * @since 1.21
242     *
243     * @param Content|null $that
244     *
245     * @return bool
246     *
247     * @see Content::equals
248     */
249    public function equals( Content $that = null ) {
250        if ( $that === null ) {
251            return false;
252        }
253
254        if ( $that === $this ) {
255            return true;
256        }
257
258        if ( $that->getModel() !== $this->getModel() ) {
259            return false;
260        }
261
262        // For type safety. Needed for odd cases like non-TextContents using CONTENT_MODEL_WIKITEXT
263        if ( get_class( $that ) !== get_class( $this ) ) {
264            return false;
265        }
266
267        return $this->equalsInternal( $that );
268    }
269
270    /**
271     * Checks whether $that is logically equal to this Content object.
272     *
273     * This method can be overwritten by subclasses that need to implement custom
274     * equality checks.
275     *
276     * This default implementation checks whether the serializations
277     * of $this and $that are the same: $this->serialize() === $that->serialize()
278     *
279     * Implementors can assume that $that is an instance of the same class
280     * as the present Content object, as long as equalsInternal() is only called
281     * by the standard implementation of equals().
282     *
283     * @note Do not call this method directly, call equals() instead.
284     *
285     * @stable to override
286     *
287     * @param Content $that
288     * @return bool
289     */
290    protected function equalsInternal( Content $that ) {
291        return $this->serialize() === $that->serialize();
292    }
293
294    /**
295     * Subclasses that implement redirects should override this.
296     *
297     * @stable to override
298     * @since 1.21
299     *
300     * @return Title|null
301     *
302     * @see Content::getRedirectTarget
303     */
304    public function getRedirectTarget() {
305        return null;
306    }
307
308    /**
309     * @since 1.21
310     *
311     * @return bool
312     *
313     * @see Content::isRedirect
314     */
315    public function isRedirect() {
316        return $this->getRedirectTarget() !== null;
317    }
318
319    /**
320     * This default implementation always returns $this.
321     * Subclasses that implement redirects should override this.
322     *
323     * @stable to override
324     * @since 1.21
325     *
326     * @param Title $target
327     *
328     * @return Content $this
329     *
330     * @see Content::updateRedirect
331     */
332    public function updateRedirect( Title $target ) {
333        return $this;
334    }
335
336    /**
337     * @stable to override
338     * @since 1.21
339     *
340     * @param string|int $sectionId
341     * @return null
342     *
343     * @see Content::getSection
344     */
345    public function getSection( $sectionId ) {
346        return null;
347    }
348
349    /**
350     * @stable to override
351     * @since 1.21
352     *
353     * @param string|int|null|false $sectionId
354     * @param Content $with
355     * @param string $sectionTitle
356     * @return null
357     *
358     * @see Content::replaceSection
359     */
360    public function replaceSection( $sectionId, Content $with, $sectionTitle = '' ) {
361        return null;
362    }
363
364    /**
365     * @stable to override
366     * @since 1.21
367     *
368     * @param string $header
369     * @return Content $this
370     *
371     * @see Content::addSectionHeader
372     */
373    public function addSectionHeader( $header ) {
374        return $this;
375    }
376
377    /**
378     * This default implementation always returns false. Subclasses may override
379     * this to supply matching logic.
380     *
381     * @stable to override
382     * @since 1.21
383     *
384     * @param MagicWord $word
385     *
386     * @return bool Always false.
387     *
388     * @see Content::matchMagicWord
389     */
390    public function matchMagicWord( MagicWord $word ) {
391        return false;
392    }
393
394    /**
395     * This base implementation calls the hook ConvertContent to enable custom conversions.
396     * Subclasses may override this to implement conversion for "their" content model.
397     *
398     * @stable to override
399     *
400     * @param string $toModel
401     * @param string $lossy
402     *
403     * @return Content|false
404     *
405     * @see Content::convert()
406     */
407    public function convert( $toModel, $lossy = '' ) {
408        if ( $this->getModel() === $toModel ) {
409            // nothing to do, shorten out.
410            return $this;
411        }
412
413        $lossy = ( $lossy === 'lossy' ); // string flag, convert to boolean for convenience
414        $result = false;
415
416        ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
417            ->onConvertContent( $this, $toModel, $lossy, $result );
418
419        return $result;
420    }
421
422}
423
424/** @deprecated class alias since 1.43 */
425class_alias( AbstractContent::class, 'AbstractContent' );