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