Completed
Branch master (62f6c6)
by
unknown
21:31
created

CoreParserFunctions   F

Complexity

Total Complexity 234

Size/Duplication

Total Lines 1292
Duplicated Lines 18.81 %

Coupling/Cohesion

Components 2
Dependencies 22

Importance

Changes 2
Bugs 0 Features 1
Metric Value
dl 243
loc 1292
rs 0.6314
c 2
b 0
f 1
wmc 234
lcom 2
cbo 22

80 Methods

Rating   Name   Duplication   Size   Complexity  
D displaytitle() 10 85 14
A pagesize() 0 16 4
A register() 0 56 4
A intFunction() 0 15 3
A formatDate() 0 17 3
A ns() 0 13 4
A nse() 0 7 2
B urlencode() 0 27 5
A lcfirst() 0 4 1
A ucfirst() 0 4 1
A lc() 0 4 1
A uc() 0 4 1
A localurl() 0 3 1
A localurle() 8 8 2
A fullurl() 0 3 1
A fullurle() 8 8 2
A canonicalurl() 0 3 1
A canonicalurle() 8 8 2
B urlFunction() 0 24 5
A formatnum() 0 10 3
A grammar() 0 4 1
C gender() 0 32 8
A plural() 0 6 2
A bidi() 0 3 1
A matchAgainstMagicword() 0 8 2
A formatRaw() 0 8 2
A numberofpages() 0 3 1
A numberofusers() 0 3 1
A numberofactiveusers() 0 3 1
A numberofarticles() 0 3 1
A numberoffiles() 0 3 1
A numberofadmins() 0 3 1
A numberofedits() 0 3 1
A pagesinnamespace() 0 3 1
A numberingroup() 0 3 1
A mwnamespace() 0 7 2
A namespacee() 7 7 2
A namespacenumber() 0 7 2
A talkspace() 0 7 3
A talkspacee() 0 7 3
A subjectspace() 7 7 2
A subjectspacee() 7 7 2
A pagename() 7 7 2
A pagenamee() 7 7 2
A fullpagename() 7 7 3
A fullpagenamee() 0 7 3
A subpagename() 7 7 2
A subpagenamee() 7 7 2
A rootpagename() 7 7 2
A rootpagenamee() 7 7 2
A basepagename() 7 7 2
A basepagenamee() 7 7 2
A talkpagename() 0 7 3
A talkpagenamee() 0 7 3
A subjectpagename() 7 7 2
A subjectpagenamee() 7 7 2
B pagesincategory() 0 57 7
A protectionlevel() 13 13 4
B protectionexpiry() 16 16 5
A language() 0 6 2
B pad() 0 27 4
A padleft() 0 3 1
A padright() 0 3 1
A anchorencode() 0 4 1
A special() 0 11 3
A speciale() 0 3 1
C defaultsort() 10 29 8
C filepath() 0 33 7
D tagObj() 0 46 10
D getCachedRevisionObject() 0 33 10
C pageid() 0 37 8
A revisionid() 9 9 3
A revisionday() 9 9 3
A revisionday2() 9 9 3
A revisionmonth() 9 9 3
A revisionmonth1() 9 9 3
A revisionyear() 9 9 3
A revisiontimestamp() 9 9 3
A revisionuser() 9 9 3
B cascadingsources() 0 17 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CoreParserFunctions often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CoreParserFunctions, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Parser functions provided by MediaWiki core
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
 * @file
21
 * @ingroup Parser
22
 */
23
24
/**
25
 * Various core parser functions, registered in Parser::firstCallInit()
26
 * @ingroup Parser
27
 */
