MediaWiki 1.41.2
CoreParserFunctions.php
Go to the documentation of this file.
1<?php
37use Wikimedia\RemexHtml\Tokenizer\Attributes;
38use Wikimedia\RemexHtml\Tokenizer\PlainAttributes;
39
46 private const MAX_TTS = 900;
47
51 public const REGISTER_OPTIONS = [
52 // See documentation for the corresponding config options
53 MainConfigNames::AllowDisplayTitle,
54 MainConfigNames::AllowSlowParserFunctions,
55 ];
56
64 public static function register( Parser $parser, ServiceOptions $options ) {
65 $options->assertRequiredOptions( self::REGISTER_OPTIONS );
66 $allowDisplayTitle = $options->get( MainConfigNames::AllowDisplayTitle );
67 $allowSlowParserFunctions = $options->get( MainConfigNames::AllowSlowParserFunctions );
68
69 # Syntax for arguments (see Parser::setFunctionHook):
70 # "name for lookup in localized magic words array",
71 # function callback,
72 # optional Parser::SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}}
73 # instead of {{#int:...}})
74 $noHashFunctions = [
75 'ns', 'nse', 'urlencode', 'lcfirst', 'ucfirst', 'lc', 'uc',
76 'localurl', 'localurle', 'fullurl', 'fullurle', 'canonicalurl',
77 'canonicalurle', 'formatnum', 'grammar', 'gender', 'plural', 'bidi',
78 'numberingroup', 'language',
79 'padleft', 'padright', 'anchorencode', 'defaultsort', 'filepath',
80 'pagesincategory', 'pagesize', 'protectionlevel', 'protectionexpiry',
81 # The following are the "parser function" forms of magic
82 # variables defined in CoreMagicVariables. The no-args form will
83 # go through the magic variable code path (and be cached); the
84 # presence of arguments will cause the parser function form to
85 # be invoked. (Note that the actual implementation will pass
86 # a Parser object as first argument, in addition to the
87 # parser function parameters.)
88
89 # For this group, the first parameter to the parser function is
90 # "page title", and the no-args form (and the magic variable)
91 # defaults to "current page title".
92 'pagename', 'pagenamee',
93 'fullpagename', 'fullpagenamee',
94 'subpagename', 'subpagenamee',
95 'rootpagename', 'rootpagenamee',
96 'basepagename', 'basepagenamee',
97 'talkpagename', 'talkpagenamee',
98 'subjectpagename', 'subjectpagenamee',
99 'pageid', 'revisionid', 'revisionday',
100 'revisionday2', 'revisionmonth', 'revisionmonth1', 'revisionyear',
101 'revisiontimestamp',
102 'revisionuser',
103 'cascadingsources',
104 'namespace', 'namespacee', 'namespacenumber', 'talkspace', 'talkspacee',
105 'subjectspace', 'subjectspacee',
106
107 # More parser functions corresponding to CoreMagicVariables.
108 # For this group, the first parameter to the parser function is
109 # "raw" (uses the 'raw' format if present) and the no-args form
110 # (and the magic variable) defaults to 'not raw'.
111 'numberofarticles', 'numberoffiles',
112 'numberofusers',
113 'numberofactiveusers',
114 'numberofpages',
115 'numberofadmins',
116 'numberofedits',
117 ];
118 foreach ( $noHashFunctions as $func ) {
119 $parser->setFunctionHook( $func, [ __CLASS__, $func ], Parser::SFH_NO_HASH );
120 }
121
122 $parser->setFunctionHook( 'int', [ __CLASS__, 'intFunction' ], Parser::SFH_NO_HASH );
123 $parser->setFunctionHook( 'special', [ __CLASS__, 'special' ] );
124 $parser->setFunctionHook( 'speciale', [ __CLASS__, 'speciale' ] );
125 $parser->setFunctionHook( 'tag', [ __CLASS__, 'tagObj' ], Parser::SFH_OBJECT_ARGS );
126 $parser->setFunctionHook( 'formatdate', [ __CLASS__, 'formatDate' ] );
127
128 if ( $allowDisplayTitle ) {
129 $parser->setFunctionHook(
130 'displaytitle',
131 [ __CLASS__, 'displaytitle' ],
133 );
134 }
135 if ( $allowSlowParserFunctions ) {
136 $parser->setFunctionHook(
137 'pagesinnamespace',
138 [ __CLASS__, 'pagesinnamespace' ],
140 );
141 }
142 }
143
150 public static function intFunction( $parser, $part1 = '', ...$params ) {
151 if ( strval( $part1 ) !== '' ) {
152 $message = wfMessage( $part1, $params )
153 ->inLanguage( $parser->getOptions()->getUserLangObj() );
154 return [ $message->plain(), 'noparse' => false ];
155 } else {
156 return [ 'found' => false ];
157 }
158 }
159
167 public static function formatDate( $parser, $date, $defaultPref = null ) {
168 $lang = $parser->getTargetLanguage();
169 $df = MediaWikiServices::getInstance()->getDateFormatterFactory()->get( $lang );
170
171 $date = trim( $date );
172
173 $pref = $parser->getOptions()->getDateFormat();
174
175 // Specify a different default date format other than the normal default
176 // if the user has 'default' for their setting
177 if ( $pref == 'default' && $defaultPref ) {
178 $pref = $defaultPref;
179 }
180
181 $date = $df->reformat( $pref, $date, [ 'match-whole' ] );
182 return $date;
183 }
184
185 public static function ns( $parser, $part1 = '' ) {
186 if ( intval( $part1 ) || $part1 == "0" ) {
187 $index = intval( $part1 );
188 } else {
189 $index = $parser->getContentLanguage()->getNsIndex( str_replace( ' ', '_', $part1 ) );
190 }
191 if ( $index !== false ) {
192 return $parser->getContentLanguage()->getFormattedNsText( $index );
193 } else {
194 return [ 'found' => false ];
195 }
196 }
197
198 public static function nse( $parser, $part1 = '' ) {
199 $ret = self::ns( $parser, $part1 );
200 if ( is_string( $ret ) ) {
201 $ret = wfUrlencode( str_replace( ' ', '_', $ret ) );
202 }
203 return $ret;
204 }
205
218 public static function urlencode( $parser, $s = '', $arg = null ) {
219 static $magicWords = null;
220 if ( $magicWords === null ) {
222 $parser->getMagicWordFactory()->newArray( [ 'url_path', 'url_query', 'url_wiki' ] );
223 }
224 switch ( $magicWords->matchStartToEnd( $arg ?? '' ) ) {
225 // Encode as though it's a wiki page, '_' for ' '.
226 case 'url_wiki':
227 $func = 'wfUrlencode';
228 $s = str_replace( ' ', '_', $s );
229 break;
230
231 // Encode for an HTTP Path, '%20' for ' '.
232 case 'url_path':
233 $func = 'rawurlencode';
234 break;
235
236 // Encode for HTTP query, '+' for ' '.
237 case 'url_query':
238 default:
239 $func = 'urlencode';
240 }
241 // See T105242, where the choice to kill markers and various
242 // other options were discussed.
243 return $func( $parser->killMarkers( $s ) );
244 }
245
246 public static function lcfirst( $parser, $s = '' ) {
247 return $parser->getContentLanguage()->lcfirst( $s );
248 }
249
250 public static function ucfirst( $parser, $s = '' ) {
251 return $parser->getContentLanguage()->ucfirst( $s );
252 }
253
259 public static function lc( $parser, $s = '' ) {
260 return $parser->markerSkipCallback( $s, [ $parser->getContentLanguage(), 'lc' ] );
261 }
262
268 public static function uc( $parser, $s = '' ) {
269 return $parser->markerSkipCallback( $s, [ $parser->getContentLanguage(), 'uc' ] );
270 }
271
272 public static function localurl( $parser, $s = '', $arg = null ) {
273 return self::urlFunction( 'getLocalURL', $s, $arg );
274 }
275
276 public static function localurle( $parser, $s = '', $arg = null ) {
277 $temp = self::urlFunction( 'getLocalURL', $s, $arg );
278 if ( !is_string( $temp ) ) {
279 return $temp;
280 } else {
281 return htmlspecialchars( $temp, ENT_COMPAT );
282 }
283 }
284
285 public static function fullurl( $parser, $s = '', $arg = null ) {
286 return self::urlFunction( 'getFullURL', $s, $arg );
287 }
288
289 public static function fullurle( $parser, $s = '', $arg = null ) {
290 $temp = self::urlFunction( 'getFullURL', $s, $arg );
291 if ( !is_string( $temp ) ) {
292 return $temp;
293 } else {
294 return htmlspecialchars( $temp, ENT_COMPAT );
295 }
296 }
297
298 public static function canonicalurl( $parser, $s = '', $arg = null ) {
299 return self::urlFunction( 'getCanonicalURL', $s, $arg );
300 }
301
302 public static function canonicalurle( $parser, $s = '', $arg = null ) {
303 $temp = self::urlFunction( 'getCanonicalURL', $s, $arg );
304 if ( !is_string( $temp ) ) {
305 return $temp;
306 } else {
307 return htmlspecialchars( $temp, ENT_COMPAT );
308 }
309 }
310
311 public static function urlFunction( $func, $s = '', $arg = null ) {
312 # Due to order of execution of a lot of bits, the values might be encoded
313 # before arriving here; if that's true, then the title can't be created
314 # and the variable will fail. If we can't get a decent title from the first
315 # attempt, url-decode and try for a second.
316 $title = Title::newFromText( $s ) ?? Title::newFromURL( urldecode( $s ) );
317 if ( $title !== null ) {
318 # Convert NS_MEDIA -> NS_FILE
319 if ( $title->inNamespace( NS_MEDIA ) ) {
320 $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
321 }
322 if ( $arg !== null ) {
323 $text = $title->$func( $arg );
324 } else {
325 $text = $title->$func();
326 }
327 return $text;
328 } else {
329 return [ 'found' => false ];
330 }
331 }
332
339 public static function formatnum( $parser, $num = '', $arg = null ) {
340 if ( self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'rawsuffix', $arg ) ) {
341 $func = [ $parser->getTargetLanguage(), 'parseFormattedNumber' ];
342 } elseif (
343 self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'nocommafysuffix', $arg )
344 ) {
345 $func = [ $parser->getTargetLanguage(), 'formatNumNoSeparators' ];
346 $func = self::getLegacyFormatNum( $parser, $func );
347 } else {
348 $func = [ $parser->getTargetLanguage(), 'formatNum' ];
349 $func = self::getLegacyFormatNum( $parser, $func );
350 }
351 return $parser->markerSkipCallback( $num, $func );
352 }
353
360 private static function getLegacyFormatNum( $parser, $callback ) {
361 // For historic reasons, the formatNum parser function will
362 // take arguments which are not actually formatted numbers,
363 // which then trigger deprecation warnings in Language::formatNum*.
364 // Instead emit a tracking category instead to allow linting.
365 return static function ( $number ) use ( $parser, $callback ) {
366 $validNumberRe = '(-(?=[\d\.]))?(\d+|(?=\.\d))(\.\d*)?([Ee][-+]?\d+)?';
367 if (
368 !is_numeric( $number ) &&
369 $number !== (string)NAN &&
370 $number !== (string)INF &&
371 $number !== (string)-INF
372 ) {
373 $parser->addTrackingCategory( 'nonnumeric-formatnum' );
374 // Don't split on NAN/INF in the legacy case since they are
375 // likely to be found embedded inside non-numeric text.
376 return preg_replace_callback( "/{$validNumberRe}/", static function ( $m ) use ( $callback ) {
377 return call_user_func( $callback, $m[0] );
378 }, $number );
379 }
380 return call_user_func( $callback, $number );
381 };
382 }
383
390 public static function grammar( $parser, $case = '', $word = '' ) {
391 $word = $parser->killMarkers( $word );
392 return $parser->getTargetLanguage()->convertGrammar( $word, $case );
393 }
394
401 public static function gender( $parser, $username, ...$forms ) {
402 // Some shortcuts to avoid loading user data unnecessarily
403 if ( count( $forms ) === 0 ) {
404 return '';
405 } elseif ( count( $forms ) === 1 ) {
406 return $forms[0];
407 }
408
409 $username = trim( $username );
410
411 $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
412 $gender = $userOptionsLookup->getDefaultOption( 'gender' );
413
414 // allow prefix and normalize (e.g. "&#42;foo" -> "*foo" ).
415 $title = Title::newFromText( $username, NS_USER );
416
417 if ( $title && $title->inNamespace( NS_USER ) ) {
418 $username = $title->getText();
419 }
420
421 // check parameter, or use the ParserOptions if in interface message
422 $user = User::newFromName( $username );
423 $genderCache = MediaWikiServices::getInstance()->getGenderCache();
424 if ( $user ) {
425 $gender = $genderCache->getGenderOf( $user, __METHOD__ );
426 } elseif ( $username === '' && $parser->getOptions()->getInterfaceMessage() ) {
427 $gender = $genderCache->getGenderOf( $parser->getOptions()->getUserIdentity(), __METHOD__ );
428 }
429 $ret = $parser->getTargetLanguage()->gender( $gender, $forms );
430 return $ret;
431 }
432
439 public static function plural( $parser, $text = '', ...$forms ) {
440 $text = $parser->getTargetLanguage()->parseFormattedNumber( $text );
441 settype( $text, ctype_digit( $text ) ? 'int' : 'float' );
442 // @phan-suppress-next-line PhanTypeMismatchArgument Phan does not handle settype
443 return $parser->getTargetLanguage()->convertPlural( $text, $forms );
444 }
445
451 public static function bidi( $parser, $text = '' ) {
452 return $parser->getTargetLanguage()->embedBidi( $text );
453 }
454
464 public static function displaytitle( $parser, $text = '', $uarg = '' ) {
465 $restrictDisplayTitle = MediaWikiServices::getInstance()->getMainConfig()
466 ->get( MainConfigNames::RestrictDisplayTitle );
467
468 static $magicWords = null;
469 if ( $magicWords === null ) {
470 $magicWords = $parser->getMagicWordFactory()->newArray(
471 [ 'displaytitle_noerror', 'displaytitle_noreplace' ] );
472 }
473 $arg = $magicWords->matchStartToEnd( $uarg );
474
475 // parse a limited subset of wiki markup (just the single quote items)
476 $text = $parser->doQuotes( $text );
477
478 // remove stripped text (e.g. the UNIQ-QINU stuff) that was generated by tag extensions/whatever
479 $text = $parser->killMarkers( $text );
480
481 // See T28547 for rationale for this processing.
482 // list of disallowed tags for DISPLAYTITLE
483 // these will be escaped even though they are allowed in normal wiki text
484 $bad = [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'blockquote', 'ol', 'ul', 'li', 'hr',
485 'table', 'tr', 'th', 'td', 'dl', 'dd', 'caption', 'p', 'ruby', 'rb', 'rt', 'rtc', 'rp', 'br' ];
486
487 // disallow some styles that could be used to bypass $wgRestrictDisplayTitle
488 if ( $restrictDisplayTitle ) {
489 // This code is tested with the cases marked T28547 in
490 // parserTests.txt
491 $htmlTagsCallback = static function ( Attributes $attr ): Attributes {
492 $decoded = $attr->getValues();
493
494 if ( isset( $decoded['style'] ) ) {
495 // this is called later anyway, but we need it right now for the regexes below to be safe
496 // calling it twice doesn't hurt
497 $decoded['style'] = Sanitizer::checkCss( $decoded['style'] );
498
499 if ( preg_match( '/(display|user-select|visibility)\s*:/i', $decoded['style'] ) ) {
500 $decoded['style'] = '/* attempt to bypass $wgRestrictDisplayTitle */';
501 }
502 }
503
504 return new PlainAttributes( $decoded );
505 };
506 } else {
507 $htmlTagsCallback = null;
508 }
509
510 // only requested titles that normalize to the actual title are allowed through
511 // if $wgRestrictDisplayTitle is true (it is by default)
512 // mimic the escaping process that occurs in OutputPage::setPageTitle
513 $text = Sanitizer::removeSomeTags( $text, [
514 'attrCallback' => $htmlTagsCallback,
515 'removeTags' => $bad,
516 ] );
517 $title = Title::newFromText( Sanitizer::stripAllTags( $text ) );
518 // Decode entities in $text the same way that Title::newFromText does
519 $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
520
521 if ( !$restrictDisplayTitle ||
522 ( $title instanceof Title
523 && !$title->hasFragment()
524 && $title->equals( $parser->getTitle() ) )
525 ) {
526 $old = $parser->getOutput()->getPageProperty( 'displaytitle' );
527 if ( $old === null || $arg !== 'displaytitle_noreplace' ) {
528 $parser->getOutput()->setDisplayTitle( $text );
529 }
530 if ( $old !== null && $old !== $text && !$arg ) {
531
532 $converter = $parser->getTargetLanguageConverter();
533 return '<span class="error">' .
534 $parser->msg( 'duplicate-displaytitle',
535 // Message should be parsed, but these params should only be escaped.
536 $converter->markNoConversion( wfEscapeWikiText( $old ) ),
537 $converter->markNoConversion( wfEscapeWikiText( $filteredText ) )
538 )->text() .
539 '</span>';
540 } else {
541 return '';
542 }
543 } else {
544 $parser->getOutput()->addWarningMsg(
545 'restricted-displaytitle',
546 // Message should be parsed, but this param should only be escaped.
547 Message::plaintextParam( $filteredText )
548 );
549 $parser->addTrackingCategory( 'restricted-displaytitle-ignored' );
550 }
551 }
552
562 private static function matchAgainstMagicword(
563 MagicWordFactory $magicWordFactory, $magicword, $value
564 ) {
565 $value = trim( strval( $value ) );
566 if ( $value === '' ) {
567 return false;
568 }
569 $mwObject = $magicWordFactory->get( $magicword );
570 return $mwObject->matchStartToEnd( $value );
571 }
572
582 public static function formatRaw(
583 $num, $raw, $language, MagicWordFactory $magicWordFactory = null
584 ) {
585 if ( $raw !== null && $raw !== '' ) {
586 if ( !$magicWordFactory ) {
587 $magicWordFactory = MediaWikiServices::getInstance()->getMagicWordFactory();
588 }
589 if ( self::matchAgainstMagicword( $magicWordFactory, 'rawsuffix', $raw ) ) {
590 return (string)$num;
591 }
592 }
593 return $language->formatNum( $num );
594 }
595
596 public static function numberofpages( $parser, $raw = null ) {
597 return self::formatRaw( SiteStats::pages(), $raw, $parser->getTargetLanguage() );
598 }
599
600 public static function numberofusers( $parser, $raw = null ) {
601 return self::formatRaw( SiteStats::users(), $raw, $parser->getTargetLanguage() );
602 }
603
604 public static function numberofactiveusers( $parser, $raw = null ) {
605 return self::formatRaw( SiteStats::activeUsers(), $raw, $parser->getTargetLanguage() );
606 }
607
608 public static function numberofarticles( $parser, $raw = null ) {
609 return self::formatRaw( SiteStats::articles(), $raw, $parser->getTargetLanguage() );
610 }
611
612 public static function numberoffiles( $parser, $raw = null ) {
613 return self::formatRaw( SiteStats::images(), $raw, $parser->getTargetLanguage() );
614 }
615
616 public static function numberofadmins( $parser, $raw = null ) {
617 return self::formatRaw(
618 SiteStats::numberingroup( 'sysop' ),
619 $raw,
620 $parser->getTargetLanguage()
621 );
622 }
623
624 public static function numberofedits( $parser, $raw = null ) {
625 return self::formatRaw( SiteStats::edits(), $raw, $parser->getTargetLanguage() );
626 }
627
628 public static function pagesinnamespace( $parser, $namespace = 0, $raw = null ) {
629 return self::formatRaw(
630 SiteStats::pagesInNs( intval( $namespace ) ),
631 $raw,
632 $parser->getTargetLanguage()
633 );
634 }
635
636 public static function numberingroup( $parser, $name = '', $raw = null ) {
637 return self::formatRaw(
638 SiteStats::numberingroup( strtolower( $name ) ),
639 $raw,
640 $parser->getTargetLanguage()
641 );
642 }
643
652 private static function makeTitle( Parser $parser, ?string $t ) {
653 if ( $t === null ) {
654 // For consistency with magic variable forms
655 $title = $parser->getTitle();
656 } else {
657 $title = Title::newFromText( $t );
658 }
659 return $title;
660 }
661
670 public static function namespace( $parser, $title = null ) {
671 $t = self::makeTitle( $parser, $title );
672 if ( $t === null ) {
673 return '';
674 }
675 return str_replace( '_', ' ', $t->getNsText() );
676 }
677
688 public static function mwnamespace( $parser, $title = null ) {
689 wfDeprecated( __METHOD__, '1.39' );
690 return self::namespace( $parser, $title );
691 }
692
693 public static function namespacee( $parser, $title = null ) {
694 $t = self::makeTitle( $parser, $title );
695 if ( $t === null ) {
696 return '';
697 }
698 return wfUrlencode( $t->getNsText() );
699 }
700
701 public static function namespacenumber( $parser, $title = null ) {
702 $t = self::makeTitle( $parser, $title );
703 if ( $t === null ) {
704 return '';
705 }
706 return (string)$t->getNamespace();
707 }
708
709 public static function talkspace( $parser, $title = null ) {
710 $t = self::makeTitle( $parser, $title );
711 if ( $t === null || !$t->canHaveTalkPage() ) {
712 return '';
713 }
714 return str_replace( '_', ' ', $t->getTalkNsText() );
715 }
716
717 public static function talkspacee( $parser, $title = null ) {
718 $t = self::makeTitle( $parser, $title );
719 if ( $t === null || !$t->canHaveTalkPage() ) {
720 return '';
721 }
722 return wfUrlencode( $t->getTalkNsText() );
723 }
724
725 public static function subjectspace( $parser, $title = null ) {
726 $t = self::makeTitle( $parser, $title );
727 if ( $t === null ) {
728 return '';
729 }
730 return str_replace( '_', ' ', $t->getSubjectNsText() );
731 }
732
733 public static function subjectspacee( $parser, $title = null ) {
734 $t = self::makeTitle( $parser, $title );
735 if ( $t === null ) {
736 return '';
737 }
738 return wfUrlencode( $t->getSubjectNsText() );
739 }
740
748 public static function pagename( $parser, $title = null ) {
749 $t = self::makeTitle( $parser, $title );
750 if ( $t === null ) {
751 return '';
752 }
753 return wfEscapeWikiText( $t->getText() );
754 }
755
756 public static function pagenamee( $parser, $title = null ) {
757 $t = self::makeTitle( $parser, $title );
758 if ( $t === null ) {
759 return '';
760 }
761 return wfEscapeWikiText( $t->getPartialURL() );
762 }
763
764 public static function fullpagename( $parser, $title = null ) {
765 $t = self::makeTitle( $parser, $title );
766 if ( $t === null ) {
767 return '';
768 }
769 return wfEscapeWikiText( $t->getPrefixedText() );
770 }
771
772 public static function fullpagenamee( $parser, $title = null ) {
773 $t = self::makeTitle( $parser, $title );
774 if ( $t === null ) {
775 return '';
776 }
777 return wfEscapeWikiText( $t->getPrefixedURL() );
778 }
779
780 public static function subpagename( $parser, $title = null ) {
781 $t = self::makeTitle( $parser, $title );
782 if ( $t === null ) {
783 return '';
784 }
785 return wfEscapeWikiText( $t->getSubpageText() );
786 }
787
788 public static function subpagenamee( $parser, $title = null ) {
789 $t = self::makeTitle( $parser, $title );
790 if ( $t === null ) {
791 return '';
792 }
793 return wfEscapeWikiText( $t->getSubpageUrlForm() );
794 }
795
796 public static function rootpagename( $parser, $title = null ) {
797 $t = self::makeTitle( $parser, $title );
798 if ( $t === null ) {
799 return '';
800 }
801 return wfEscapeWikiText( $t->getRootText() );
802 }
803
804 public static function rootpagenamee( $parser, $title = null ) {
805 $t = self::makeTitle( $parser, $title );
806 if ( $t === null ) {
807 return '';
808 }
809 return wfEscapeWikiText( wfUrlencode( str_replace( ' ', '_', $t->getRootText() ) ) );
810 }
811
812 public static function basepagename( $parser, $title = null ) {
813 $t = self::makeTitle( $parser, $title );
814 if ( $t === null ) {
815 return '';
816 }
817 return wfEscapeWikiText( $t->getBaseText() );
818 }
819
820 public static function basepagenamee( $parser, $title = null ) {
821 $t = self::makeTitle( $parser, $title );
822 if ( $t === null ) {
823 return '';
824 }
825 return wfEscapeWikiText( wfUrlencode( str_replace( ' ', '_', $t->getBaseText() ) ) );
826 }
827
828 public static function talkpagename( $parser, $title = null ) {
829 $t = self::makeTitle( $parser, $title );
830 if ( $t === null || !$t->canHaveTalkPage() ) {
831 return '';
832 }
833 return wfEscapeWikiText( $t->getTalkPage()->getPrefixedText() );
834 }
835
836 public static function talkpagenamee( $parser, $title = null ) {
837 $t = self::makeTitle( $parser, $title );
838 if ( $t === null || !$t->canHaveTalkPage() ) {
839 return '';
840 }
841 return wfEscapeWikiText( $t->getTalkPage()->getPrefixedURL() );
842 }
843
844 public static function subjectpagename( $parser, $title = null ) {
845 $t = self::makeTitle( $parser, $title );
846 if ( $t === null ) {
847 return '';
848 }
849 return wfEscapeWikiText( $t->getSubjectPage()->getPrefixedText() );
850 }
851
852 public static function subjectpagenamee( $parser, $title = null ) {
853 $t = self::makeTitle( $parser, $title );
854 if ( $t === null ) {
855 return '';
856 }
857 return wfEscapeWikiText( $t->getSubjectPage()->getPrefixedURL() );
858 }
859
870 public static function pagesincategory( $parser, $name = '', $arg1 = '', $arg2 = '' ) {
871 static $magicWords = null;
872 if ( $magicWords === null ) {
873 $magicWords = $parser->getMagicWordFactory()->newArray( [
874 'pagesincategory_all',
875 'pagesincategory_pages',
876 'pagesincategory_subcats',
877 'pagesincategory_files'
878 ] );
879 }
880 static $cache = [];
881
882 // split the given option to its variable
883 if ( self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'rawsuffix', $arg1 ) ) {
884 // {{pagesincategory:|raw[|type]}}
885 $raw = $arg1;
886 $type = $magicWords->matchStartToEnd( $arg2 );
887 } else {
888 // {{pagesincategory:[|type[|raw]]}}
889 $type = $magicWords->matchStartToEnd( $arg1 );
890 $raw = $arg2;
891 }
892 if ( !$type ) { // backward compatibility
893 $type = 'pagesincategory_all';
894 }
895
896 $title = Title::makeTitleSafe( NS_CATEGORY, $name );
897 if ( !$title ) { # invalid title
898 return self::formatRaw( 0, $raw, $parser->getTargetLanguage() );
899 }
900 $languageConverter = MediaWikiServices::getInstance()
901 ->getLanguageConverterFactory()
902 ->getLanguageConverter( $parser->getContentLanguage() );
903 $languageConverter->findVariantLink( $name, $title, true );
904
905 // Normalize name for cache
906 $name = $title->getDBkey();
907
908 if ( !isset( $cache[$name] ) ) {
909 $category = Category::newFromTitle( $title );
910
911 $allCount = $subcatCount = $fileCount = $pageCount = 0;
912 if ( $parser->incrementExpensiveFunctionCount() ) {
913 $allCount = $category->getMemberCount();
914 $subcatCount = $category->getSubcatCount();
915 $fileCount = $category->getFileCount();
916 $pageCount = $category->getPageCount( Category::COUNT_CONTENT_PAGES );
917 }
918 $cache[$name]['pagesincategory_all'] = $allCount;
919 $cache[$name]['pagesincategory_pages'] = $pageCount;
920 $cache[$name]['pagesincategory_subcats'] = $subcatCount;
921 $cache[$name]['pagesincategory_files'] = $fileCount;
922 }
923
924 $count = $cache[$name][$type];
925 return self::formatRaw( $count, $raw, $parser->getTargetLanguage() );
926 }
927
937 public static function pagesize( $parser, $page = '', $raw = null ) {
938 $title = Title::newFromText( $page );
939
940 if ( !is_object( $title ) ) {
941 return self::formatRaw( 0, $raw, $parser->getTargetLanguage() );
942 }
943
944 // fetch revision from cache/database and return the value
945 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_SHA1 );
946 $length = $rev ? $rev->getSize() : 0;
947 if ( $length === null ) {
948 // We've had bugs where rev_len was not being recorded for empty pages, see T135414
949 $length = 0;
950 }
951 return self::formatRaw( $length, $raw, $parser->getTargetLanguage() );
952 }
953
966 public static function protectionlevel( $parser, $type = '', $title = '' ) {
967 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
968 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
969 if ( $restrictionStore->areRestrictionsLoaded( $titleObject ) || $parser->incrementExpensiveFunctionCount() ) {
970 $restrictions = $restrictionStore->getRestrictions( $titleObject, strtolower( $type ) );
971 # RestrictionStore::getRestrictions returns an array, its possible it may have
972 # multiple values in the future
973 return implode( ',', $restrictions );
974 }
975 return '';
976 }
977
990 public static function protectionexpiry( $parser, $type = '', $title = '' ) {
991 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
992 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
993 if ( $restrictionStore->areRestrictionsLoaded( $titleObject ) || $parser->incrementExpensiveFunctionCount() ) {
994 // getRestrictionExpiry() returns null on invalid type; trying to
995 // match protectionlevel() function that returns empty string instead
996 return $restrictionStore->getRestrictionExpiry( $titleObject, strtolower( $type ) ) ?? '';
997 }
998 return '';
999 }
1000
1008 public static function language( $parser, $code = '', $inLanguage = '' ) {
1009 $code = strtolower( $code );
1010 $inLanguage = strtolower( $inLanguage );
1011 $lang = MediaWikiServices::getInstance()
1012 ->getLanguageNameUtils()
1013 ->getLanguageName( $code, $inLanguage );
1014 return $lang !== '' ? $lang : LanguageCode::bcp47( $code );
1015 }
1016
1026 public static function pad(
1027 $parser, $string, $length, $padding = '0', $direction = STR_PAD_RIGHT
1028 ) {
1029 $padding = $parser->killMarkers( $padding );
1030 $lengthOfPadding = mb_strlen( $padding );
1031 if ( $lengthOfPadding == 0 ) {
1032 return $string;
1033 }
1034
1035 # The remaining length to add counts down to 0 as padding is added
1036 $length = min( (int)$length, 500 ) - mb_strlen( $string );
1037 if ( $length <= 0 ) {
1038 // Nothing to add
1039 return $string;
1040 }
1041
1042 # $finalPadding is just $padding repeated enough times so that
1043 # mb_strlen( $string ) + mb_strlen( $finalPadding ) == $length
1044 $finalPadding = '';
1045 while ( $length > 0 ) {
1046 # If $length < $lengthofPadding, truncate $padding so we get the
1047 # exact length desired.
1048 $finalPadding .= mb_substr( $padding, 0, $length );
1049 $length -= $lengthOfPadding;
1050 }
1051
1052 if ( $direction == STR_PAD_LEFT ) {
1053 return $finalPadding . $string;
1054 } else {
1055 return $string . $finalPadding;
1056 }
1057 }
1058
1059 public static function padleft( $parser, $string = '', $length = 0, $padding = '0' ) {
1060 return self::pad( $parser, $string, $length, $padding, STR_PAD_LEFT );
1061 }
1062
1063 public static function padright( $parser, $string = '', $length = 0, $padding = '0' ) {
1064 return self::pad( $parser, $string, $length, $padding );
1065 }
1066
1072 public static function anchorencode( $parser, $text ) {
1073 $text = $parser->killMarkers( $text );
1074 $section = (string)substr( $parser->guessSectionNameFromWikiText( $text ), 1 );
1075 return Sanitizer::safeEncodeAttribute( $section );
1076 }
1077
1078 public static function special( $parser, $text ) {
1079 [ $page, $subpage ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
1080 resolveAlias( $text );
1081 if ( $page ) {
1082 $title = SpecialPage::getTitleFor( $page, $subpage );
1083 return $title->getPrefixedText();
1084 } else {
1085 // unknown special page, just use the given text as its title, if at all possible
1086 $title = Title::makeTitleSafe( NS_SPECIAL, $text );
1087 return $title ? $title->getPrefixedText() : self::special( $parser, 'Badtitle' );
1088 }
1089 }
1090
1091 public static function speciale( $parser, $text ) {
1092 return wfUrlencode( str_replace( ' ', '_', self::special( $parser, $text ) ) );
1093 }
1094
1103 public static function defaultsort( $parser, $text, $uarg = '' ) {
1104 static $magicWords = null;
1105 if ( $magicWords === null ) {
1106 $magicWords = $parser->getMagicWordFactory()->newArray(
1107 [ 'defaultsort_noerror', 'defaultsort_noreplace' ] );
1108 }
1109 $arg = $magicWords->matchStartToEnd( $uarg );
1110
1111 $text = trim( $text );
1112 if ( strlen( $text ) == 0 ) {
1113 return '';
1114 }
1115 $old = $parser->getOutput()->getPageProperty( 'defaultsort' );
1116 if ( $old === null || $arg !== 'defaultsort_noreplace' ) {
1117 $parser->getOutput()->setPageProperty( 'defaultsort', $text );
1118 }
1119
1120 if ( $old === null || $old == $text || $arg ) {
1121 return '';
1122 } else {
1123 $converter = $parser->getTargetLanguageConverter();
1124 return '<span class="error">' .
1125 $parser->msg( 'duplicate-defaultsort',
1126 // Message should be parsed, but these params should only be escaped.
1127 $converter->markNoConversion( wfEscapeWikiText( $old ) ),
1128 $converter->markNoConversion( wfEscapeWikiText( $text ) )
1129 )->text() .
1130 '</span>';
1131 }
1132 }
1133
1145 public static function filepath( $parser, $name = '', $argA = '', $argB = '' ) {
1146 $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $name );
1147
1148 if ( $argA == 'nowiki' ) {
1149 // {{filepath: | option [| size] }}
1150 $isNowiki = true;
1151 $parsedWidthParam = Parser::parseWidthParam( $argB );
1152 } else {
1153 // {{filepath: [| size [|option]] }}
1154 $parsedWidthParam = Parser::parseWidthParam( $argA );
1155 $isNowiki = ( $argB == 'nowiki' );
1156 }
1157
1158 if ( $file ) {
1159 $url = $file->getFullUrl();
1160
1161 // If a size is requested...
1162 if ( count( $parsedWidthParam ) ) {
1163 $mto = $file->transform( $parsedWidthParam );
1164 // ... and we can
1165 if ( $mto && !$mto->isError() ) {
1166 // ... change the URL to point to a thumbnail.
1167 $url = wfExpandUrl( $mto->getUrl(), PROTO_RELATIVE );
1168 }
1169 }
1170 if ( $isNowiki ) {
1171 return [ $url, 'nowiki' => true ];
1172 }
1173 return $url;
1174 } else {
1175 return '';
1176 }
1177 }
1178
1186 public static function tagObj( $parser, $frame, $args ) {
1187 if ( !count( $args ) ) {
1188 return '';
1189 }
1190 $tagName = strtolower( trim( $frame->expand( array_shift( $args ) ) ) );
1191 $processNowiki = $parser->tagNeedsNowikiStrippedInTagPF( $tagName ) ? PPFrame::PROCESS_NOWIKI : 0;
1192
1193 if ( count( $args ) ) {
1194 $inner = $frame->expand( array_shift( $args ), $processNowiki );
1195 } else {
1196 $inner = null;
1197 }
1198
1199 $attributes = [];
1200 foreach ( $args as $arg ) {
1201 $bits = $arg->splitArg();
1202 if ( strval( $bits['index'] ) === '' ) {
1203 $name = trim( $frame->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
1204 $value = trim( $frame->expand( $bits['value'] ) );
1205 if ( preg_match( '/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m ) ) {
1206 $value = $m[1] ?? '';
1207 }
1208 $attributes[$name] = $value;
1209 }
1210 }
1211
1212 $stripList = $parser->getStripList();
1213 if ( !in_array( $tagName, $stripList ) ) {
1214 // we can't handle this tag (at least not now), so just re-emit it as an ordinary tag
1215 $attrText = '';
1216 foreach ( $attributes as $name => $value ) {
1217 $attrText .= ' ' . htmlspecialchars( $name ) .
1218 '="' . htmlspecialchars( $value, ENT_COMPAT ) . '"';
1219 }
1220 if ( $inner === null ) {
1221 return "<$tagName$attrText/>";
1222 }
1223 return "<$tagName$attrText>$inner</$tagName>";
1224 }
1225
1226 $params = [
1227 'name' => $tagName,
1228 'inner' => $inner,
1229 'attributes' => $attributes,
1230 'close' => "</$tagName>",
1231 ];
1232 return $parser->extensionSubstitution( $params, $frame );
1233 }
1234
1248 private static function getCachedRevisionObject( $parser, $title, $vary ) {
1249 if ( !$title ) {
1250 return null;
1251 }
1252
1253 $revisionRecord = null;
1254
1255 $isSelfReferential = $title->equals( $parser->getTitle() );
1256 if ( $isSelfReferential ) {
1257 // Revision is for the same title that is currently being parsed. Only use the last
1258 // saved revision, regardless of Parser::getRevisionId() or fake revision injection
1259 // callbacks against the current title.
1260
1261 // FIXME (T318278): the above is the intention, but doesn't
1262 // describe the actual current behavior of this code, since
1263 // ->isCurrent() for the last saved revision will return
1264 // false so we're going to fall through and end up calling
1265 // ->getCurrentRevisionRecordOfTitle().
1266 $parserRevisionRecord = $parser->getRevisionRecordObject();
1267 if ( $parserRevisionRecord && $parserRevisionRecord->isCurrent() ) {
1268 $revisionRecord = $parserRevisionRecord;
1269 }
1270 }
1271
1272 $parserOutput = $parser->getOutput();
1273 if ( !$revisionRecord ) {
1274 if (
1275 !$parser->isCurrentRevisionOfTitleCached( $title ) &&
1277 ) {
1278 return null; // not allowed
1279 }
1280 // Get the current revision, ignoring Parser::getRevisionId() being null/old
1281 $revisionRecord = $parser->fetchCurrentRevisionRecordOfTitle( $title );
1282 if ( !$revisionRecord ) {
1283 // Convert `false` error return to `null`
1284 $revisionRecord = null;
1285 }
1286 // Register dependency in templatelinks
1287 $parserOutput->addTemplate(
1288 $title,
1289 $revisionRecord ? $revisionRecord->getPageId() : 0,
1290 $revisionRecord ? $revisionRecord->getId() : 0
1291 );
1292 }
1293
1294 if ( $isSelfReferential ) {
1295 wfDebug( __METHOD__ . ": used current revision, setting $vary" );
1296 // Upon page save, the result of the parser function using this might change
1297 $parserOutput->setOutputFlag( $vary );
1298 if ( $vary === ParserOutputFlags::VARY_REVISION_SHA1 && $revisionRecord ) {
1299 try {
1300 $sha1 = $revisionRecord->getSha1();
1301 } catch ( RevisionAccessException $e ) {
1302 $sha1 = null;
1303 }
1304 $parserOutput->setRevisionUsedSha1Base36( $sha1 );
1305 }
1306 }
1307
1308 return $revisionRecord;
1309 }
1310
1318 public static function pageid( $parser, $title = null ) {
1319 $t = self::makeTitle( $parser, $title );
1320 if ( !$t ) {
1321 return '';
1322 } elseif ( !$t->canExist() || $t->isExternal() ) {
1323 return 0; // e.g. special page or interwiki link
1324 }
1325
1326 $parserOutput = $parser->getOutput();
1327
1328 if ( $t->equals( $parser->getTitle() ) ) {
1329 // Revision is for the same title that is currently being parsed.
1330 // Use the title from Parser in case a new page ID was injected into it.
1331 $parserOutput->setOutputFlag( ParserOutputFlags::VARY_PAGE_ID );
1332 $id = $parser->getTitle()->getArticleID();
1333 if ( $id ) {
1334 $parserOutput->setSpeculativePageIdUsed( $id );
1335 }
1336
1337 return $id;
1338 }
1339
1340 // Check the link cache for the title
1341 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
1342 $pdbk = $t->getPrefixedDBkey();
1343 $id = $linkCache->getGoodLinkID( $pdbk );
1344 if ( $id != 0 || $linkCache->isBadLink( $pdbk ) ) {
1345 $parserOutput->addLink( $t, $id );
1346
1347 return $id;
1348 }
1349
1350 // We need to load it from the DB, so mark expensive
1351 if ( $parser->incrementExpensiveFunctionCount() ) {
1352 $id = $t->getArticleID();
1353 $parserOutput->addLink( $t, $id );
1354
1355 return $id;
1356 }
1357
1358 return null;
1359 }
1360
1368 public static function revisionid( $parser, $title = null ) {
1369 $t = self::makeTitle( $parser, $title );
1370 if ( $t === null ) {
1371 return '';
1372 }
1373
1374 $services = MediaWikiServices::getInstance();
1375 if (
1376 $t->equals( $parser->getTitle() ) &&
1377 $services->getMainConfig()->get( MainConfigNames::MiserMode ) &&
1378 !$parser->getOptions()->getInterfaceMessage() &&
1379 // @TODO: disallow this word on all namespaces (T235957)
1380 $services->getNamespaceInfo()->isSubject( $t->getNamespace() )
1381 ) {
1382 // Use a stub result instead of the actual revision ID in order to avoid
1383 // double parses on page save but still allow preview detection (T137900)
1384 if ( $parser->getRevisionId() || $parser->getOptions()->getSpeculativeRevId() ) {
1385 return '-';
1386 } else {
1387 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS );
1388 return '';
1389 }
1390 }
1391 // Fetch revision from cache/database and return the value.
1392 // Inform the edit saving system that getting the canonical output
1393 // after revision insertion requires a parse that used that exact
1394 // revision ID.
1395 if ( $t->equals( $parser->getTitle() ) && $title === null ) {
1396 // special handling for no-arg case: use speculative rev id
1397 // for current page.
1398 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_ID );
1399 $id = $parser->getRevisionId();
1400 if ( $id === 0 ) {
1401 $rev = $parser->getRevisionRecordObject();
1402 if ( $rev ) {
1403 $id = $rev->getId();
1404 }
1405 }
1406 if ( !$id ) {
1407 $id = $parser->getOptions()->getSpeculativeRevId();
1408 if ( $id ) {
1409 $parser->getOutput()->setSpeculativeRevIdUsed( $id );
1410 }
1411 }
1412 return (string)$id;
1413 }
1414 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_REVISION_ID );
1415 return $rev ? $rev->getId() : '';
1416 }
1417
1418 private static function getRevisionTimestampSubstring(
1419 Parser $parser,
1420 Title $title,
1421 int $start,
1422 int $len,
1423 int $mtts
1424 ): string {
1425 // If fetching the revision timestamp of the current page, substitute the
1426 // speculative timestamp to be used when this revision is saved. This
1427 // avoids having to invalidate the cache immediately by assuming the "last
1428 // saved revision" will in fact be this one.
1429 // Don't do this for interface messages (eg, edit notices) however; in that
1430 // case fall through and use the actual timestamp of the last saved revision.
1431 if ( $title->equals( $parser->getTitle() ) && !$parser->getOptions()->getInterfaceMessage() ) {
1432 // Get the timezone-adjusted timestamp to be used for this revision
1433 $resNow = substr( $parser->getRevisionTimestamp(), $start, $len );
1434 // Possibly set vary-revision if there is not yet an associated revision
1435 if ( !$parser->getRevisionRecordObject() ) {
1436 // Get the timezone-adjusted timestamp $mtts seconds in the future.
1437 // This future is relative to the current time and not that of the
1438 // parser options. The rendered timestamp can be compared to that
1439 // of the timestamp specified by the parser options.
1440 $resThen = substr(
1441 $parser->getContentLanguage()->userAdjust( wfTimestamp( TS_MW, time() + $mtts ), '' ),
1442 $start,
1443 $len
1444 );
1445
1446 if ( $resNow !== $resThen ) {
1447 // Inform the edit saving system that getting the canonical output after
1448 // revision insertion requires a parse that used an actual revision timestamp
1449 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1450 }
1451 }
1452
1453 return $resNow;
1454 } else {
1455 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1456 if ( !$rev ) {
1457 return '';
1458 }
1459 $resNow = substr(
1460 $parser->getContentLanguage()->userAdjust( $rev->getTimestamp(), '' ), $start, $len
1461 );
1462 return $resNow;
1463 }
1464 }
1465
1473 public static function revisionday( $parser, $title = null ) {
1474 $t = self::makeTitle( $parser, $title );
1475 if ( $t === null ) {
1476 return '';
1477 }
1478 return strval( (int)self::getRevisionTimestampSubstring(
1479 $parser, $t, 6, 2, self::MAX_TTS
1480 ) );
1481 }
1482
1490 public static function revisionday2( $parser, $title = null ) {
1491 $t = self::makeTitle( $parser, $title );
1492 if ( $t === null ) {
1493 return '';
1494 }
1495 return self::getRevisionTimestampSubstring(
1496 $parser, $t, 6, 2, self::MAX_TTS
1497 );
1498 }
1499
1507 public static function revisionmonth( $parser, $title = null ) {
1508 $t = self::makeTitle( $parser, $title );
1509 if ( $t === null ) {
1510 return '';
1511 }
1512 return self::getRevisionTimestampSubstring(
1513 $parser, $t, 4, 2, self::MAX_TTS
1514 );
1515 }
1516
1524 public static function revisionmonth1( $parser, $title = null ) {
1525 $t = self::makeTitle( $parser, $title );
1526 if ( $t === null ) {
1527 return '';
1528 }
1529 return strval( (int)self::getRevisionTimestampSubstring(
1530 $parser, $t, 4, 2, self::MAX_TTS
1531 ) );
1532 }
1533
1541 public static function revisionyear( $parser, $title = null ) {
1542 $t = self::makeTitle( $parser, $title );
1543 if ( $t === null ) {
1544 return '';
1545 }
1546 return self::getRevisionTimestampSubstring(
1547 $parser, $t, 0, 4, self::MAX_TTS
1548 );
1549 }
1550
1558 public static function revisiontimestamp( $parser, $title = null ) {
1559 $t = self::makeTitle( $parser, $title );
1560 if ( $t === null ) {
1561 return '';
1562 }
1563 return self::getRevisionTimestampSubstring(
1564 $parser, $t, 0, 14, self::MAX_TTS
1565 );
1566 }
1567
1575 public static function revisionuser( $parser, $title = null ) {
1576 $t = self::makeTitle( $parser, $title );
1577 if ( $t === null ) {
1578 return '';
1579 }
1580 // VARY_USER informs the edit saving system that getting the canonical
1581 // output after revision insertion requires a parse that used the
1582 // actual user ID.
1583 if ( $t->equals( $parser->getTitle() ) ) {
1584 // Fall back to Parser's "revision user" for the current title
1585 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_USER );
1586 // Note that getRevisionUser() can return null; we need to
1587 // be sure to cast this to (an empty) string, since returning
1588 // null means "magic variable not handled".
1589 return (string)$parser->getRevisionUser();
1590 }
1591 // Fetch revision from cache/database and return the value.
1592 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_USER );
1593 $user = ( $rev !== null ) ? $rev->getUser() : null;
1594 return $user ? $user->getName() : '';
1595 }
1596
1609 public static function cascadingsources( $parser, $title = '' ) {
1610 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
1611 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
1612 if ( $restrictionStore->areCascadeProtectionSourcesLoaded( $titleObject )
1614 ) {
1615 $names = [];
1616 $sources = $restrictionStore->getCascadeProtectionSources( $titleObject );
1617 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
1618 foreach ( $sources[0] as $sourcePageIdentity ) {
1619 $names[] = $titleFormatter->getPrefixedText( $sourcePageIdentity );
1620 }
1621 return implode( '|', $names );
1622 }
1623 return '';
1624 }
1625}
const NS_USER
Definition Defines.php:66
const NS_FILE
Definition Defines.php:70
const NS_SPECIAL
Definition Defines.php:53
const NS_MEDIA
Definition Defines.php:52
const PROTO_RELATIVE
Definition Defines.php:193
const NS_CATEGORY
Definition Defines.php:78
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL using $wgServer (or one of its alternatives).
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
$magicWords
@phpcs-require-sorted-array
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:88
Various core parser functions, registered in every Parser.
static language( $parser, $code='', $inLanguage='')
Gives language names.
static numberingroup( $parser, $name='', $raw=null)
static rootpagename( $parser, $title=null)
static localurl( $parser, $s='', $arg=null)
static formatnum( $parser, $num='', $arg=null)
static ns( $parser, $part1='')
static protectionexpiry( $parser, $type='', $title='')
Returns the requested protection expiry for the current page.
static pagenamee( $parser, $title=null)
static padleft( $parser, $string='', $length=0, $padding='0')
static fullurle( $parser, $s='', $arg=null)
static revisionmonth( $parser, $title=null)
Get the month with leading zeros from the last revision of a specified page.
static fullpagename( $parser, $title=null)
static revisionid( $parser, $title=null)
Get the id from the last revision of a specified page.
static pagesinnamespace( $parser, $namespace=0, $raw=null)
static numberofusers( $parser, $raw=null)
static special( $parser, $text)
static nse( $parser, $part1='')
static pagesize( $parser, $page='', $raw=null)
Return the size of the given page, or 0 if it's nonexistent.
static canonicalurl( $parser, $s='', $arg=null)
static basepagenamee( $parser, $title=null)
static subjectspacee( $parser, $title=null)
static numberofedits( $parser, $raw=null)
static numberoffiles( $parser, $raw=null)
static ucfirst( $parser, $s='')
static basepagename( $parser, $title=null)
static gender( $parser, $username,... $forms)
static numberofpages( $parser, $raw=null)
static lcfirst( $parser, $s='')
static urlFunction( $func, $s='', $arg=null)
static plural( $parser, $text='',... $forms)
static formatRaw( $num, $raw, $language, MagicWordFactory $magicWordFactory=null)
Formats a number according to a language.
static talkspacee( $parser, $title=null)
static bidi( $parser, $text='')
static revisionuser( $parser, $title=null)
Get the user from the last revision of a specified page.
static anchorencode( $parser, $text)
static subjectpagenamee( $parser, $title=null)
static namespacee( $parser, $title=null)
static subjectpagename( $parser, $title=null)
static filepath( $parser, $name='', $argA='', $argB='')
Usage {{filepath|300}}, {{filepath|nowiki}}, {{filepath|nowiki|300}} or {{filepath|300|nowiki}} or {{...
static padright( $parser, $string='', $length=0, $padding='0')
static pad( $parser, $string, $length, $padding='0', $direction=STR_PAD_RIGHT)
Unicode-safe str_pad with the restriction that $length is forced to be <= 500.
static canonicalurle( $parser, $s='', $arg=null)
static cascadingsources( $parser, $title='')
Returns the sources of any cascading protection acting on a specified page.
static numberofactiveusers( $parser, $raw=null)
static displaytitle( $parser, $text='', $uarg='')
Override the title of the page when viewed, provided we've been given a title which will normalise to...
static pageid( $parser, $title=null)
Get the pageid of a specified page.
static formatDate( $parser, $date, $defaultPref=null)
static urlencode( $parser, $s='', $arg=null)
urlencodes a string according to one of three patterns: (T24474)
static grammar( $parser, $case='', $word='')
static subpagenamee( $parser, $title=null)
static tagObj( $parser, $frame, $args)
Parser function to extension tag adaptor.
static revisionmonth1( $parser, $title=null)
Get the month from the last revision of a specified page.
static rootpagenamee( $parser, $title=null)
static protectionlevel( $parser, $type='', $title='')
Returns the requested protection level for the current page.
static namespacenumber( $parser, $title=null)
static revisionday( $parser, $title=null)
Get the day from the last revision of a specified page.
static subjectspace( $parser, $title=null)
static lc( $parser, $s='')
static pagesincategory( $parser, $name='', $arg1='', $arg2='')
Return the number of pages, files or subcats in the given category, or 0 if it's nonexistent.
static talkpagename( $parser, $title=null)
static fullurl( $parser, $s='', $arg=null)
static mwnamespace( $parser, $title=null)
Given a title, return the namespace name that would be given by the corresponding magic word.
static pagename( $parser, $title=null)
Functions to get and normalize pagenames, corresponding to the magic words of the same names.
static intFunction( $parser, $part1='',... $params)
static numberofadmins( $parser, $raw=null)
static numberofarticles( $parser, $raw=null)
static revisionyear( $parser, $title=null)
Get the year from the last revision of a specified page.
static revisionday2( $parser, $title=null)
Get the day with leading zeros from the last revision of a specified page.
static uc( $parser, $s='')
static talkpagenamee( $parser, $title=null)
static revisiontimestamp( $parser, $title=null)
Get the timestamp from the last revision of a specified page.
static subpagename( $parser, $title=null)
static localurle( $parser, $s='', $arg=null)
static defaultsort( $parser, $text, $uarg='')
static fullpagenamee( $parser, $title=null)
static talkspace( $parser, $title=null)
static speciale( $parser, $text)
Category objects are immutable, strictly speaking.
Definition Category.php:41
A class for passing options to services.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
A factory that stores information about MagicWords, and creates them on demand with caching.
get( $id)
Factory: creates an object representing an ID.
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:46
Exception representing a failure to look up a revision.
Page revision base class.
Static accessor class for site_stats and related things.
Definition SiteStats.php:36
Parent class for all special pages.
Represents a title within MediaWiki.
Definition Title.php:76
inNamespace(int $ns)
Returns true if the title is inside the specified namespace.
Definition Title.php:1316
equals(object $other)
Compares with another Title.
Definition Title.php:3162
getDBkey()
Get the main part with underscores.
Definition Title.php:1049
getText()
Get the text form (spaces not underscores) of the main part.
Definition Title.php:1031
hasFragment()
Check if a Title fragment is set.
Definition Title.php:1772
internal since 1.36
Definition User.php:98
static plaintextParam( $plaintext)
Definition Message.php:1275
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:115
addTrackingCategory( $msg)
Definition Parser.php:4081
getTargetLanguage()
Get the target language for the content being parsed.
Definition Parser.php:1119
getRevisionTimestamp()
Get the timestamp associated with the current revision, adjusted for the default server-local timesta...
Definition Parser.php:5980
getMagicWordFactory()
Get the MagicWordFactory that this Parser is using.
Definition Parser.php:1173
getOptions()
Definition Parser.php:1059
getRevisionUser()
Get the name of the user that edited the last revision.
Definition Parser.php:6008
isCurrentRevisionOfTitleCached(LinkTarget $link)
Definition Parser.php:3494
extensionSubstitution(array $params, PPFrame $frame, bool $processNowiki=false)
Return the text to be used for a given extension tag.
Definition Parser.php:3906
tagNeedsNowikiStrippedInTagPF(string $lowerTagName)
Definition Parser.php:3882
fetchCurrentRevisionRecordOfTitle(LinkTarget $link)
Fetch the current revision of a given title as a RevisionRecord.
Definition Parser.php:3464
setFunctionHook( $id, callable $callback, $flags=0)
Create a function, e.g.
Definition Parser.php:4912
getTitle()
Definition Parser.php:964
getStripList()
Get a list of strippable XML-like elements.
Definition Parser.php:1279
getTargetLanguageConverter()
Shorthand for getting a Language Converter for Target language.
Definition Parser.php:1592
getContentLanguage()
Get the content language that this Parser is using.
Definition Parser.php:1183
getOutput()
Definition Parser.php:1051
guessSectionNameFromWikiText( $text)
Try to guess the section anchor name based on a wikitext fragment presumably extracted from a heading...
Definition Parser.php:6125
getRevisionId()
Get the ID of the revision we are parsing.
Definition Parser.php:5915
static parseWidthParam( $value, $parseHeight=true)
Parsed a width param of imagelink like 300px or 200x300px.
Definition Parser.php:6278
const SFH_OBJECT_ARGS
Definition Parser.php:119
doQuotes( $text)
Helper function for handleAllQuotes()
Definition Parser.php:1930
incrementExpensiveFunctionCount()
Definition Parser.php:4012
markerSkipCallback( $s, callable $callback)
Call a callback function on all regions of the given text that are not inside strip markers,...
Definition Parser.php:6233
killMarkers( $text)
Remove any strip markers found in the given text.
Definition Parser.php:6264
msg(string $msg,... $args)
Helper function to correctly set the target language and title of a message based on the parser conte...
Definition Parser.php:4100
getRevisionRecordObject()
Get the revision record object for $this->mRevisionId.
Definition Parser.php:5925
const SFH_NO_HASH
Definition Parser.php:118
return true
Definition router.php:92
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42