Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
62.50% covered (warning)
62.50%
30 / 48
58.33% covered (warning)
58.33%
7 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
TextContent
63.83% covered (warning)
63.83%
30 / 47
58.33% covered (warning)
58.33%
7 / 12
41.87
0.00% covered (danger)
0.00%
0 / 1
 __construct
50.00% covered (danger)
50.00%
4 / 8
0.00% covered (danger)
0.00%
0 / 1
6.00
 copy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTextForSummary
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getSize
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isCountable
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 getNativeData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getText
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTextForSearchIndex
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getWikitextForTransclusion
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 normalizeLineEndings
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 diff
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 convert
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
3.02
1<?php
2/**
3 * Content object implementation for representing flat text.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @since 1.21
21 *
22 * @file
23 * @ingroup Content
24 *
25 * @author Daniel Kinzler
26 */
27
28namespace MediaWiki\Content;
29
30use InvalidArgumentException;
31use MediaWiki\Language\Language;
32use MediaWiki\MainConfigNames;
33use MediaWiki\MediaWikiServices;
34use MWUnknownContentModelException;
35use Wikimedia\Diff\Diff;
36
37/**
38 * Content object implementation for representing flat text.
39 *
40 * TextContent instances are immutable
41 *
42 * @newable
43 * @stable to extend
44 * @ingroup Content
45 */
46class TextContent extends AbstractContent {
47
48    /**
49     * @var string
50     */
51    protected $mText;
52
53    /**
54     * @stable to call
55     * @param string $text
56     * @param string $model_id
57     */
58    public function __construct( $text, $model_id = CONTENT_MODEL_TEXT ) {
59        parent::__construct( $model_id );
60
61        if ( $text === null || $text === false ) {
62            wfWarn( "TextContent constructed with \$text = " . var_export( $text, true ) . "! "
63                . "This may indicate an error in the caller's scope.", 2 );
64
65            $text = '';
66        }
67
68        if ( !is_string( $text ) ) {
69            throw new InvalidArgumentException( "TextContent expects a string in the constructor." );
70        }
71
72        $this->mText = $text;
73    }
74
75    /**
76     * @note Mutable subclasses MUST override this to return a copy!
77     *
78     * @return Content $this
79     */
80    public function copy() {
81        return $this; # NOTE: this is ok since TextContent are immutable.
82    }
83
84    /**
85     * @stable to override
86     *
87     * @param int $maxlength
88     *
89     * @return string
90     */
91    public function getTextForSummary( $maxlength = 250 ) {
92        $text = $this->getText();
93
94        $truncatedtext = MediaWikiServices::getInstance()->getContentLanguage()->
95            truncateForDatabase( preg_replace( "/[\n\r]/", ' ', $text ), max( 0, $maxlength ) );
96
97        return $truncatedtext;
98    }
99
100    /**
101     * Returns the text's size in bytes.
102     *
103     * @stable to override
104     *
105     * @return int
106     */
107    public function getSize() {
108        $text = $this->getText();
109
110        return strlen( $text );
111    }
112
113    /**
114     * Returns true if this content is not a redirect, and $wgArticleCountMethod
115     * is "any".
116     *
117     * @stable to override
118     *
119     * @param bool|null $hasLinks If it is known whether this content contains links,
120     * provide this information here, to avoid redundant parsing to find out.
121     *
122     * @return bool
123     */
124    public function isCountable( $hasLinks = null ) {
125        $articleCountMethod = MediaWikiServices::getInstance()->getMainConfig()->get(
126            MainConfigNames::ArticleCountMethod );
127
128        if ( $this->isRedirect() ) {
129            return false;
130        }
131
132        if ( $articleCountMethod === 'any' ) {
133            return true;
134        }
135
136        return false;
137    }
138
139    /**
140     * Returns the text represented by this Content object, as a string.
141     *
142     * @deprecated since 1.33 use getText() instead.
143     *
144     * @return string The raw text. Subclasses may guarantee a specific syntax here.
145     */
146    public function getNativeData() {
147        return $this->getText();
148    }
149
150    /**
151     * Returns the text represented by this Content object, as a string.
152     *
153     * @since 1.33
154     * @note This method should not be overwritten by subclasses. If a subclass find itself in
155     * need to override this method, it should probably not be based on TextContent, but
156     * should rather extend AbstractContent instead.
157     *
158     * @return string The raw text.
159     */
160    public function getText() {
161        return $this->mText;
162    }
163
164    /**
165     * Returns the text represented by this Content object, as a string.
166     *
167     * @stable to override
168     *
169     * @return string The raw text.
170     */
171    public function getTextForSearchIndex() {
172        return $this->getText();
173    }
174
175    /**
176     * Returns attempts to convert this content object to wikitext,
177     * and then returns the text string. The conversion may be lossy.
178     *
179     * @stable to override
180     *
181     * @note this allows any text-based content to be transcluded as if it was wikitext.
182     *
183     * @return string|false The raw text, or false if the conversion failed.
184     */
185    public function getWikitextForTransclusion() {
186        /** @var WikitextContent $wikitext */
187        $wikitext = $this->convert( CONTENT_MODEL_WIKITEXT, 'lossy' );
188        '@phan-var WikitextContent $wikitext';
189
190        if ( $wikitext ) {
191            return $wikitext->getText();
192        } else {
193            return false;
194        }
195    }
196
197    /**
198     * Do a "\r\n" -> "\n" and "\r" -> "\n" transformation
199     * as well as trim trailing whitespace
200     *
201     * This was formerly part of Parser::preSaveTransform, but
202     * for non-wikitext content models they probably still want
203     * to normalize line endings without all of the other PST
204     * changes.
205     *
206     * @since 1.28
207     * @param string $text
208     * @return string
209     */
210    public static function normalizeLineEndings( $text ) {
211        return str_replace( [ "\r\n", "\r" ], "\n", rtrim( $text ) );
212    }
213
214    /**
215     * Diff this content object with another content object.
216     *
217     * @stable to override
218     * @since 1.21
219     *
220     * @param Content $that The other content object to compare this content object to.
221     * @param Language|null $lang The language object to use for text segmentation.
222     *    If not given, the content language is used.
223     *
224     * @return Diff A diff representing the changes that would have to be
225     *    made to this content object to make it equal to $that.
226     */
227    public function diff( Content $that, Language $lang = null ) {
228        $this->checkModelID( $that->getModel() );
229        /** @var self $that */
230        '@phan-var self $that';
231        // @todo could implement this in DifferenceEngine and just delegate here?
232
233        if ( !$lang ) {
234            $lang = MediaWikiServices::getInstance()->getContentLanguage();
235        }
236
237        $otext = $this->getText();
238        $ntext = $that->getText();
239
240        # Note: Use native PHP diff, external engines don't give us abstract output
241        $ota = explode( "\n", $lang->segmentForDiff( $otext ) );
242        $nta = explode( "\n", $lang->segmentForDiff( $ntext ) );
243
244        $diff = new Diff( $ota, $nta );
245
246        return $diff;
247    }
248
249    /**
250     * This implementation provides lossless conversion between content models based
251     * on TextContent.
252     *
253     * @stable to override
254     *
255     * @param string $toModel The desired content model, use the CONTENT_MODEL_XXX flags.
256     * @param string $lossy Flag, set to "lossy" to allow lossy conversion. If lossy conversion is not
257     *     allowed, full round-trip conversion is expected to work without losing information.
258     *
259     * @return Content|false A content object with the content model $toModel, or false if that
260     *     conversion is not supported.
261     * @throws MWUnknownContentModelException
262     *
263     * @see Content::convert()
264     */
265    public function convert( $toModel, $lossy = '' ) {
266        $converted = parent::convert( $toModel, $lossy );
267
268        if ( $converted !== false ) {
269            return $converted;
270        }
271
272        $toHandler = $this->getContentHandlerFactory()->getContentHandler( $toModel );
273
274        if ( $toHandler instanceof TextContentHandler ) {
275            // NOTE: ignore content serialization format - it's just text anyway.
276            $text = $this->getText();
277            $converted = $toHandler->unserializeContent( $text );
278        }
279
280        return $converted;
281    }
282
283}
284/** @deprecated class alias since 1.43 */
285class_alias( TextContent::class, 'TextContent' );