28
class CoreParserFunctions {
29
	/**
30
	 * @param Parser $parser
31
	 * @return void
32
	 */
33
	public static function register( $parser ) {
34
		global $wgAllowDisplayTitle, $wgAllowSlowParserFunctions;
35
36
		# Syntax for arguments (see Parser::setFunctionHook):
37
		#  "name for lookup in localized magic words array",
38
		#  function callback,
39
		#  optional Parser::SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}}
40
		#    instead of {{#int:...}})
41
		$noHashFunctions = [
42
			'ns', 'nse', 'urlencode', 'lcfirst', 'ucfirst', 'lc', 'uc',
43
			'localurl', 'localurle', 'fullurl', 'fullurle', 'canonicalurl',
44
			'canonicalurle', 'formatnum', 'grammar', 'gender', 'plural', 'bidi',
45
			'numberofpages', 'numberofusers', 'numberofactiveusers',
46
			'numberofarticles', 'numberoffiles', 'numberofadmins',
47
			'numberingroup', 'numberofedits', 'language',
48
			'padleft', 'padright', 'anchorencode', 'defaultsort', 'filepath',
49
			'pagesincategory', 'pagesize', 'protectionlevel', 'protectionexpiry',
50
			'namespacee', 'namespacenumber', 'talkspace', 'talkspacee',
51
			'subjectspace', 'subjectspacee', 'pagename', 'pagenamee',
52
			'fullpagename', 'fullpagenamee', 'rootpagename', 'rootpagenamee',
53
			'basepagename', 'basepagenamee', 'subpagename', 'subpagenamee',
54
			'talkpagename', 'talkpagenamee', 'subjectpagename',
55
			'subjectpagenamee', 'pageid', 'revisionid', 'revisionday',
56
			'revisionday2', 'revisionmonth', 'revisionmonth1', 'revisionyear',
57
			'revisiontimestamp', 'revisionuser', 'cascadingsources',
58
		];
59
		foreach ( $noHashFunctions as $func ) {
60
			$parser->setFunctionHook( $func, [ __CLASS__, $func ], Parser::SFH_NO_HASH );
61
		}
62
63
		$parser->setFunctionHook(
64
			'namespace',
65
			[ __CLASS__, 'mwnamespace' ],
66
			Parser::SFH_NO_HASH
67
		);
68
		$parser->setFunctionHook( 'int', [ __CLASS__, 'intFunction' ], Parser::SFH_NO_HASH );
69
		$parser->setFunctionHook( 'special', [ __CLASS__, 'special' ] );
70
		$parser->setFunctionHook( 'speciale', [ __CLASS__, 'speciale' ] );
71
		$parser->setFunctionHook( 'tag', [ __CLASS__, 'tagObj' ], Parser::SFH_OBJECT_ARGS );
72
		$parser->setFunctionHook( 'formatdate', [ __CLASS__, 'formatDate' ] );
73
74
		if ( $wgAllowDisplayTitle ) {
75
			$parser->setFunctionHook(
76
				'displaytitle',
77
				[ __CLASS__, 'displaytitle' ],
78
				Parser::SFH_NO_HASH
79
			);
80
		}
81
		if ( $wgAllowSlowParserFunctions ) {
82
			$parser->setFunctionHook(
83
				'pagesinnamespace',
84
				[ __CLASS__, 'pagesinnamespace' ],
85
				Parser::SFH_NO_HASH
86
			);
87
		}
88
	}
89
90
	/**
91
	 * @param Parser $parser
92
	 * @param string $part1
93
	 * @return array
94
	 */
95
	public static function intFunction( $parser, $part1 = '' /*, ... */ ) {
96
		if ( strval( $part1 ) !== '' ) {
97
			$args = array_slice( func_get_args(), 2 );
98
			$message = wfMessage( $part1, $args )
99
				->inLanguage( $parser->getOptions()->getUserLangObj() );
100
			if ( !$message->exists() ) {
101
				// When message does not exists, the message name is surrounded by angle
102
				// and can result in a tag, therefore escape the angles
103
				return $message->escaped();
104
			}
105
			return [ $message->plain(), 'noparse' => false ];
106
		} else {
107
			return [ 'found' => false ];
108
		}
109
	}
110
111
	/**
112
	 * @param Parser $parser
113
	 * @param string $date
114
	 * @param string $defaultPref
115
	 *
116
	 * @return string
117
	 */
118
	public static function formatDate( $parser, $date, $defaultPref = null ) {
119
		$lang = $parser->getFunctionLang();
120
		$df = DateFormatter::getInstance( $lang );
121
122
		$date = trim( $date );
123
124
		$pref = $parser->getOptions()->getDateFormat();
125
126
		// Specify a different default date format other than the normal default
127
		// if the user has 'default' for their setting
128
		if ( $pref == 'default' && $defaultPref ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $defaultPref of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
129
			$pref = $defaultPref;
130
		}
131
132
		$date = $df->reformat( $pref, $date, [ 'match-whole' ] );
133
		return $date;
134
	}
135
136
	public static function ns( $parser, $part1 = '' ) {
137
		global $wgContLang;
138
		if ( intval( $part1 ) || $part1 == "0" ) {
139
			$index = intval( $part1 );
140
		} else {
141
			$index = $wgContLang->getNsIndex( str_replace( ' ', '_', $part1 ) );
142
		}
143
		if ( $index !== false ) {
144
			return $wgContLang->getFormattedNsText( $index );
145
		} else {
146
			return [ 'found' => false ];
147
		}
148
	}
149
150
	public static function nse( $parser, $part1 = '' ) {
151
		$ret = self::ns( $parser, $part1 );
152
		if ( is_string( $ret ) ) {
153
			$ret = wfUrlencode( str_replace( ' ', '_', $ret ) );
154
		}
155
		return $ret;
156
	}
157
158
	/**
159
	 * urlencodes a string according to one of three patterns: (bug 22474)
160
	 *
161
	 * By default (for HTTP "query" strings), spaces are encoded as '+'.
162
	 * Or to encode a value for the HTTP "path", spaces are encoded as '%20'.
163
	 * For links to "wiki"s, or similar software, spaces are encoded as '_',
164
	 *
165
	 * @param Parser $parser
166
	 * @param string $s The text to encode.
167
	 * @param string $arg (optional): The type of encoding.
168
	 * @return string
169
	 */
170
	public static function urlencode( $parser, $s = '', $arg = null ) {
171
		static $magicWords = null;
172
		if ( is_null( $magicWords ) ) {
173
			$magicWords = new MagicWordArray( [ 'url_path', 'url_query', 'url_wiki' ] );
174
		}
175
		switch ( $magicWords->matchStartToEnd( $arg ) ) {
176
177
			// Encode as though it's a wiki page, '_' for ' '.
178
			case 'url_wiki':
179
				$func = 'wfUrlencode';
180
				$s = str_replace( ' ', '_', $s );
181
				break;
182
183
			// Encode for an HTTP Path, '%20' for ' '.
184
			case 'url_path':
185
				$func = 'rawurlencode';
186
				break;
187
188
			// Encode for HTTP query, '+' for ' '.
189
			case 'url_query':
190
			default:
191
				$func = 'urlencode';
192
		}
193
		// See T105242, where the choice to kill markers and various
194
		// other options were discussed.
195
		return $func( $parser->killMarkers( $s ) );
196
	}
197
198
	public static function lcfirst( $parser, $s = '' ) {
199
		global $wgContLang;
200
		return $wgContLang->lcfirst( $s );
201
	}
202
203
	public static function ucfirst( $parser, $s = '' ) {
204
		global $wgContLang;
205
		return $wgContLang->ucfirst( $s );
206
	}
207
208
	/**
209
	 * @param Parser $parser
210
	 * @param string $s
211
	 * @return string
212
	 */
213
	public static function lc( $parser, $s = '' ) {
214
		global $wgContLang;
215
		return $parser->markerSkipCallback( $s, [ $wgContLang, 'lc' ] );
216
	}
217
218
	/**
219
	 * @param Parser $parser
220
	 * @param string $s
221
	 * @return string
222
	 */
223
	public static function uc( $parser, $s = '' ) {
224
		global $wgContLang;
225
		return $parser->markerSkipCallback( $s, [ $wgContLang, 'uc' ] );
226
	}
227
228
	public static function localurl( $parser, $s = '', $arg = null ) {
229
		return self::urlFunction( 'getLocalURL', $s, $arg );
230
	}
231
232 View Code Duplication
	public static function localurle( $parser, $s = '', $arg = null ) {
233
		$temp = self::urlFunction( 'getLocalURL', $s, $arg );
234
		if ( !is_string( $temp ) ) {
235
			return $temp;
236
		} else {
237
			return htmlspecialchars( $temp );
238
		}
239
	}
240
241
	public static function fullurl( $parser, $s = '', $arg = null ) {
242
		return self::urlFunction( 'getFullURL', $s, $arg );
243
	}
244
245 View Code Duplication
	public static function fullurle( $parser, $s = '', $arg = null ) {
246
		$temp = self::urlFunction( 'getFullURL', $s, $arg );
247
		if ( !is_string( $temp ) ) {
248
			return $temp;
249
		} else {
250
			return htmlspecialchars( $temp );
251
		}
252
	}
253
254
	public static function canonicalurl( $parser, $s = '', $arg = null ) {
255
		return self::urlFunction( 'getCanonicalURL', $s, $arg );
256
	}
257
258 View Code Duplication
	public static function canonicalurle( $parser, $s = '', $arg = null ) {
259
		$temp = self::urlFunction( 'getCanonicalURL', $s, $arg );
260
		if ( !is_string( $temp ) ) {
261
			return $temp;
262
		} else {
263
			return htmlspecialchars( $temp );
264
		}
265
	}
266
267
	public static function urlFunction( $func, $s = '', $arg = null ) {
268
		$title = Title::newFromText( $s );
269
		# Due to order of execution of a lot of bits, the values might be encoded
270
		# before arriving here; if that's true, then the title can't be created
271
		# and the variable will fail. If we can't get a decent title from the first
272
		# attempt, url-decode and try for a second.
273
		if ( is_null( $title ) ) {
274
			$title = Title::newFromURL( urldecode( $s ) );
275
		}
276
		if ( !is_null( $title ) ) {
277
			# Convert NS_MEDIA -> NS_FILE
278
			if ( $title->getNamespace() == NS_MEDIA ) {
279
				$title = Title::makeTitle( NS_FILE, $title->getDBkey() );
280
			}
281
			if ( !is_null( $arg ) ) {
282
				$text = $title->$func( $arg );
283
			} else {
284
				$text = $title->$func();
285
			}
286
			return $text;
287
		} else {
288
			return [ 'found' => false ];
289
		}
290
	}
291
292
	/**
293
	 * @param Parser $parser
294
	 * @param string $num
295
	 * @param string $arg
296
	 * @return string
297
	 */
298
	public static function formatnum( $parser, $num = '', $arg = null ) {
299
		if ( self::matchAgainstMagicword( 'rawsuffix', $arg ) ) {
300
			$func = [ $parser->getFunctionLang(), 'parseFormattedNumber' ];
301
		} elseif ( self::matchAgainstMagicword( 'nocommafysuffix', $arg ) ) {
302
			$func = [ $parser->getFunctionLang(), 'formatNumNoSeparators' ];
303
		} else {
304
			$func = [ $parser->getFunctionLang(), 'formatNum' ];
305
		}
306
		return $parser->markerSkipCallback( $num, $func );
307
	}
308
309
	/**
310
	 * @param Parser $parser
311
	 * @param string $case
312
	 * @param string $word
313
	 * @return string
314
	 */
315
	public static function grammar( $parser, $case = '', $word = '' ) {
316
		$word = $parser->killMarkers( $word );
317
		return $parser->getFunctionLang()->convertGrammar( $word, $case );
318
	}
319
320
	/**
321
	 * @param Parser $parser
322
	 * @param string $username
323
	 * @return string
324
	 */
325
	public static function gender( $parser, $username ) {
326
		$forms = array_slice( func_get_args(), 2 );
327
328
		// Some shortcuts to avoid loading user data unnecessarily
329
		if ( count( $forms ) === 0 ) {
330
			return '';
331
		} elseif ( count( $forms ) === 1 ) {
332
			return $forms[0];
333
		}
334
335
		$username = trim( $username );
336
337
		// default
338
		$gender = User::getDefaultOption( 'gender' );
339
340
		// allow prefix.
341
		$title = Title::newFromText( $username );
342
343
		if ( $title && $title->getNamespace() == NS_USER ) {
344
			$username = $title->getText();
345
		}
346
347
		// check parameter, or use the ParserOptions if in interface message
348
		$user = User::newFromName( $username );
349
		if ( $user ) {
350
			$gender = GenderCache::singleton()->getGenderOf( $user, __METHOD__ );
0 ignored issues
show
Deprecated Code introduced by
The method GenderCache::singleton() has been deprecated with message: in 1.28 see MediaWikiServices::getInstance()->getGenderCache()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
351
		} elseif ( $username === '' && $parser->getOptions()->getInterfaceMessage() ) {
352
			$gender = GenderCache::singleton()->getGenderOf( $parser->getOptions()->getUser(), __METHOD__ );
0 ignored issues
show
Deprecated Code introduced by
The method GenderCache::singleton() has been deprecated with message: in 1.28 see MediaWikiServices::getInstance()->getGenderCache()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
353
		}
354
		$ret = $parser->getFunctionLang()->gender( $gender, $forms );
355
		return $ret;
356
	}
357
358
	/**
359
	 * @param Parser $parser
360
	 * @param string $text
361
	 * @return string
362
	 */
363
	public static function plural( $parser, $text = '' ) {
364
		$forms = array_slice( func_get_args(), 2 );
365
		$text = $parser->getFunctionLang()->parseFormattedNumber( $text );
366
		settype( $text, ctype_digit( $text ) ? 'int' : 'float' );
367
		return $parser->getFunctionLang()->convertPlural( $text, $forms );
368
	}
369
370
	/**
371
	 * @param Parser $parser
372
	 * @param string $text
373
	 * @return string
374
	 */
375
	public static function bidi( $parser, $text = '' ) {
376
		return $parser->getFunctionLang()->embedBidi( $text );
377
	}
378
379
	/**
380
	 * Override the title of the page when viewed, provided we've been given a
381
	 * title which will normalise to the canonical title
382
	 *
383
	 * @param Parser $parser Parent parser
384
	 * @param string $text Desired title text
385
	 * @param string $uarg
386
	 * @return string
387
	 */
388
	public static function displaytitle( $parser, $text = '', $uarg = '' ) {
389
		global $wgRestrictDisplayTitle;
390
391
		static $magicWords = null;
392
		if ( is_null( $magicWords ) ) {
393
			$magicWords = new MagicWordArray( [ 'displaytitle_noerror', 'displaytitle_noreplace' ] );
394
		}
395
		$arg = $magicWords->matchStartToEnd( $uarg );
396
397
		// parse a limited subset of wiki markup (just the single quote items)
398
		$text = $parser->doQuotes( $text );
399
400
		// remove stripped text (e.g. the UNIQ-QINU stuff) that was generated by tag extensions/whatever
401
		$text = $parser->killMarkers( $text );
402
403
		// list of disallowed tags for DISPLAYTITLE
404
		// these will be escaped even though they are allowed in normal wiki text
405
		$bad = [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'blockquote', 'ol', 'ul', 'li', 'hr',
406
			'table', 'tr', 'th', 'td', 'dl', 'dd', 'caption', 'p', 'ruby', 'rb', 'rt', 'rtc', 'rp', 'br' ];
407
408
		// disallow some styles that could be used to bypass $wgRestrictDisplayTitle
409
		if ( $wgRestrictDisplayTitle ) {
410
			$htmlTagsCallback = function ( &$params ) {
411
				$decoded = Sanitizer::decodeTagAttributes( $params );
412
413
				if ( isset( $decoded['style'] ) ) {
414
					// this is called later anyway, but we need it right now for the regexes below to be safe
415
					// calling it twice doesn't hurt
416
					$decoded['style'] = Sanitizer::checkCss( $decoded['style'] );
417
418
					if ( preg_match( '/(display|user-select|visibility)\s*:/i', $decoded['style'] ) ) {
419
						$decoded['style'] = '/* attempt to bypass $wgRestrictDisplayTitle */';
420
					}
421
				}
422
423
				$params = Sanitizer::safeEncodeTagAttributes( $decoded );
424
			};
425
		} else {
426
			$htmlTagsCallback = null;
427
		}
428
429
		// only requested titles that normalize to the actual title are allowed through
430
		// if $wgRestrictDisplayTitle is true (it is by default)
431
		// mimic the escaping process that occurs in OutputPage::setPageTitle
432
		$text = Sanitizer::normalizeCharReferences( Sanitizer::removeHTMLtags(
433
			$text,
434
			$htmlTagsCallback,
435
			[],
436
			[],
437
			$bad
438
		) );
439
		$title = Title::newFromText( Sanitizer::stripAllTags( $text ) );
440
441
		if ( !$wgRestrictDisplayTitle ||
442
			( $title instanceof Title
443
			&& !$title->hasFragment()
444
			&& $title->equals( $parser->mTitle ) )
445
		) {
446
			$old = $parser->mOutput->getProperty( 'displaytitle' );
447
			if ( $old === false || $arg !== 'displaytitle_noreplace' ) {
448
				$parser->mOutput->setDisplayTitle( $text );
449
			}
450
			if ( $old !== false && $old !== $text && !$arg ) {
451
				$converter = $parser->getConverterLanguage()->getConverter();
452
				return '<span class="error">' .
453
					wfMessage( 'duplicate-displaytitle',
454
						// Message should be parsed, but these params should only be escaped.
455
						$converter->markNoConversion( wfEscapeWikiText( $old ) ),
456
						$converter->markNoConversion( wfEscapeWikiText( $text ) )
457
					)->inContentLanguage()->text() .
458
					'</span>';
459
			} else {
460
				return '';
461
			}
462 View Code Duplication
		} else {
463
			$parser->addTrackingCategory( 'restricted-displaytitle-ignored' );
464
			$converter = $parser->getConverterLanguage()->getConverter();
465
			return '<span class="error">' .
466
				wfMessage( 'restricted-displaytitle',
467
					// Message should be parsed, but this param should only be escaped.
468
					$converter->markNoConversion( wfEscapeWikiText( $text ) )
469
				)->inContentLanguage()->text() .
470
				'</span>';
471
		}
472
	}
473
474
	/**
475
	 * Matches the given value against the value of given magic word
476
	 *
477
	 * @param string $magicword Magic word key
478
	 * @param string $value Value to match
479
	 * @return bool True on successful match
480
	 */
481
	private static function matchAgainstMagicword( $magicword, $value ) {
482
		$value = trim( strval( $value ) );
483
		if ( $value === '' ) {
484
			return false;
485
		}
486
		$mwObject = MagicWord::get( $magicword );
487
		return $mwObject->matchStartToEnd( $value );
488
	}
489
490
	public static function formatRaw( $num, $raw ) {
491
		if ( self::matchAgainstMagicword( 'rawsuffix', $raw ) ) {
492
			return $num;
493
		} else {
494
			global $wgContLang;
495
			return $wgContLang->formatNum( $num );
496
		}
497
	}
498
	public static function numberofpages( $parser, $raw = null ) {
499
		return self::formatRaw( SiteStats::pages(), $raw );
500
	}
501
	public static function numberofusers( $parser, $raw = null ) {
502
		return self::formatRaw( SiteStats::users(), $raw );
503
	}
504
	public static function numberofactiveusers( $parser, $raw = null ) {
505
		return self::formatRaw( SiteStats::activeUsers(), $raw );
506
	}
507
	public static function numberofarticles( $parser, $raw = null ) {
508
		return self::formatRaw( SiteStats::articles(), $raw );
509
	}
510
	public static function numberoffiles( $parser, $raw = null ) {
511
		return self::formatRaw( SiteStats::images(), $raw );
512
	}
513
	public static function numberofadmins( $parser, $raw = null ) {
514
		return self::formatRaw( SiteStats::numberingroup( 'sysop' ), $raw );
515
	}
516
	public static function numberofedits( $parser, $raw = null ) {
517
		return self::formatRaw( SiteStats::edits(), $raw );
518
	}
519
	public static function pagesinnamespace( $parser, $namespace = 0, $raw = null ) {
520
		return self::formatRaw( SiteStats::pagesInNs( intval( $namespace ) ), $raw );
521
	}
522
	public static function numberingroup( $parser, $name = '', $raw = null ) {
523
		return self::formatRaw( SiteStats::numberingroup( strtolower( $name ) ), $raw );
524
	}
525
526
	/**
527
	 * Given a title, return the namespace name that would be given by the
528
	 * corresponding magic word
529
	 * Note: function name changed to "mwnamespace" rather than "namespace"
530
	 * to not break PHP 5.3
531
	 * @param Parser $parser
532
	 * @param string $title
533
	 * @return mixed|string
534
	 */
535
	public static function mwnamespace( $parser, $title = null ) {
536
		$t = Title::newFromText( $title );
537
		if ( is_null( $t ) ) {
538
			return '';
539
		}
540
		return str_replace( '_', ' ', $t->getNsText() );
541
	}
542 View Code Duplication
	public static function namespacee( $parser, $title = null ) {
543
		$t = Title::newFromText( $title );
544
		if ( is_null( $t ) ) {
545
			return '';
546
		}
547
		return wfUrlencode( $t->getNsText() );
0 ignored issues
show
Bug introduced by
It seems like $t->getNsText() targeting Title::getNsText() can also be of type boolean; however, wfUrlencode() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
548
	}
549
	public static function namespacenumber( $parser, $title = null ) {
550
		$t = Title::newFromText( $title );
551
		if ( is_null( $t ) ) {
552
			return '';
553
		}
554
		return $t->getNamespace();
555
	}
556
	public static function talkspace( $parser, $title = null ) {
557
		$t = Title::newFromText( $title );
558
		if ( is_null( $t ) || !$t->canTalk() ) {
559
			return '';
560
		}
561
		return str_replace( '_', ' ', $t->getTalkNsText() );
562
	}
563
	public static function talkspacee( $parser, $title = null ) {
564
		$t = Title::newFromText( $title );
565
		if ( is_null( $t ) || !$t->canTalk() ) {
566
			return '';
567
		}
568
		return wfUrlencode( $t->getTalkNsText() );
569
	}
570 View Code Duplication
	public static function subjectspace( $parser, $title = null ) {
571
		$t = Title::newFromText( $title );
572
		if ( is_null( $t ) ) {
573
			return '';
574
		}
575
		return str_replace( '_', ' ', $t->getSubjectNsText() );
576
	}
577 View Code Duplication
	public static function subjectspacee( $parser, $title = null ) {
578
		$t = Title::newFromText( $title );
579
		if ( is_null( $t ) ) {
580
			return '';
581
		}
582
		return wfUrlencode( $t->getSubjectNsText() );
583
	}
584
585
	/**
586
	 * Functions to get and normalize pagenames, corresponding to the magic words
587
	 * of the same names
588
	 * @param Parser $parser
589
	 * @param string $title
590
	 * @return string
591
	 */
592 View Code Duplication
	public static function pagename( $parser, $title = null ) {
593
		$t = Title::newFromText( $title );
594
		if ( is_null( $t ) ) {
595
			return '';
596
		}
597
		return wfEscapeWikiText( $t->getText() );
598
	}
599 View Code Duplication
	public static function pagenamee( $parser, $title = null ) {
600
		$t = Title::newFromText( $title );
601
		if ( is_null( $t ) ) {
602
			return '';
603
		}
604
		return wfEscapeWikiText( $t->getPartialURL() );
605
	}
606 View Code Duplication
	public static function fullpagename( $parser, $title = null ) {
607
		$t = Title::newFromText( $title );
608
		if ( is_null( $t ) || !$t->canTalk() ) {
609
			return '';
610
		}
611
		return wfEscapeWikiText( $t->getPrefixedText() );
612
	}
613
	public static function fullpagenamee( $parser, $title = null ) {
614
		$t = Title::newFromText( $title );
615
		if ( is_null( $t ) || !$t->canTalk() ) {
616
			return '';
617
		}
618
		return wfEscapeWikiText( $t->getPrefixedURL() );
619
	}
620 View Code Duplication
	public static function subpagename( $parser, $title = null ) {
621
		$t = Title::newFromText( $title );
622
		if ( is_null( $t ) ) {
623
			return '';
624
		}
625
		return wfEscapeWikiText( $t->getSubpageText() );
626
	}
627 View Code Duplication
	public static function subpagenamee( $parser, $title = null ) {
628
		$t = Title::newFromText( $title );
629
		if ( is_null( $t ) ) {
630
			return '';
631
		}
632
		return wfEscapeWikiText( $t->getSubpageUrlForm() );
633
	}
634 View Code Duplication
	public static function rootpagename( $parser, $title = null ) {
635
		$t = Title::newFromText( $title );
636
		if ( is_null( $t ) ) {
637
			return '';
638
		}
639
		return wfEscapeWikiText( $t->getRootText() );
640
	}
641 View Code Duplication
	public static function rootpagenamee( $parser, $title = null ) {
642
		$t = Title::newFromText( $title );
643
		if ( is_null( $t ) ) {
644
			return '';
645
		}
646
		return wfEscapeWikiText( wfUrlencode( str_replace( ' ', '_', $t->getRootText() ) ) );
647
	}
648 View Code Duplication
	public static function basepagename( $parser, $title = null ) {
649
		$t = Title::newFromText( $title );
650
		if ( is_null( $t ) ) {
651
			return '';
652
		}
653
		return wfEscapeWikiText( $t->getBaseText() );
654
	}
655 View Code Duplication
	public static function basepagenamee( $parser, $title = null ) {
656
		$t = Title::newFromText( $title );
657
		if ( is_null( $t ) ) {
658
			return '';
659
		}
660
		return wfEscapeWikiText( wfUrlencode( str_replace( ' ', '_', $t->getBaseText() ) ) );
661
	}
662
	public static function talkpagename( $parser, $title = null ) {
663
		$t = Title::newFromText( $title );
664
		if ( is_null( $t ) || !$t->canTalk() ) {
665
			return '';
666
		}
667
		return wfEscapeWikiText( $t->getTalkPage()->getPrefixedText() );
668
	}
669
	public static function talkpagenamee( $parser, $title = null ) {
670
		$t = Title::newFromText( $title );
671
		if ( is_null( $t ) || !$t->canTalk() ) {
672
			return '';
673
		}
674
		return wfEscapeWikiText( $t->getTalkPage()->getPrefixedURL() );
675
	}
676 View Code Duplication
	public static function subjectpagename( $parser, $title = null ) {
677
		$t = Title::newFromText( $title );
678
		if ( is_null( $t ) ) {
679
			return '';
680
		}
681
		return wfEscapeWikiText( $t->getSubjectPage()->getPrefixedText() );
682
	}
683 View Code Duplication
	public static function subjectpagenamee( $parser, $title = null ) {
684
		$t = Title::newFromText( $title );
685
		if ( is_null( $t ) ) {
686
			return '';
687
		}
688
		return wfEscapeWikiText( $t->getSubjectPage()->getPrefixedURL() );
689
	}
690
691
	/**
692
	 * Return the number of pages, files or subcats in the given category,
693
	 * or 0 if it's nonexistent. This is an expensive parser function and
694
	 * can't be called too many times per page.
695
	 * @param Parser $parser
696
	 * @param string $name
697
	 * @param string $arg1
698
	 * @param string $arg2
699
	 * @return string
700
	 */
701
	public static function pagesincategory( $parser, $name = '', $arg1 = null, $arg2 = null ) {
702
		global $wgContLang;
703
		static $magicWords = null;
704
		if ( is_null( $magicWords ) ) {
705
			$magicWords = new MagicWordArray( [
706
				'pagesincategory_all',
707
				'pagesincategory_pages',
708
				'pagesincategory_subcats',
709
				'pagesincategory_files'
710
			] );
711
		}
712
		static $cache = [];
713
714
		// split the given option to its variable
715
		if ( self::matchAgainstMagicword( 'rawsuffix', $arg1 ) ) {
716
			// {{pagesincategory:|raw[|type]}}
717
			$raw = $arg1;
718
			$type = $magicWords->matchStartToEnd( $arg2 );
719
		} else {
720
			// {{pagesincategory:[|type[|raw]]}}
721
			$type = $magicWords->matchStartToEnd( $arg1 );
722
			$raw = $arg2;
723
		}
724
		if ( !$type ) { // backward compatibility
725
			$type = 'pagesincategory_all';
726
		}
727
728
		$title = Title::makeTitleSafe( NS_CATEGORY, $name );
729
		if ( !$title ) { # invalid title
730
			return self::formatRaw( 0, $raw );
731
		}
732
		$wgContLang->findVariantLink( $name, $title, true );
733
734
		// Normalize name for cache
735
		$name = $title->getDBkey();
736
737
		if ( !isset( $cache[$name] ) ) {
738
			$category = Category::newFromTitle( $title );
739
740
			$allCount = $subcatCount = $fileCount = $pagesCount = 0;
741
			if ( $parser->incrementExpensiveFunctionCount() ) {
742
				// $allCount is the total number of cat members,
743
				// not the count of how many members are normal pages.
744
				$allCount = (int)$category->getPageCount();
745
				$subcatCount = (int)$category->getSubcatCount();
746
				$fileCount = (int)$category->getFileCount();
747
				$pagesCount = $allCount - $subcatCount - $fileCount;
748
			}
749
			$cache[$name]['pagesincategory_all'] = $allCount;
750
			$cache[$name]['pagesincategory_pages'] = $pagesCount;
751
			$cache[$name]['pagesincategory_subcats'] = $subcatCount;
752
			$cache[$name]['pagesincategory_files'] = $fileCount;
753
		}
754
755
		$count = $cache[$name][$type];
756
		return self::formatRaw( $count, $raw );
757
	}
758
759
	/**
760
	 * Return the size of the given page, or 0 if it's nonexistent.  This is an
761
	 * expensive parser function and can't be called too many times per page.
762
	 *
763
	 * @param Parser $parser
764
	 * @param string $page Name of page to check (Default: empty string)
765
	 * @param string $raw Should number be human readable with commas or just number
766
	 * @return string
767
	 */
768
	public static function pagesize( $parser, $page = '', $raw = null ) {
769
		$title = Title::newFromText( $page );
770
771
		if ( !is_object( $title ) ) {
772
			return self::formatRaw( 0, $raw );
773
		}
774
775
		// fetch revision from cache/database and return the value
776
		$rev = self::getCachedRevisionObject( $parser, $title );
777
		$length = $rev ? $rev->getSize() : 0;
778
		if ( $length === null ) {
779
			// We've had bugs where rev_len was not being recorded for empty pages, see T135414
780
			$length = 0;
781
		}
782
		return self::formatRaw( $length, $raw );
783
	}
784
785
	/**
786
	 * Returns the requested protection level for the current page. This
787
	 * is an expensive parser function and can't be called too many times
788
	 * per page, unless the protection levels/expiries for the given title
789
	 * have already been retrieved
790
	 *
791
	 * @param Parser $parser
792
	 * @param string $type
793
	 * @param string $title
794
	 *
795
	 * @return string
796
	 */
797 View Code Duplication
	public static function protectionlevel( $parser, $type = '', $title = '' ) {
798
		$titleObject = Title::newFromText( $title );
799
		if ( !( $titleObject instanceof Title ) ) {
800
			$titleObject = $parser->mTitle;
801
		}
802
		if ( $titleObject->areRestrictionsLoaded() || $parser->incrementExpensiveFunctionCount() ) {
803
			$restrictions = $titleObject->getRestrictions( strtolower( $type ) );
804
			# Title::getRestrictions returns an array, its possible it may have
805
			# multiple values in the future
806
			return implode( $restrictions, ',' );
807
		}
808
		return '';
809
	}
810
811
	/**
812
	 * Returns the requested protection expiry for the current page. This
813
	 * is an expensive parser function and can't be called too many times
814
	 * per page, unless the protection levels/expiries for the given title
815
	 * have already been retrieved
816
	 *
817
	 * @param Parser $parser
818
	 * @param string $type
819
	 * @param string $title
820
	 *
821
	 * @return string
822
	 */
823 View Code Duplication
	public static function protectionexpiry( $parser, $type = '', $title = '' ) {
824
		$titleObject = Title::newFromText( $title );
825
		if ( !( $titleObject instanceof Title ) ) {
826
			$titleObject = $parser->mTitle;
827
		}
828
		if ( $titleObject->areRestrictionsLoaded() || $parser->incrementExpensiveFunctionCount() ) {
829
			$expiry = $titleObject->getRestrictionExpiry( strtolower( $type ) );
830
			// getRestrictionExpiry() returns false on invalid type; trying to
831
			// match protectionlevel() function that returns empty string instead
832
			if ( $expiry === false ) {
833
				$expiry = '';
834
			}
835
			return $expiry;
836
		}
837
		return '';
838
	}
839
840
	/**
841
	 * Gives language names.
842
	 * @param Parser $parser
843
	 * @param string $code Language code (of which to get name)
844
	 * @param string $inLanguage Language code (in which to get name)
845
	 * @return string
846
	 */
847
	public static function language( $parser, $code = '', $inLanguage = '' ) {
848
		$code = strtolower( $code );
849
		$inLanguage = strtolower( $inLanguage );
850
		$lang = Language::fetchLanguageName( $code, $inLanguage );
851
		return $lang !== '' ? $lang : wfBCP47( $code );
852
	}
853
854
	/**
855
	 * Unicode-safe str_pad with the restriction that $length is forced to be <= 500
856
	 * @param Parser $parser
857
	 * @param string $string
858
	 * @param int $length
859
	 * @param string $padding
860
	 * @param int $direction
861
	 * @return string
862
	 */
863
	public static function pad(
864
		$parser, $string, $length, $padding = '0', $direction = STR_PAD_RIGHT
865
	) {
866
		$padding = $parser->killMarkers( $padding );
867
		$lengthOfPadding = mb_strlen( $padding );
868
		if ( $lengthOfPadding == 0 ) {
869
			return $string;
870
		}
871
872
		# The remaining length to add counts down to 0 as padding is added
873
		$length = min( $length, 500 ) - mb_strlen( $string );
874
		# $finalPadding is just $padding repeated enough times so that
875
		# mb_strlen( $string ) + mb_strlen( $finalPadding ) == $length
876
		$finalPadding = '';
877
		while ( $length > 0 ) {
878
			# If $length < $lengthofPadding, truncate $padding so we get the
879
			# exact length desired.
880
			$finalPadding .= mb_substr( $padding, 0, $length );
881
			$length -= $lengthOfPadding;
882
		}
883
884
		if ( $direction == STR_PAD_LEFT ) {
885
			return $finalPadding . $string;
886
		} else {
887
			return $string . $finalPadding;
888
		}
889
	}
890
891
	public static function padleft( $parser, $string = '', $length = 0, $padding = '0' ) {
892
		return self::pad( $parser, $string, $length, $padding, STR_PAD_LEFT );
893
	}
894
895
	public static function padright( $parser, $string = '', $length = 0, $padding = '0' ) {
896
		return self::pad( $parser, $string, $length, $padding );
897
	}
898
899
	/**
900
	 * @param Parser $parser
901
	 * @param string $text
902
	 * @return string
903
	 */
904
	public static function anchorencode( $parser, $text ) {
905
		$text = $parser->killMarkers( $text );
906
		return (string)substr( $parser->guessSectionNameFromWikiText( $text ), 1 );
907
	}
908
909
	public static function special( $parser, $text ) {
910
		list( $page, $subpage ) = SpecialPageFactory::resolveAlias( $text );
911
		if ( $page ) {
912
			$title = SpecialPage::getTitleFor( $page, $subpage );
913
			return $title->getPrefixedText();
914
		} else {
915
			// unknown special page, just use the given text as its title, if at all possible
916
			$title = Title::makeTitleSafe( NS_SPECIAL, $text );
917
			return $title ? $title->getPrefixedText() : self::special( $parser, 'Badtitle' );
918
		}
919
	}
920
921
	public static function speciale( $parser, $text ) {
922
		return wfUrlencode( str_replace( ' ', '_', self::special( $parser, $text ) ) );
923
	}
924
925
	/**
926
	 * @param Parser $parser
927
	 * @param string $text The sortkey to use
928
	 * @param string $uarg Either "noreplace" or "noerror" (in en)
929
	 *   both suppress errors, and noreplace does nothing if
930
	 *   a default sortkey already exists.
931
	 * @return string
932
	 */
933
	public static function defaultsort( $parser, $text, $uarg = '' ) {
934
		static $magicWords = null;
935
		if ( is_null( $magicWords ) ) {
936
			$magicWords = new MagicWordArray( [ 'defaultsort_noerror', 'defaultsort_noreplace' ] );
937
		}
938
		$arg = $magicWords->matchStartToEnd( $uarg );
939
940
		$text = trim( $text );
941
		if ( strlen( $text ) == 0 ) {
942
			return '';
943
		}
944
		$old = $parser->getCustomDefaultSort();
945
		if ( $old === false || $arg !== 'defaultsort_noreplace' ) {
946
			$parser->setDefaultSort( $text );
947
		}
948
949
		if ( $old === false || $old == $text || $arg ) {
950
			return '';
951 View Code Duplication
		} else {
952
			$converter = $parser->getConverterLanguage()->getConverter();
953
			return '<span class="error">' .
954
				wfMessage( 'duplicate-defaultsort',
955
					// Message should be parsed, but these params should only be escaped.
956
					$converter->markNoConversion( wfEscapeWikiText( $old ) ),
957
					$converter->markNoConversion( wfEscapeWikiText( $text ) )
958
				)->inContentLanguage()->text() .
959
				'</span>';
960
		}
961
	}
962
963
	/**
964
	 * Usage {{filepath|300}}, {{filepath|nowiki}}, {{filepath|nowiki|300}}
965
	 * or {{filepath|300|nowiki}} or {{filepath|300px}}, {{filepath|200x300px}},
966
	 * {{filepath|nowiki|200x300px}}, {{filepath|200x300px|nowiki}}.
967
	 *
968
	 * @param Parser $parser
969
	 * @param string $name
970
	 * @param string $argA
971
	 * @param string $argB
972
	 * @return array|string
973
	 */
974
	public static function filepath( $parser, $name = '', $argA = '', $argB = '' ) {
975
		$file = wfFindFile( $name );
976
977
		if ( $argA == 'nowiki' ) {
978
			// {{filepath: | option [| size] }}
979
			$isNowiki = true;
980
			$parsedWidthParam = $parser->parseWidthParam( $argB );
981
		} else {
982
			// {{filepath: [| size [|option]] }}
983
			$parsedWidthParam = $parser->parseWidthParam( $argA );
984
			$isNowiki = ( $argB == 'nowiki' );
985
		}
986
987
		if ( $file ) {
988
			$url = $file->getFullUrl();
989
990
			// If a size is requested...
991
			if ( count( $parsedWidthParam ) ) {
992
				$mto = $file->transform( $parsedWidthParam );
993
				// ... and we can
994
				if ( $mto && !$mto->isError() ) {
995
					// ... change the URL to point to a thumbnail.
996
					$url = wfExpandUrl( $mto->getUrl(), PROTO_RELATIVE );
997
				}
998
			}
999
			if ( $isNowiki ) {
1000
				return [ $url, 'nowiki' => true ];
1001
			}
1002
			return $url;
1003
		} else {
1004
			return '';
1005
		}
1006
	}
1007
1008
	/**
1009
	 * Parser function to extension tag adaptor
1010
	 * @param Parser $parser
1011
	 * @param PPFrame $frame
1012
	 * @param PPNode[] $args
1013
	 * @return string
1014
	 */
1015
	public static function tagObj( $parser, $frame, $args ) {
1016
		if ( !count( $args ) ) {
1017
			return '';
1018
		}
1019
		$tagName = strtolower( trim( $frame->expand( array_shift( $args ) ) ) );
0 ignored issues
show
Bug introduced by
It seems like array_shift($args) can be null; however, expand() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1020
1021
		if ( count( $args ) ) {
1022
			$inner = $frame->expand( array_shift( $args ) );
1023
		} else {
1024
			$inner = null;
1025
		}
1026
1027
		$attributes = [];
1028
		foreach ( $args as $arg ) {
1029
			$bits = $arg->splitArg();
1030
			if ( strval( $bits['index'] ) === '' ) {
1031
				$name = trim( $frame->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
1032
				$value = trim( $frame->expand( $bits['value'] ) );
1033
				if ( preg_match( '/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m ) ) {
1034
					$value = isset( $m[1] ) ? $m[1] : '';
1035
				}
1036
				$attributes[$name] = $value;
1037
			}
1038
		}
1039
1040
		$stripList = $parser->getStripList();
1041
		if ( !in_array( $tagName, $stripList ) ) {
1042
			// we can't handle this tag (at least not now), so just re-emit it as an ordinary tag
1043
			$attrText = '';
1044
			foreach ( $attributes as $name => $value ) {
1045
				$attrText .= ' ' . htmlspecialchars( $name ) . '="' . htmlspecialchars( $value ) . '"';
1046
			}
1047
			if ( $inner === null ) {
1048
				return "<$tagName$attrText/>";
1049
			}
1050
			return "<$tagName$attrText>$inner</$tagName>";
1051
		}
1052
1053
		$params = [
1054
			'name' => $tagName,
1055
			'inner' => $inner,
1056
			'attributes' => $attributes,
1057
			'close' => "</$tagName>",
1058
		];
1059
		return $parser->extensionSubstitution( $params, $frame );
1060
	}
1061
1062
	/**
1063
	 * Fetched the current revision of the given title and return this.
1064
	 * Will increment the expensive function count and
1065
	 * add a template link to get the value refreshed on changes.
1066
	 * For a given title, which is equal to the current parser title,
1067
	 * the revision object from the parser is used, when that is the current one
1068
	 *
1069
	 * @param Parser $parser
1070
	 * @param Title $title
1071
	 * @return Revision
1072
	 * @since 1.23
1073
	 */
1074
	private static function getCachedRevisionObject( $parser, $title = null ) {
1075
		if ( is_null( $title ) ) {
1076
			return null;
1077
		}
1078
1079
		// Use the revision from the parser itself, when param is the current page
1080
		// and the revision is the current one
1081
		if ( $title->equals( $parser->getTitle() ) ) {
1082
			$parserRev = $parser->getRevisionObject();
1083
			if ( $parserRev && $parserRev->isCurrent() ) {
1084
				// force reparse after edit with vary-revision flag
1085
				$parser->getOutput()->setFlag( 'vary-revision' );
1086
				wfDebug( __METHOD__ . ": use current revision from parser, setting vary-revision...\n" );
1087
				return $parserRev;
1088
			}
1089
		}
1090
1091
		// Normalize name for cache
1092
		$page = $title->getPrefixedDBkey();
1093
1094
		if ( !( $parser->currentRevisionCache && $parser->currentRevisionCache->has( $page ) )
1095
			&& !$parser->incrementExpensiveFunctionCount() ) {
1096
			return null;
1097
		}
1098
		$rev = $parser->fetchCurrentRevisionOfTitle( $title );
1099
		$pageID = $rev ? $rev->getPage() : 0;
1100
		$revID = $rev ? $rev->getId() : 0;
1101
1102
		// Register dependency in templatelinks
1103
		$parser->getOutput()->addTemplate( $title, $pageID, $revID );
1104
1105
		return $rev;
1106
	}
1107
1108
	/**
1109
	 * Get the pageid of a specified page
1110
	 * @param Parser $parser
1111
	 * @param string $title Title to get the pageid from
1112
	 * @return int|null|string
1113
	 * @since 1.23
1114
	 */
1115
	public static function pageid( $parser, $title = null ) {
1116
		$t = Title::newFromText( $title );
1117
		if ( is_null( $t ) ) {
1118
			return '';
1119
		}
1120
		// Use title from parser to have correct pageid after edit
1121
		if ( $t->equals( $parser->getTitle() ) ) {
1122
			$t = $parser->getTitle();
1123
			return $t->getArticleID();
1124
		}
1125
1126
		// These can't have ids
1127
		if ( !$t->canExist() || $t->isExternal() ) {
1128
			return 0;
1129
		}
1130
1131
		// Check the link cache, maybe something already looked it up.
1132
		$linkCache = LinkCache::singleton();
0 ignored issues
show
Deprecated Code introduced by
The method LinkCache::singleton() has been deprecated with message: since 1.28, use MediaWikiServices instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1133
		$pdbk = $t->getPrefixedDBkey();
1134
		$id = $linkCache->getGoodLinkID( $pdbk );
1135
		if ( $id != 0 ) {
1136
			$parser->mOutput->addLink( $t, $id );
1137
			return $id;
1138
		}
1139
		if ( $linkCache->isBadLink( $pdbk ) ) {
1140
			$parser->mOutput->addLink( $t, 0 );
1141
			return $id;
1142
		}
1143
1144
		// We need to load it from the DB, so mark expensive
1145
		if ( $parser->incrementExpensiveFunctionCount() ) {
1146
			$id = $t->getArticleID();
1147
			$parser->mOutput->addLink( $t, $id );
1148
			return $id;
1149
		}
1150
		return null;
1151
	}
1152
1153
	/**
1154
	 * Get the id from the last revision of a specified page.
1155
	 * @param Parser $parser
1156
	 * @param string $title Title to get the id from
1157
	 * @return int|null|string
1158
	 * @since 1.23
1159
	 */
1160 View Code Duplication
	public static function revisionid( $parser, $title = null ) {
1161
		$t = Title::newFromText( $title );
1162
		if ( is_null( $t ) ) {
1163
			return '';
1164
		}
1165
		// fetch revision from cache/database and return the value
1166
		$rev = self::getCachedRevisionObject( $parser, $t );
1167
		return $rev ? $rev->getId() : '';
1168
	}
1169
1170
	/**
1171
	 * Get the day from the last revision of a specified page.
1172
	 * @param Parser $parser
1173
	 * @param string $title Title to get the day from
1174
	 * @return string
1175
	 * @since 1.23
1176
	 */
1177 View Code Duplication
	public static function revisionday( $parser, $title = null ) {
1178
		$t = Title::newFromText( $title );
1179
		if ( is_null( $t ) ) {
1180
			return '';
1181
		}
1182
		// fetch revision from cache/database and return the value
1183
		$rev = self::getCachedRevisionObject( $parser, $t );
1184
		return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'j' ) : '';
1185
	}
1186
1187
	/**
1188
	 * Get the day with leading zeros from the last revision of a specified page.
1189
	 * @param Parser $parser
1190
	 * @param string $title Title to get the day from
1191
	 * @return string
1192
	 * @since 1.23
1193
	 */
1194 View Code Duplication
	public static function revisionday2( $parser, $title = null ) {
1195
		$t = Title::newFromText( $title );
1196
		if ( is_null( $t ) ) {
1197
			return '';
1198
		}
1199
		// fetch revision from cache/database and return the value
1200
		$rev = self::getCachedRevisionObject( $parser, $t );
1201
		return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'd' ) : '';
1202
	}
1203
1204
	/**
1205
	 * Get the month with leading zeros from the last revision of a specified page.
1206
	 * @param Parser $parser
1207
	 * @param string $title Title to get the month from
1208
	 * @return string
1209
	 * @since 1.23
1210
	 */
1211 View Code Duplication
	public static function revisionmonth( $parser, $title = null ) {
1212
		$t = Title::newFromText( $title );
1213
		if ( is_null( $t ) ) {
1214
			return '';
1215
		}
1216
		// fetch revision from cache/database and return the value
1217
		$rev = self::getCachedRevisionObject( $parser, $t );
1218
		return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'm' ) : '';
1219
	}
