MediaWiki master
CoreParserFunctions.php
Go to the documentation of this file.
1<?php
38use Wikimedia\RemexHtml\Tokenizer\Attributes;
39use Wikimedia\RemexHtml\Tokenizer\PlainAttributes;
40
47 private const MAX_TTS = 900;
48
52 public const REGISTER_OPTIONS = [
53 // See documentation for the corresponding config options
54 MainConfigNames::AllowDisplayTitle,
55 MainConfigNames::AllowSlowParserFunctions,
56 ];
57
65 public static function register( Parser $parser, ServiceOptions $options ) {
66 $options->assertRequiredOptions( self::REGISTER_OPTIONS );
67 $allowDisplayTitle = $options->get( MainConfigNames::AllowDisplayTitle );
68 $allowSlowParserFunctions = $options->get( MainConfigNames::AllowSlowParserFunctions );
69
70 # Syntax for arguments (see Parser::setFunctionHook):
71 # "name for lookup in localized magic words array",
72 # function callback,
73 # optional Parser::SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}}
74 # instead of {{#int:...}})
75 $noHashFunctions = [
76 'ns', 'nse', 'urlencode', 'lcfirst', 'ucfirst', 'lc', 'uc',
77 'localurl', 'localurle', 'fullurl', 'fullurle', 'canonicalurl',
78 'canonicalurle', 'formatnum', 'grammar', 'gender', 'plural', 'bidi',
79 'numberingroup', 'language',
80 'padleft', 'padright', 'anchorencode', 'defaultsort', 'filepath',
81 'pagesincategory', 'pagesize', 'protectionlevel', 'protectionexpiry',
82 # The following are the "parser function" forms of magic
83 # variables defined in CoreMagicVariables. The no-args form will
84 # go through the magic variable code path (and be cached); the
85 # presence of arguments will cause the parser function form to
86 # be invoked. (Note that the actual implementation will pass
87 # a Parser object as first argument, in addition to the
88 # parser function parameters.)
89
90 # For this group, the first parameter to the parser function is
91 # "page title", and the no-args form (and the magic variable)
92 # defaults to "current page title".
93 'pagename', 'pagenamee',
94 'fullpagename', 'fullpagenamee',
95 'subpagename', 'subpagenamee',
96 'rootpagename', 'rootpagenamee',
97 'basepagename', 'basepagenamee',
98 'talkpagename', 'talkpagenamee',
99 'subjectpagename', 'subjectpagenamee',
100 'pageid', 'revisionid', 'revisionday',
101 'revisionday2', 'revisionmonth', 'revisionmonth1', 'revisionyear',
102 'revisiontimestamp',
103 'revisionuser',
104 'cascadingsources',
105 'namespace', 'namespacee', 'namespacenumber', 'talkspace', 'talkspacee',
106 'subjectspace', 'subjectspacee',
107
108 # More parser functions corresponding to CoreMagicVariables.
109 # For this group, the first parameter to the parser function is
110 # "raw" (uses the 'raw' format if present) and the no-args form
111 # (and the magic variable) defaults to 'not raw'.
112 'numberofarticles', 'numberoffiles',
113 'numberofusers',
114 'numberofactiveusers',
115 'numberofpages',
116 'numberofadmins',
117 'numberofedits',
118 ];
119 foreach ( $noHashFunctions as $func ) {
120 $parser->setFunctionHook( $func, [ __CLASS__, $func ], Parser::SFH_NO_HASH );
121 }
122
123 $parser->setFunctionHook( 'int', [ __CLASS__, 'intFunction' ], Parser::SFH_NO_HASH );
124 $parser->setFunctionHook( 'special', [ __CLASS__, 'special' ] );
125 $parser->setFunctionHook( 'speciale', [ __CLASS__, 'speciale' ] );
126 $parser->setFunctionHook( 'tag', [ __CLASS__, 'tagObj' ], Parser::SFH_OBJECT_ARGS );
127 $parser->setFunctionHook( 'formatdate', [ __CLASS__, 'formatDate' ] );
128
129 if ( $allowDisplayTitle ) {
130 $parser->setFunctionHook(
131 'displaytitle',
132 [ __CLASS__, 'displaytitle' ],
133 Parser::SFH_NO_HASH
134 );
135 }
136 if ( $allowSlowParserFunctions ) {
137 $parser->setFunctionHook(
138 'pagesinnamespace',
139 [ __CLASS__, 'pagesinnamespace' ],
140 Parser::SFH_NO_HASH
141 );
142 }
143 }
144
151 public static function intFunction( $parser, $part1 = '', ...$params ) {
152 if ( strval( $part1 ) !== '' ) {
153 $message = wfMessage( $part1, $params )
154 ->inLanguage( $parser->getOptions()->getUserLangObj() );
155 return [ $message->plain(), 'noparse' => false ];
156 } else {
157 return [ 'found' => false ];
158 }
159 }
160
168 public static function formatDate( $parser, $date, $defaultPref = null ) {
169 $lang = $parser->getTargetLanguage();
170 $df = MediaWikiServices::getInstance()->getDateFormatterFactory()->get( $lang );
171
172 $date = trim( $date );
173
174 $pref = $parser->getOptions()->getDateFormat();
175
176 // Specify a different default date format other than the normal default
177 // if the user has 'default' for their setting
178 if ( $pref == 'default' && $defaultPref ) {
179 $pref = $defaultPref;
180 }
181
182 $date = $df->reformat( $pref, $date, [ 'match-whole' ] );
183 return $date;
184 }
185
186 public static function ns( $parser, $part1 = '' ) {
187 if ( intval( $part1 ) || $part1 == "0" ) {
188 $index = intval( $part1 );
189 } else {
190 $index = $parser->getContentLanguage()->getNsIndex( str_replace( ' ', '_', $part1 ) );
191 }
192 if ( $index !== false ) {
193 return $parser->getContentLanguage()->getFormattedNsText( $index );
194 } else {
195 return [ 'found' => false ];
196 }
197 }
198
199 public static function nse( $parser, $part1 = '' ) {
200 $ret = self::ns( $parser, $part1 );
201 if ( is_string( $ret ) ) {
202 $ret = wfUrlencode( str_replace( ' ', '_', $ret ) );
203 }
204 return $ret;
205 }
206
219 public static function urlencode( $parser, $s = '', $arg = null ) {
220 static $magicWords = null;
221 if ( $magicWords === null ) {
223 $parser->getMagicWordFactory()->newArray( [ 'url_path', 'url_query', 'url_wiki' ] );
224 }
225 switch ( $magicWords->matchStartToEnd( $arg ?? '' ) ) {
226 // Encode as though it's a wiki page, '_' for ' '.
227 case 'url_wiki':
228 $func = 'wfUrlencode';
229 $s = str_replace( ' ', '_', $s );
230 break;
231
232 // Encode for an HTTP Path, '%20' for ' '.
233 case 'url_path':
234 $func = 'rawurlencode';
235 break;
236
237 // Encode for HTTP query, '+' for ' '.
238 case 'url_query':
239 default:
240 $func = 'urlencode';
241 }
242 // See T105242, where the choice to kill markers and various
243 // other options were discussed.
244 return $func( $parser->killMarkers( $s ) );
245 }
246
247 public static function lcfirst( $parser, $s = '' ) {
248 return $parser->getContentLanguage()->lcfirst( $s );
249 }
250
251 public static function ucfirst( $parser, $s = '' ) {
252 return $parser->getContentLanguage()->ucfirst( $s );
253 }
254
260 public static function lc( $parser, $s = '' ) {
261 return $parser->markerSkipCallback( $s, [ $parser->getContentLanguage(), 'lc' ] );
262 }
263
269 public static function uc( $parser, $s = '' ) {
270 return $parser->markerSkipCallback( $s, [ $parser->getContentLanguage(), 'uc' ] );
271 }
272
273 public static function localurl( $parser, $s = '', $arg = null ) {
274 return self::urlFunction( 'getLocalURL', $s, $arg );
275 }
276
277 public static function localurle( $parser, $s = '', $arg = null ) {
278 $temp = self::urlFunction( 'getLocalURL', $s, $arg );
279 if ( !is_string( $temp ) ) {
280 return $temp;
281 } else {
282 return htmlspecialchars( $temp, ENT_COMPAT );
283 }
284 }
285
286 public static function fullurl( $parser, $s = '', $arg = null ) {
287 return self::urlFunction( 'getFullURL', $s, $arg );
288 }
289
290 public static function fullurle( $parser, $s = '', $arg = null ) {
291 $temp = self::urlFunction( 'getFullURL', $s, $arg );
292 if ( !is_string( $temp ) ) {
293 return $temp;
294 } else {
295 return htmlspecialchars( $temp, ENT_COMPAT );
296 }
297 }
298
299 public static function canonicalurl( $parser, $s = '', $arg = null ) {
300 return self::urlFunction( 'getCanonicalURL', $s, $arg );
301 }
302
303 public static function canonicalurle( $parser, $s = '', $arg = null ) {
304 $temp = self::urlFunction( 'getCanonicalURL', $s, $arg );
305 if ( !is_string( $temp ) ) {
306 return $temp;
307 } else {
308 return htmlspecialchars( $temp, ENT_COMPAT );
309 }
310 }
311
312 public static function urlFunction( $func, $s = '', $arg = null ) {
313 # Due to order of execution of a lot of bits, the values might be encoded
314 # before arriving here; if that's true, then the title can't be created
315 # and the variable will fail. If we can't get a decent title from the first
316 # attempt, url-decode and try for a second.
317 $title = Title::newFromText( $s ) ?? Title::newFromURL( urldecode( $s ) );
318 if ( $title !== null ) {
319 # Convert NS_MEDIA -> NS_FILE
320 if ( $title->inNamespace( NS_MEDIA ) ) {
321 $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
322 }
323 if ( $arg !== null ) {
324 $text = $title->$func( $arg );
325 } else {
326 $text = $title->$func();
327 }
328 return $text;
329 } else {
330 return [ 'found' => false ];
331 }
332 }
333
340 public static function formatnum( $parser, $num = '', $arg = null ) {
341 if ( self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'rawsuffix', $arg ) ) {
342 $func = [ $parser->getTargetLanguage(), 'parseFormattedNumber' ];
343 } elseif (
344 self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'nocommafysuffix', $arg )
345 ) {
346 $func = [ $parser->getTargetLanguage(), 'formatNumNoSeparators' ];
347 $func = self::getLegacyFormatNum( $parser, $func );
348 } else {
349 $func = [ $parser->getTargetLanguage(), 'formatNum' ];
350 $func = self::getLegacyFormatNum( $parser, $func );
351 }
352 return $parser->markerSkipCallback( $num, $func );
353 }
354
361 private static function getLegacyFormatNum( $parser, $callback ) {
362 // For historic reasons, the formatNum parser function will
363 // take arguments which are not actually formatted numbers,
364 // which then trigger deprecation warnings in Language::formatNum*.
365 // Instead emit a tracking category instead to allow linting.
366 return static function ( $number ) use ( $parser, $callback ) {
367 $validNumberRe = '(-(?=[\d\.]))?(\d+|(?=\.\d))(\.\d*)?([Ee][-+]?\d+)?';
368 if (
369 !is_numeric( $number ) &&
370 $number !== (string)NAN &&
371 $number !== (string)INF &&
372 $number !== (string)-INF
373 ) {
374 $parser->addTrackingCategory( 'nonnumeric-formatnum' );
375 // Don't split on NAN/INF in the legacy case since they are
376 // likely to be found embedded inside non-numeric text.
377 return preg_replace_callback( "/{$validNumberRe}/", static function ( $m ) use ( $callback ) {
378 return call_user_func( $callback, $m[0] );
379 }, $number );
380 }
381 return call_user_func( $callback, $number );
382 };
383 }
384
391 public static function grammar( $parser, $case = '', $word = '' ) {
392 $word = $parser->killMarkers( $word );
393 return $parser->getTargetLanguage()->convertGrammar( $word, $case );
394 }
395
402 public static function gender( $parser, $username, ...$forms ) {
403 // Some shortcuts to avoid loading user data unnecessarily
404 if ( count( $forms ) === 0 ) {
405 return '';
406 } elseif ( count( $forms ) === 1 ) {
407 return $forms[0];
408 }
409
410 $username = trim( $username );
411
412 $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
413 $gender = $userOptionsLookup->getDefaultOption( 'gender' );
414
415 // allow prefix and normalize (e.g. "&#42;foo" -> "*foo" ).
416 $title = Title::newFromText( $username, NS_USER );
417
418 if ( $title && $title->inNamespace( NS_USER ) ) {
419 $username = $title->getText();
420 }
421
422 // check parameter, or use the ParserOptions if in interface message
423 $user = User::newFromName( $username );
424 $genderCache = MediaWikiServices::getInstance()->getGenderCache();
425 if ( $user ) {
426 $gender = $genderCache->getGenderOf( $user, __METHOD__ );
427 } elseif ( $username === '' && $parser->getOptions()->getInterfaceMessage() ) {
428 $gender = $genderCache->getGenderOf( $parser->getOptions()->getUserIdentity(), __METHOD__ );
429 }
430 $ret = $parser->getTargetLanguage()->gender( $gender, $forms );
431 return $ret;
432 }
433
440 public static function plural( $parser, $text = '', ...$forms ) {
441 $text = $parser->getTargetLanguage()->parseFormattedNumber( $text );
442 settype( $text, ctype_digit( $text ) ? 'int' : 'float' );
443 // @phan-suppress-next-line PhanTypeMismatchArgument Phan does not handle settype
444 return $parser->getTargetLanguage()->convertPlural( $text, $forms );
445 }
446
452 public static function bidi( $parser, $text = '' ) {
453 return $parser->getTargetLanguage()->embedBidi( $text );
454 }
455
465 public static function displaytitle( $parser, $text = '', $uarg = '' ) {
466 $restrictDisplayTitle = MediaWikiServices::getInstance()->getMainConfig()
467 ->get( MainConfigNames::RestrictDisplayTitle );
468
469 static $magicWords = null;
470 if ( $magicWords === null ) {
471 $magicWords = $parser->getMagicWordFactory()->newArray(
472 [ 'displaytitle_noerror', 'displaytitle_noreplace' ] );
473 }
474 $arg = $magicWords->matchStartToEnd( $uarg );
475
476 // parse a limited subset of wiki markup (just the single quote items)
477 $text = $parser->doQuotes( $text );
478
479 // remove stripped text (e.g. the UNIQ-QINU stuff) that was generated by tag extensions/whatever
480 $text = $parser->killMarkers( $text );
481
482 // See T28547 for rationale for this processing.
483 // list of disallowed tags for DISPLAYTITLE
484 // these will be escaped even though they are allowed in normal wiki text
485 $bad = [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'blockquote', 'ol', 'ul', 'li', 'hr',
486 'table', 'tr', 'th', 'td', 'dl', 'dd', 'caption', 'p', 'ruby', 'rb', 'rt', 'rtc', 'rp', 'br' ];
487
488 // disallow some styles that could be used to bypass $wgRestrictDisplayTitle
489 if ( $restrictDisplayTitle ) {
490 // This code is tested with the cases marked T28547 in
491 // parserTests.txt
492 $htmlTagsCallback = static function ( Attributes $attr ): Attributes {
493 $decoded = $attr->getValues();
494
495 if ( isset( $decoded['style'] ) ) {
496 // this is called later anyway, but we need it right now for the regexes below to be safe
497 // calling it twice doesn't hurt
498 $decoded['style'] = Sanitizer::checkCss( $decoded['style'] );
499
500 if ( preg_match( '/(display|user-select|visibility)\s*:/i', $decoded['style'] ) ) {
501 $decoded['style'] = '/* attempt to bypass $wgRestrictDisplayTitle */';
502 }
503 }
504
505 return new PlainAttributes( $decoded );
506 };
507 } else {
508 $htmlTagsCallback = null;
509 }
510
511 // only requested titles that normalize to the actual title are allowed through
512 // if $wgRestrictDisplayTitle is true (it is by default)
513 // mimic the escaping process that occurs in OutputPage::setPageTitle
514 $text = Sanitizer::removeSomeTags( $text, [
515 'attrCallback' => $htmlTagsCallback,
516 'removeTags' => $bad,
517 ] );
518 $title = Title::newFromText( Sanitizer::stripAllTags( $text ) );
519 // Decode entities in $text the same way that Title::newFromText does
520 $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
521
522 if ( !$restrictDisplayTitle ||
523 ( $title instanceof Title
524 && !$title->hasFragment()
525 && $title->equals( $parser->getTitle() ) )
526 ) {
527 $old = $parser->getOutput()->getPageProperty( 'displaytitle' );
528 if ( $old === null || $arg !== 'displaytitle_noreplace' ) {
529 $parser->getOutput()->setDisplayTitle( $text );
530 }
531 if ( $old !== null && $old !== $text && !$arg ) {
532
533 $converter = $parser->getTargetLanguageConverter();
534 return '<span class="error">' .
535 $parser->msg( 'duplicate-displaytitle',
536 // Message should be parsed, but these params should only be escaped.
537 $converter->markNoConversion( wfEscapeWikiText( $old ) ),
538 $converter->markNoConversion( wfEscapeWikiText( $filteredText ) )
539 )->text() .
540 '</span>';
541 } else {
542 return '';
543 }
544 } else {
545 $parser->getOutput()->addWarningMsg(
546 'restricted-displaytitle',
547 // Message should be parsed, but this param should only be escaped.
548 Message::plaintextParam( $filteredText )
549 );
550 $parser->addTrackingCategory( 'restricted-displaytitle-ignored' );
551 }
552 }
553
563 private static function matchAgainstMagicword(
564 MagicWordFactory $magicWordFactory, $magicword, $value
565 ) {
566 $value = trim( strval( $value ) );
567 if ( $value === '' ) {
568 return false;
569 }
570 $mwObject = $magicWordFactory->get( $magicword );
571 return $mwObject->matchStartToEnd( $value );
572 }
573
583 public static function formatRaw(
584 $num, $raw, $language, MagicWordFactory $magicWordFactory = null
585 ) {
586 if ( $raw !== null && $raw !== '' ) {
587 if ( !$magicWordFactory ) {
588 $magicWordFactory = MediaWikiServices::getInstance()->getMagicWordFactory();
589 }
590 if ( self::matchAgainstMagicword( $magicWordFactory, 'rawsuffix', $raw ) ) {
591 return (string)$num;
592 }
593 }
594 return $language->formatNum( $num );
595 }
596
597 public static function numberofpages( $parser, $raw = null ) {
598 return self::formatRaw( SiteStats::pages(), $raw, $parser->getTargetLanguage() );
599 }
600
601 public static function numberofusers( $parser, $raw = null ) {
602 return self::formatRaw( SiteStats::users(), $raw, $parser->getTargetLanguage() );
603 }
604
605 public static function numberofactiveusers( $parser, $raw = null ) {
606 return self::formatRaw( SiteStats::activeUsers(), $raw, $parser->getTargetLanguage() );
607 }
608
609 public static function numberofarticles( $parser, $raw = null ) {
610 return self::formatRaw( SiteStats::articles(), $raw, $parser->getTargetLanguage() );
611 }
612
613 public static function numberoffiles( $parser, $raw = null ) {
614 return self::formatRaw( SiteStats::images(), $raw, $parser->getTargetLanguage() );
615 }
616
617 public static function numberofadmins( $parser, $raw = null ) {
618 return self::formatRaw(
619 SiteStats::numberingroup( 'sysop' ),
620 $raw,
621 $parser->getTargetLanguage()
622 );
623 }
624
625 public static function numberofedits( $parser, $raw = null ) {
626 return self::formatRaw( SiteStats::edits(), $raw, $parser->getTargetLanguage() );
627 }
628
629 public static function pagesinnamespace( $parser, $namespace = 0, $raw = null ) {
630 return self::formatRaw(
631 SiteStats::pagesInNs( intval( $namespace ) ),
632 $raw,
633 $parser->getTargetLanguage()
634 );
635 }
636
637 public static function numberingroup( $parser, $name = '', $raw = null ) {
638 return self::formatRaw(
639 SiteStats::numberingroup( strtolower( $name ) ),
640 $raw,
641 $parser->getTargetLanguage()
642 );
643 }
644
653 private static function makeTitle( Parser $parser, ?string $t ) {
654 if ( $t === null ) {
655 // For consistency with magic variable forms
656 $title = $parser->getTitle();
657 } else {
658 $title = Title::newFromText( $t );
659 }
660 return $title;
661 }
662
671 public static function namespace( $parser, $title = null ) {
672 $t = self::makeTitle( $parser, $title );
673 if ( $t === null ) {
674 return '';
675 }
676 return str_replace( '_', ' ', $t->getNsText() );
677 }
678
679 public static function namespacee( $parser, $title = null ) {
680 $t = self::makeTitle( $parser, $title );
681 if ( $t === null ) {
682 return '';
683 }
684 return wfUrlencode( $t->getNsText() );
685 }
686
687 public static function namespacenumber( $parser, $title = null ) {
688 $t = self::makeTitle( $parser, $title );
689 if ( $t === null ) {
690 return '';
691 }
692 return (string)$t->getNamespace();
693 }
694
695 public static function talkspace( $parser, $title = null ) {
696 $t = self::makeTitle( $parser, $title );
697 if ( $t === null || !$t->canHaveTalkPage() ) {
698 return '';
699 }
700 return str_replace( '_', ' ', $t->getTalkNsText() );
701 }
702
703 public static function talkspacee( $parser, $title = null ) {
704 $t = self::makeTitle( $parser, $title );
705 if ( $t === null || !$t->canHaveTalkPage() ) {
706 return '';
707 }
708 return wfUrlencode( $t->getTalkNsText() );
709 }
710
711 public static function subjectspace( $parser, $title = null ) {
712 $t = self::makeTitle( $parser, $title );
713 if ( $t === null ) {
714 return '';
715 }
716 return str_replace( '_', ' ', $t->getSubjectNsText() );
717 }
718
719 public static function subjectspacee( $parser, $title = null ) {
720 $t = self::makeTitle( $parser, $title );
721 if ( $t === null ) {
722 return '';
723 }
724 return wfUrlencode( $t->getSubjectNsText() );
725 }
726
734 public static function pagename( $parser, $title = null ) {
735 $t = self::makeTitle( $parser, $title );
736 if ( $t === null ) {
737 return '';
738 }
739 return wfEscapeWikiText( $t->getText() );
740 }
741
742 public static function pagenamee( $parser, $title = null ) {
743 $t = self::makeTitle( $parser, $title );
744 if ( $t === null ) {
745 return '';
746 }
747 return wfEscapeWikiText( $t->getPartialURL() );
748 }
749
750 public static function fullpagename( $parser, $title = null ) {
751 $t = self::makeTitle( $parser, $title );
752 if ( $t === null ) {
753 return '';
754 }
755 return wfEscapeWikiText( $t->getPrefixedText() );
756 }
757
758 public static function fullpagenamee( $parser, $title = null ) {
759 $t = self::makeTitle( $parser, $title );
760 if ( $t === null ) {
761 return '';
762 }
763 return wfEscapeWikiText( $t->getPrefixedURL() );
764 }
765
766 public static function subpagename( $parser, $title = null ) {
767 $t = self::makeTitle( $parser, $title );
768 if ( $t === null ) {
769 return '';
770 }
771 return wfEscapeWikiText( $t->getSubpageText() );
772 }
773
774 public static function subpagenamee( $parser, $title = null ) {
775 $t = self::makeTitle( $parser, $title );
776 if ( $t === null ) {
777 return '';
778 }
779 return wfEscapeWikiText( $t->getSubpageUrlForm() );
780 }
781
782 public static function rootpagename( $parser, $title = null ) {
783 $t = self::makeTitle( $parser, $title );
784 if ( $t === null ) {
785 return '';
786 }
787 return wfEscapeWikiText( $t->getRootText() );
788 }
789
790 public static function rootpagenamee( $parser, $title = null ) {
791 $t = self::makeTitle( $parser, $title );
792 if ( $t === null ) {
793 return '';
794 }
795 return wfEscapeWikiText( wfUrlencode( str_replace( ' ', '_', $t->getRootText() ) ) );
796 }
797
798 public static function basepagename( $parser, $title = null ) {
799 $t = self::makeTitle( $parser, $title );
800 if ( $t === null ) {
801 return '';
802 }
803 return wfEscapeWikiText( $t->getBaseText() );
804 }
805
806 public static function basepagenamee( $parser, $title = null ) {
807 $t = self::makeTitle( $parser, $title );
808 if ( $t === null ) {
809 return '';
810 }
811 return wfEscapeWikiText( wfUrlencode( str_replace( ' ', '_', $t->getBaseText() ) ) );
812 }
813
814 public static function talkpagename( $parser, $title = null ) {
815 $t = self::makeTitle( $parser, $title );
816 if ( $t === null || !$t->canHaveTalkPage() ) {
817 return '';
818 }
819 return wfEscapeWikiText( $t->getTalkPage()->getPrefixedText() );
820 }
821
822 public static function talkpagenamee( $parser, $title = null ) {
823 $t = self::makeTitle( $parser, $title );
824 if ( $t === null || !$t->canHaveTalkPage() ) {
825 return '';
826 }
827 return wfEscapeWikiText( $t->getTalkPage()->getPrefixedURL() );
828 }
829
830 public static function subjectpagename( $parser, $title = null ) {
831 $t = self::makeTitle( $parser, $title );
832 if ( $t === null ) {
833 return '';
834 }
835 return wfEscapeWikiText( $t->getSubjectPage()->getPrefixedText() );
836 }
837
838 public static function subjectpagenamee( $parser, $title = null ) {
839 $t = self::makeTitle( $parser, $title );
840 if ( $t === null ) {
841 return '';
842 }
843 return wfEscapeWikiText( $t->getSubjectPage()->getPrefixedURL() );
844 }
845
856 public static function pagesincategory( $parser, $name = '', $arg1 = '', $arg2 = '' ) {
857 static $magicWords = null;
858 if ( $magicWords === null ) {
859 $magicWords = $parser->getMagicWordFactory()->newArray( [
860 'pagesincategory_all',
861 'pagesincategory_pages',
862 'pagesincategory_subcats',
863 'pagesincategory_files'
864 ] );
865 }
866 static $cache = [];
867
868 // split the given option to its variable
869 if ( self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'rawsuffix', $arg1 ) ) {
870 // {{pagesincategory:|raw[|type]}}
871 $raw = $arg1;
872 $type = $magicWords->matchStartToEnd( $arg2 );
873 } else {
874 // {{pagesincategory:[|type[|raw]]}}
875 $type = $magicWords->matchStartToEnd( $arg1 );
876 $raw = $arg2;
877 }
878 if ( !$type ) { // backward compatibility
879 $type = 'pagesincategory_all';
880 }
881
882 $title = Title::makeTitleSafe( NS_CATEGORY, $name );
883 if ( !$title ) { # invalid title
884 return self::formatRaw( 0, $raw, $parser->getTargetLanguage() );
885 }
886 $languageConverter = MediaWikiServices::getInstance()
887 ->getLanguageConverterFactory()
888 ->getLanguageConverter( $parser->getContentLanguage() );
889 $languageConverter->findVariantLink( $name, $title, true );
890
891 // Normalize name for cache
892 $name = $title->getDBkey();
893
894 if ( !isset( $cache[$name] ) ) {
895 $category = Category::newFromTitle( $title );
896
897 $allCount = $subcatCount = $fileCount = $pageCount = 0;
898 if ( $parser->incrementExpensiveFunctionCount() ) {
899 $allCount = $category->getMemberCount();
900 $subcatCount = $category->getSubcatCount();
901 $fileCount = $category->getFileCount();
902 $pageCount = $category->getPageCount( Category::COUNT_CONTENT_PAGES );
903 }
904 $cache[$name]['pagesincategory_all'] = $allCount;
905 $cache[$name]['pagesincategory_pages'] = $pageCount;
906 $cache[$name]['pagesincategory_subcats'] = $subcatCount;
907 $cache[$name]['pagesincategory_files'] = $fileCount;
908 }
909
910 $count = $cache[$name][$type];
911 return self::formatRaw( $count, $raw, $parser->getTargetLanguage() );
912 }
913
923 public static function pagesize( $parser, $page = '', $raw = null ) {
924 $title = Title::newFromText( $page );
925
926 if ( !is_object( $title ) || $title->isExternal() ) {
927 return self::formatRaw( 0, $raw, $parser->getTargetLanguage() );
928 }
929
930 // fetch revision from cache/database and return the value
931 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_SHA1 );
932 $length = $rev ? $rev->getSize() : 0;
933 if ( $length === null ) {
934 // We've had bugs where rev_len was not being recorded for empty pages, see T135414
935 $length = 0;
936 }
937 return self::formatRaw( $length, $raw, $parser->getTargetLanguage() );
938 }
939
952 public static function protectionlevel( $parser, $type = '', $title = '' ) {
953 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
954 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
955 if ( $restrictionStore->areRestrictionsLoaded( $titleObject ) || $parser->incrementExpensiveFunctionCount() ) {
956 $restrictions = $restrictionStore->getRestrictions( $titleObject, strtolower( $type ) );
957 # RestrictionStore::getRestrictions returns an array, its possible it may have
958 # multiple values in the future
959 return implode( ',', $restrictions );
960 }
961 return '';
962 }
963
976 public static function protectionexpiry( $parser, $type = '', $title = '' ) {
977 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
978 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
979 if ( $restrictionStore->areRestrictionsLoaded( $titleObject ) || $parser->incrementExpensiveFunctionCount() ) {
980 // getRestrictionExpiry() returns null on invalid type; trying to
981 // match protectionlevel() function that returns empty string instead
982 return $restrictionStore->getRestrictionExpiry( $titleObject, strtolower( $type ) ) ?? '';
983 }
984 return '';
985 }
986
994 public static function language( $parser, $code = '', $inLanguage = '' ) {
995 $code = strtolower( $code );
996 $inLanguage = strtolower( $inLanguage );
997 $lang = MediaWikiServices::getInstance()
998 ->getLanguageNameUtils()
999 ->getLanguageName( $code, $inLanguage );
1000 return $lang !== '' ? $lang : LanguageCode::bcp47( $code );
1001 }
1002
1012 public static function pad(
1013 $parser, $string, $length, $padding = '0', $direction = STR_PAD_RIGHT
1014 ) {
1015 $padding = $parser->killMarkers( $padding );
1016 $lengthOfPadding = mb_strlen( $padding );
1017 if ( $lengthOfPadding == 0 ) {
1018 return $string;
1019 }
1020
1021 # The remaining length to add counts down to 0 as padding is added
1022 $length = min( (int)$length, 500 ) - mb_strlen( $string );
1023 if ( $length <= 0 ) {
1024 // Nothing to add
1025 return $string;
1026 }
1027
1028 # $finalPadding is just $padding repeated enough times so that
1029 # mb_strlen( $string ) + mb_strlen( $finalPadding ) == $length
1030 $finalPadding = '';
1031 while ( $length > 0 ) {
1032 # If $length < $lengthofPadding, truncate $padding so we get the
1033 # exact length desired.
1034 $finalPadding .= mb_substr( $padding, 0, $length );
1035 $length -= $lengthOfPadding;
1036 }
1037
1038 if ( $direction == STR_PAD_LEFT ) {
1039 return $finalPadding . $string;
1040 } else {
1041 return $string . $finalPadding;
1042 }
1043 }
1044
1045 public static function padleft( $parser, $string = '', $length = 0, $padding = '0' ) {
1046 return self::pad( $parser, $string, $length, $padding, STR_PAD_LEFT );
1047 }
1048
1049 public static function padright( $parser, $string = '', $length = 0, $padding = '0' ) {
1050 return self::pad( $parser, $string, $length, $padding );
1051 }
1052
1058 public static function anchorencode( $parser, $text ) {
1059 $text = $parser->killMarkers( $text );
1060 $section = (string)substr( $parser->guessSectionNameFromWikiText( $text ), 1 );
1061 return Sanitizer::safeEncodeAttribute( $section );
1062 }
1063
1064 public static function special( $parser, $text ) {
1065 [ $page, $subpage ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
1066 resolveAlias( $text );
1067 if ( $page ) {
1068 $title = SpecialPage::getTitleFor( $page, $subpage );
1069 return $title->getPrefixedText();
1070 } else {
1071 // unknown special page, just use the given text as its title, if at all possible
1072 $title = Title::makeTitleSafe( NS_SPECIAL, $text );
1073 return $title ? $title->getPrefixedText() : self::special( $parser, 'Badtitle' );
1074 }
1075 }
1076
1077 public static function speciale( $parser, $text ) {
1078 return wfUrlencode( str_replace( ' ', '_', self::special( $parser, $text ) ) );
1079 }
1080
1089 public static function defaultsort( $parser, $text, $uarg = '' ) {
1090 static $magicWords = null;
1091 if ( $magicWords === null ) {
1092 $magicWords = $parser->getMagicWordFactory()->newArray(
1093 [ 'defaultsort_noerror', 'defaultsort_noreplace' ] );
1094 }
1095 $arg = $magicWords->matchStartToEnd( $uarg );
1096
1097 $text = trim( $text );
1098 if ( strlen( $text ) == 0 ) {
1099 return '';
1100 }
1101 $old = $parser->getOutput()->getPageProperty( 'defaultsort' );
1102 if ( $old === null || $arg !== 'defaultsort_noreplace' ) {
1103 $parser->getOutput()->setPageProperty( 'defaultsort', $text );
1104 }
1105
1106 if ( $old === null || $old == $text || $arg ) {
1107 return '';
1108 } else {
1109 $converter = $parser->getTargetLanguageConverter();
1110 return '<span class="error">' .
1111 $parser->msg( 'duplicate-defaultsort',
1112 // Message should be parsed, but these params should only be escaped.
1113 $converter->markNoConversion( wfEscapeWikiText( $old ) ),
1114 $converter->markNoConversion( wfEscapeWikiText( $text ) )
1115 )->text() .
1116 '</span>';
1117 }
1118 }
1119
1131 public static function filepath( $parser, $name = '', $argA = '', $argB = '' ) {
1132 $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $name );
1133
1134 if ( $argA == 'nowiki' ) {
1135 // {{filepath: | option [| size] }}
1136 $isNowiki = true;
1137 $parsedWidthParam = Parser::parseWidthParam( $argB );
1138 } else {
1139 // {{filepath: [| size [|option]] }}
1140 $parsedWidthParam = Parser::parseWidthParam( $argA );
1141 $isNowiki = ( $argB == 'nowiki' );
1142 }
1143
1144 if ( $file ) {
1145 $url = $file->getFullUrl();
1146
1147 // If a size is requested...
1148 if ( count( $parsedWidthParam ) ) {
1149 $mto = $file->transform( $parsedWidthParam );
1150 // ... and we can
1151 if ( $mto && !$mto->isError() ) {
1152 // ... change the URL to point to a thumbnail.
1153 $url = wfExpandUrl( $mto->getUrl(), PROTO_RELATIVE );
1154 }
1155 }
1156 if ( $isNowiki ) {
1157 return [ $url, 'nowiki' => true ];
1158 }
1159 return $url;
1160 } else {
1161 return '';
1162 }
1163 }
1164
1172 public static function tagObj( $parser, $frame, $args ) {
1173 if ( !count( $args ) ) {
1174 return '';
1175 }
1176 $tagName = strtolower( trim( $frame->expand( array_shift( $args ) ) ) );
1177 $processNowiki = $parser->tagNeedsNowikiStrippedInTagPF( $tagName ) ? PPFrame::PROCESS_NOWIKI : 0;
1178
1179 if ( count( $args ) ) {
1180 $inner = $frame->expand( array_shift( $args ), $processNowiki );
1181 } else {
1182 $inner = null;
1183 }
1184
1185 $attributes = [];
1186 foreach ( $args as $arg ) {
1187 $bits = $arg->splitArg();
1188 if ( strval( $bits['index'] ) === '' ) {
1189 $name = trim( $frame->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
1190 $value = trim( $frame->expand( $bits['value'] ) );
1191 if ( preg_match( '/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m ) ) {
1192 $value = $m[1] ?? '';
1193 }
1194 $attributes[$name] = $value;
1195 }
1196 }
1197
1198 $stripList = $parser->getStripList();
1199 if ( !in_array( $tagName, $stripList ) ) {
1200 // we can't handle this tag (at least not now), so just re-emit it as an ordinary tag
1201 $attrText = '';
1202 foreach ( $attributes as $name => $value ) {
1203 $attrText .= ' ' . htmlspecialchars( $name ) .
1204 '="' . htmlspecialchars( $value, ENT_COMPAT ) . '"';
1205 }
1206 if ( $inner === null ) {
1207 return "<$tagName$attrText/>";
1208 }
1209 return "<$tagName$attrText>$inner</$tagName>";
1210 }
1211
1212 $params = [
1213 'name' => $tagName,
1214 'inner' => $inner,
1215 'attributes' => $attributes,
1216 'close' => "</$tagName>",
1217 ];
1218 return $parser->extensionSubstitution( $params, $frame );
1219 }
1220
1234 private static function getCachedRevisionObject( $parser, $title, $vary ) {
1235 if ( !$title ) {
1236 return null;
1237 }
1238
1239 $revisionRecord = null;
1240
1241 $isSelfReferential = $title->equals( $parser->getTitle() );
1242 if ( $isSelfReferential ) {
1243 // Revision is for the same title that is currently being parsed. Only use the last
1244 // saved revision, regardless of Parser::getRevisionId() or fake revision injection
1245 // callbacks against the current title.
1246
1247 // FIXME (T318278): the above is the intention, but doesn't
1248 // describe the actual current behavior of this code, since
1249 // ->isCurrent() for the last saved revision will return
1250 // false so we're going to fall through and end up calling
1251 // ->getCurrentRevisionRecordOfTitle().
1252 $parserRevisionRecord = $parser->getRevisionRecordObject();
1253 if ( $parserRevisionRecord && $parserRevisionRecord->isCurrent() ) {
1254 $revisionRecord = $parserRevisionRecord;
1255 }
1256 }
1257
1258 $parserOutput = $parser->getOutput();
1259 if ( !$revisionRecord ) {
1260 if (
1261 !$parser->isCurrentRevisionOfTitleCached( $title ) &&
1263 ) {
1264 return null; // not allowed
1265 }
1266 // Get the current revision, ignoring Parser::getRevisionId() being null/old
1267 $revisionRecord = $parser->fetchCurrentRevisionRecordOfTitle( $title );
1268 if ( !$revisionRecord ) {
1269 // Convert `false` error return to `null`
1270 $revisionRecord = null;
1271 }
1272 // Register dependency in templatelinks
1273 $parserOutput->addTemplate(
1274 $title,
1275 $revisionRecord ? $revisionRecord->getPageId() : 0,
1276 $revisionRecord ? $revisionRecord->getId() : 0
1277 );
1278 }
1279
1280 if ( $isSelfReferential ) {
1281 wfDebug( __METHOD__ . ": used current revision, setting $vary" );
1282 // Upon page save, the result of the parser function using this might change
1283 $parserOutput->setOutputFlag( $vary );
1284 if ( $vary === ParserOutputFlags::VARY_REVISION_SHA1 && $revisionRecord ) {
1285 try {
1286 $sha1 = $revisionRecord->getSha1();
1287 } catch ( RevisionAccessException $e ) {
1288 $sha1 = null;
1289 }
1290 $parserOutput->setRevisionUsedSha1Base36( $sha1 );
1291 }
1292 }
1293
1294 return $revisionRecord;
1295 }
1296
1304 public static function pageid( $parser, $title = null ) {
1305 $t = self::makeTitle( $parser, $title );
1306 if ( !$t ) {
1307 return '';
1308 } elseif ( !$t->canExist() || $t->isExternal() ) {
1309 return 0; // e.g. special page or interwiki link
1310 }
1311
1312 $parserOutput = $parser->getOutput();
1313
1314 if ( $t->equals( $parser->getTitle() ) ) {
1315 // Revision is for the same title that is currently being parsed.
1316 // Use the title from Parser in case a new page ID was injected into it.
1317 $parserOutput->setOutputFlag( ParserOutputFlags::VARY_PAGE_ID );
1318 $id = $parser->getTitle()->getArticleID();
1319 if ( $id ) {
1320 $parserOutput->setSpeculativePageIdUsed( $id );
1321 }
1322
1323 return $id;
1324 }
1325
1326 // Check the link cache for the title
1327 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
1328 $pdbk = $t->getPrefixedDBkey();
1329 $id = $linkCache->getGoodLinkID( $pdbk );
1330 if ( $id != 0 || $linkCache->isBadLink( $pdbk ) ) {
1331 $parserOutput->addLink( $t, $id );
1332
1333 return $id;
1334 }
1335
1336 // We need to load it from the DB, so mark expensive
1337 if ( $parser->incrementExpensiveFunctionCount() ) {
1338 $id = $t->getArticleID();
1339 $parserOutput->addLink( $t, $id );
1340
1341 return $id;
1342 }
1343
1344 return null;
1345 }
1346
1354 public static function revisionid( $parser, $title = null ) {
1355 $t = self::makeTitle( $parser, $title );
1356 if ( $t === null || $t->isExternal() ) {
1357 return '';
1358 }
1359
1360 $services = MediaWikiServices::getInstance();
1361 if (
1362 $t->equals( $parser->getTitle() ) &&
1363 $services->getMainConfig()->get( MainConfigNames::MiserMode ) &&
1364 !$parser->getOptions()->getInterfaceMessage() &&
1365 // @TODO: disallow this word on all namespaces (T235957)
1366 $services->getNamespaceInfo()->isSubject( $t->getNamespace() )
1367 ) {
1368 // Use a stub result instead of the actual revision ID in order to avoid
1369 // double parses on page save but still allow preview detection (T137900)
1370 if ( $parser->getRevisionId() || $parser->getOptions()->getSpeculativeRevId() ) {
1371 return '-';
1372 } else {
1373 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS );
1374 return '';
1375 }
1376 }
1377 // Fetch revision from cache/database and return the value.
1378 // Inform the edit saving system that getting the canonical output
1379 // after revision insertion requires a parse that used that exact
1380 // revision ID.
1381 if ( $t->equals( $parser->getTitle() ) && $title === null ) {
1382 // special handling for no-arg case: use speculative rev id
1383 // for current page.
1384 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_ID );
1385 $id = $parser->getRevisionId();
1386 if ( $id === 0 ) {
1387 $rev = $parser->getRevisionRecordObject();
1388 if ( $rev ) {
1389 $id = $rev->getId();
1390 }
1391 }
1392 if ( !$id ) {
1393 $id = $parser->getOptions()->getSpeculativeRevId();
1394 if ( $id ) {
1395 $parser->getOutput()->setSpeculativeRevIdUsed( $id );
1396 }
1397 }
1398 return (string)$id;
1399 }
1400 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_REVISION_ID );
1401 return $rev ? $rev->getId() : '';
1402 }
1403
1404 private static function getRevisionTimestampSubstring(
1405 Parser $parser,
1406 Title $title,
1407 int $start,
1408 int $len,
1409 int $mtts
1410 ): string {
1411 // If fetching the revision timestamp of the current page, substitute the
1412 // speculative timestamp to be used when this revision is saved. This
1413 // avoids having to invalidate the cache immediately by assuming the "last
1414 // saved revision" will in fact be this one.
1415 // Don't do this for interface messages (eg, edit notices) however; in that
1416 // case fall through and use the actual timestamp of the last saved revision.
1417 if ( $title->equals( $parser->getTitle() ) && !$parser->getOptions()->getInterfaceMessage() ) {
1418 // Get the timezone-adjusted timestamp to be used for this revision
1419 $resNow = substr( $parser->getRevisionTimestamp(), $start, $len );
1420 // Possibly set vary-revision if there is not yet an associated revision
1421 if ( !$parser->getRevisionRecordObject() ) {
1422 // Get the timezone-adjusted timestamp $mtts seconds in the future.
1423 // This future is relative to the current time and not that of the
1424 // parser options. The rendered timestamp can be compared to that
1425 // of the timestamp specified by the parser options.
1426 $resThen = substr(
1427 $parser->getContentLanguage()->userAdjust( wfTimestamp( TS_MW, time() + $mtts ), '' ),
1428 $start,
1429 $len
1430 );
1431
1432 if ( $resNow !== $resThen ) {
1433 // Inform the edit saving system that getting the canonical output after
1434 // revision insertion requires a parse that used an actual revision timestamp
1435 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1436 }
1437 }
1438
1439 return $resNow;
1440 } else {
1441 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1442 if ( !$rev ) {
1443 return '';
1444 }
1445 $resNow = substr(
1446 $parser->getContentLanguage()->userAdjust( $rev->getTimestamp(), '' ), $start, $len
1447 );
1448 return $resNow;
1449 }
1450 }
1451
1459 public static function revisionday( $parser, $title = null ) {
1460 $t = self::makeTitle( $parser, $title );
1461 if ( $t === null || $t->isExternal() ) {
1462 return '';
1463 }
1464 return strval( (int)self::getRevisionTimestampSubstring(
1465 $parser, $t, 6, 2, self::MAX_TTS
1466 ) );
1467 }
1468
1476 public static function revisionday2( $parser, $title = null ) {
1477 $t = self::makeTitle( $parser, $title );
1478 if ( $t === null || $t->isExternal() ) {
1479 return '';
1480 }
1481 return self::getRevisionTimestampSubstring(
1482 $parser, $t, 6, 2, self::MAX_TTS
1483 );
1484 }
1485
1493 public static function revisionmonth( $parser, $title = null ) {
1494 $t = self::makeTitle( $parser, $title );
1495 if ( $t === null || $t->isExternal() ) {
1496 return '';
1497 }
1498 return self::getRevisionTimestampSubstring(
1499 $parser, $t, 4, 2, self::MAX_TTS
1500 );
1501 }
1502
1510 public static function revisionmonth1( $parser, $title = null ) {
1511 $t = self::makeTitle( $parser, $title );
1512 if ( $t === null || $t->isExternal() ) {
1513 return '';
1514 }
1515 return strval( (int)self::getRevisionTimestampSubstring(
1516 $parser, $t, 4, 2, self::MAX_TTS
1517 ) );
1518 }
1519
1527 public static function revisionyear( $parser, $title = null ) {
1528 $t = self::makeTitle( $parser, $title );
1529 if ( $t === null || $t->isExternal() ) {
1530 return '';
1531 }
1532 return self::getRevisionTimestampSubstring(
1533 $parser, $t, 0, 4, self::MAX_TTS
1534 );
1535 }
1536
1544 public static function revisiontimestamp( $parser, $title = null ) {
1545 $t = self::makeTitle( $parser, $title );
1546 if ( $t === null || $t->isExternal() ) {
1547 return '';
1548 }
1549 return self::getRevisionTimestampSubstring(
1550 $parser, $t, 0, 14, self::MAX_TTS
1551 );
1552 }
1553
1561 public static function revisionuser( $parser, $title = null ) {
1562 $t = self::makeTitle( $parser, $title );
1563 if ( $t === null || $t->isExternal() ) {
1564 return '';
1565 }
1566 // VARY_USER informs the edit saving system that getting the canonical
1567 // output after revision insertion requires a parse that used the
1568 // actual user ID.
1569 if ( $t->equals( $parser->getTitle() ) ) {
1570 // Fall back to Parser's "revision user" for the current title
1571 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_USER );
1572 // Note that getRevisionUser() can return null; we need to
1573 // be sure to cast this to (an empty) string, since returning
1574 // null means "magic variable not handled".
1575 return (string)$parser->getRevisionUser();
1576 }
1577 // Fetch revision from cache/database and return the value.
1578 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_USER );
1579 $user = ( $rev !== null ) ? $rev->getUser() : null;
1580 return $user ? $user->getName() : '';
1581 }
1582
1595 public static function cascadingsources( $parser, $title = '' ) {
1596 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
1597 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
1598 if ( $restrictionStore->areCascadeProtectionSourcesLoaded( $titleObject )
1600 ) {
1601 $names = [];
1602 $sources = $restrictionStore->getCascadeProtectionSources( $titleObject );
1603 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
1604 foreach ( $sources[0] as $sourcePageIdentity ) {
1605 $names[] = $titleFormatter->getPrefixedText( $sourcePageIdentity );
1606 }
1607 return implode( '|', $names );
1608 }
1609 return '';
1610 }
1611}
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:204
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,...
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
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.
$magicWords
@phpcs-require-sorted-array
array $params
The job parameters.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:81
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 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:42
A class for passing options to services.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Store information about magic words, and create/cache MagicWord objects.
get( $id)
Get a MagicWord object for a given internal ID.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:156
getTargetLanguageConverter()
Shorthand for getting a Language Converter for Target language.
Definition Parser.php:1665
markerSkipCallback( $s, callable $callback)
Call a callback function on all regions of the given text that are not inside strip markers,...
Definition Parser.php:6318
tagNeedsNowikiStrippedInTagPF(string $lowerTagName)
Definition Parser.php:3964
getMagicWordFactory()
Get the MagicWordFactory that this Parser is using.
Definition Parser.php:1246
setFunctionHook( $id, callable $callback, $flags=0)
Create a function, e.g.
Definition Parser.php:5027
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:4182
guessSectionNameFromWikiText( $text)
Try to guess the section anchor name based on a wikitext fragment presumably extracted from a heading...
Definition Parser.php:6210
isCurrentRevisionOfTitleCached(LinkTarget $link)
Definition Parser.php:3569
getRevisionId()
Get the ID of the revision we are parsing.
Definition Parser.php:6027
getContentLanguage()
Get the content language that this Parser is using.
Definition Parser.php:1256
killMarkers( $text)
Remove any strip markers found in the given text.
Definition Parser.php:6349
getRevisionUser()
Get the name of the user that edited the last revision.
Definition Parser.php:6120
getTargetLanguage()
Get the target language for the content being parsed.
Definition Parser.php:1192
getStripList()
Get a list of strippable XML-like elements.
Definition Parser.php:1352
extensionSubstitution(array $params, PPFrame $frame, bool $processNowiki=false)
Return the text to be used for a given extension tag.
Definition Parser.php:3988
getRevisionRecordObject()
Get the revision record object for $this->mRevisionId.
Definition Parser.php:6037
getRevisionTimestamp()
Get the timestamp associated with the current revision, adjusted for the default server-local timesta...
Definition Parser.php:6092
doQuotes( $text)
Helper function for handleAllQuotes()
Definition Parser.php:2003
fetchCurrentRevisionRecordOfTitle(LinkTarget $link)
Fetch the current revision of a given title as a RevisionRecord.
Definition Parser.php:3539
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:78
inNamespace(int $ns)
Returns true if the title is inside the specified namespace.
Definition Title.php:1302
equals(object $other)
Compares with another Title.
Definition Title.php:3158
getDBkey()
Get the main part with underscores.
Definition Title.php:1035
getText()
Get the text form (spaces not underscores) of the main part.
Definition Title.php:1017
internal since 1.36
Definition User.php:93
hasFragment()
Whether the link target has a fragment.