1220
1221
	/**
1222
	 * Get the month from the last revision of a specified page.
1223
	 * @param Parser $parser
1224
	 * @param string $title Title to get the month from
1225
	 * @return string
1226
	 * @since 1.23
1227
	 */
1228 View Code Duplication
	public static function revisionmonth1( $parser, $title = null ) {
1229
		$t = Title::newFromText( $title );
1230
		if ( is_null( $t ) ) {
1231
			return '';
1232
		}
1233
		// fetch revision from cache/database and return the value
1234
		$rev = self::getCachedRevisionObject( $parser, $t );
1235
		return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'n' ) : '';
1236
	}
1237
1238
	/**
1239
	 * Get the year from the last revision of a specified page.
1240
	 * @param Parser $parser
1241
	 * @param string $title Title to get the year from
1242
	 * @return string
1243
	 * @since 1.23
1244
	 */
1245 View Code Duplication
	public static function revisionyear( $parser, $title = null ) {
1246
		$t = Title::newFromText( $title );
1247
		if ( is_null( $t ) ) {
1248
			return '';
1249
		}
1250
		// fetch revision from cache/database and return the value
1251
		$rev = self::getCachedRevisionObject( $parser, $t );
1252
		return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'Y' ) : '';
1253
	}
1254
1255
	/**
1256
	 * Get the timestamp from the last revision of a specified page.
1257
	 * @param Parser $parser
1258
	 * @param string $title Title to get the timestamp from
1259
	 * @return string
1260
	 * @since 1.23
1261
	 */
1262 View Code Duplication
	public static function revisiontimestamp( $parser, $title = null ) {
1263
		$t = Title::newFromText( $title );
1264
		if ( is_null( $t ) ) {
1265
			return '';
1266
		}
1267
		// fetch revision from cache/database and return the value
1268
		$rev = self::getCachedRevisionObject( $parser, $t );
1269
		return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'YmdHis' ) : '';
1270
	}
1271
1272
	/**
1273
	 * Get the user from the last revision of a specified page.
1274
	 * @param Parser $parser
1275
	 * @param string $title Title to get the user from
1276
	 * @return string
1277
	 * @since 1.23
1278
	 */
1279 View Code Duplication
	public static function revisionuser( $parser, $title = null ) {
1280
		$t = Title::newFromText( $title );
1281
		if ( is_null( $t ) ) {
1282
			return '';
1283
		}
1284
		// fetch revision from cache/database and return the value
1285
		$rev = self::getCachedRevisionObject( $parser, $t );
1286
		return $rev ? $rev->getUserText() : '';
1287
	}
1288
1289
	/**
1290
	 * Returns the sources of any cascading protection acting on a specified page.
1291
	 * Pages will not return their own title unless they transclude themselves.
1292
	 * This is an expensive parser function and can't be called too many times per page,
1293
	 * unless cascading protection sources for the page have already been loaded.
1294
	 *
1295
	 * @param Parser $parser
1296
	 * @param string $title
1297
	 *
1298
	 * @return string
1299
	 * @since 1.23
1300
	 */
1301
	public static function cascadingsources( $parser, $title = '' ) {
1302
		$titleObject = Title::newFromText( $title );
1303
		if ( !( $titleObject instanceof Title ) ) {
1304
			$titleObject = $parser->mTitle;
1305
		}
1306
		if ( $titleObject->areCascadeProtectionSourcesLoaded()
1307
			|| $parser->incrementExpensiveFunctionCount()
1308
		) {
1309
			$names = [];
1310
			$sources = $titleObject->getCascadeProtectionSources();
1311
			foreach ( $sources[0] as $sourceTitle ) {
0 ignored issues
show
Bug introduced by
The expression $sources[0] of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1312
				$names[] = $sourceTitle->getPrefixedText();
1313
			}
1314
			return implode( $names, '|' );
1315
		}
1316
		return '';
1317
	}
1318
1319
}
1320