26use InvalidArgumentException;
31use MediaWiki\Languages\LanguageNameUtils;
43use Wikimedia\Bcp47Code\Bcp47CodeValue;
44use Wikimedia\RemexHtml\Tokenizer\Attributes;
45use Wikimedia\RemexHtml\Tokenizer\PlainAttributes;
53privateconst MAX_TTS = 900;
59// See documentation for the corresponding config options 72 $options->assertRequiredOptions( self::REGISTER_OPTIONS );
76 # Syntax for arguments (see Parser::setFunctionHook): 77 # "name for lookup in localized magic words array", 79 # optional Parser::SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}} 80 # instead of {{#int:...}}) 82'ns',
'nse',
'urlencode',
'lcfirst',
'ucfirst',
'lc',
'uc',
83'localurl',
'localurle',
'fullurl',
'fullurle',
'canonicalurl',
84'canonicalurle',
'formatnum',
'grammar',
'gender',
'plural',
'formal',
85'bidi',
'numberingroup',
'language',
86'padleft',
'padright',
'anchorencode',
'defaultsort',
'filepath',
87'pagesincategory',
'pagesize',
'protectionlevel',
'protectionexpiry',
88 # The following are the "parser function" forms of magic 89 # variables defined in CoreMagicVariables. The no-args form will 90 # go through the magic variable code path (and be cached); the 91 # presence of arguments will cause the parser function form to 92 # be invoked. (Note that the actual implementation will pass 93 # a Parser object as first argument, in addition to the 94 # parser function parameters.) 96 # For this group, the first parameter to the parser function is 97 # "page title", and the no-args form (and the magic variable) 98 # defaults to "current page title". 99'pagename',
'pagenamee',
100'fullpagename',
'fullpagenamee',
101'subpagename',
'subpagenamee',
102'rootpagename',
'rootpagenamee',
103'basepagename',
'basepagenamee',
104'talkpagename',
'talkpagenamee',
105'subjectpagename',
'subjectpagenamee',
106'pageid',
'revisionid',
'revisionday',
107'revisionday2',
'revisionmonth',
'revisionmonth1',
'revisionyear',
111'namespace',
'namespacee',
'namespacenumber',
'talkspace',
'talkspacee',
112'subjectspace',
'subjectspacee',
114 # More parser functions corresponding to CoreMagicVariables. 115 # For this group, the first parameter to the parser function is 116 # "raw" (uses the 'raw' format if present) and the no-args form 117 # (and the magic variable) defaults to 'not raw'. 118'numberofarticles',
'numberoffiles',
120'numberofactiveusers',
125 # These magic words already contain the hash, and the no-args form 126 # is the same as passing an empty first argument 132foreach ( $noHashFunctions as $func ) {
142if ( $allowDisplayTitle ) {
145 [ self::class,
'displaytitle' ],
149if ( $allowSlowParserFunctions ) {
152 [ self::class,
'pagesinnamespace' ],
164publicstaticfunctionintFunction( $parser, $part1 =
'', ...$params ) {
165if ( strval( $part1 ) !==
'' ) {
167 ->inLanguage( $parser->
getOptions()->getUserLangObj() );
168return [ $message->plain(),
'noparse' => false ];
170return [
'found' => false ];
181publicstaticfunctionformatDate( $parser, $date, $defaultPref =
null ) {
185 $date = trim( $date );
187 $pref = $parser->
getOptions()->getDateFormat();
189// Specify a different default date format other than the normal default 190// if the user has 'default' for their setting 191if ( $pref ==
'default' && $defaultPref ) {
192 $pref = $defaultPref;
195 $date = $df->reformat( $pref, $date, [
'match-whole' ] );
199publicstaticfunctionns( $parser, $part1 =
'' ) {
200if ( intval( $part1 ) || $part1 ==
"0" ) {
201 $index = intval( $part1 );
205if ( $index !==
false ) {
208return [
'found' => false ];
212publicstaticfunctionnse( $parser, $part1 =
'' ) {
213 $ret = self::ns( $parser, $part1 );
214if ( is_string( $ret ) ) {
232publicstaticfunctionurlencode( $parser, $s =
'', $arg =
null ) {
238switch (
$magicWords->matchStartToEnd( $arg ??
'' ) ) {
239// Encode as though it's a wiki page, '_' for ' '. 241 $func =
'wfUrlencode';
242 $s = str_replace(
' ',
'_', $s );
245// Encode for an HTTP Path, '%20' for ' '. 247 $func =
'rawurlencode';
250// Encode for HTTP query, '+' for ' '. 255// See T105242, where the choice to kill markers and various 256// other options were discussed. 260publicstaticfunctionlcfirst( $parser, $s =
'' ) {
264publicstaticfunctionucfirst( $parser, $s =
'' ) {
273publicstaticfunctionlc( $parser, $s =
'' ) {
282publicstaticfunctionuc( $parser, $s =
'' ) {
286publicstaticfunctionlocalurl( $parser, $s =
'', $arg =
null ) {
287return self::urlFunction(
'getLocalURL', $s, $arg );
290publicstaticfunctionlocalurle( $parser, $s =
'', $arg =
null ) {
291 $temp = self::urlFunction(
'getLocalURL', $s, $arg );
292if ( !is_string( $temp ) ) {
295return htmlspecialchars( $temp, ENT_COMPAT );
299publicstaticfunctionfullurl( $parser, $s =
'', $arg =
null ) {
300return self::urlFunction(
'getFullURL', $s, $arg );
303publicstaticfunctionfullurle( $parser, $s =
'', $arg =
null ) {
304 $temp = self::urlFunction(
'getFullURL', $s, $arg );
305if ( !is_string( $temp ) ) {
308return htmlspecialchars( $temp, ENT_COMPAT );
313return self::urlFunction(
'getCanonicalURL', $s, $arg );
317 $temp = self::urlFunction(
'getCanonicalURL', $s, $arg );
318if ( !is_string( $temp ) ) {
321return htmlspecialchars( $temp, ENT_COMPAT );
326 # Due to order of execution of a lot of bits, the values might be encoded 327 # before arriving here; if that's true, then the title can't be created 328 # and the variable will fail. If we can't get a decent title from the first 329 # attempt, url-decode and try for a second. 330 $title = Title::newFromText( $s ) ?? Title::newFromURL( urldecode( $s ) );
331if ( $title !==
null ) {
332 # Convert NS_MEDIA -> NS_FILE 337 $text = $title->$func( $arg );
339 $text = $title->$func();
343return [
'found' => false ];
354publicstaticfunctionformatnum( $parser, $num =
'', $arg1 =
'', $arg2 =
'' ) {
366if ( in_array(
'rawsuffix', $modifiers,
true ) ) {
367 $func = [ $targetLanguage,
'parseFormattedNumber' ];
369if ( in_array(
'nocommafysuffix', $modifiers,
true ) ) {
370 $func = [ $targetLanguage,
'formatNumNoSeparators' ];
372 $func = [ $targetLanguage,
'formatNum' ];
373 $func = self::getLegacyFormatNum( $parser, $func );
375if ( in_array(
'lossless', $modifiers,
true ) ) {
376 $potentiallyLossyFunc = $func;
377 $func =
staticfunction ( $num ) use ( $targetLanguage, $potentiallyLossyFunc ) {
378 $formatted = $potentiallyLossyFunc( $num );
379 $parsed = $targetLanguage->parseFormattedNumber( $formatted );
380if ( $num === $parsed ) {
397privatestaticfunction getLegacyFormatNum( $parser, $callback ) {
398// For historic reasons, the formatNum parser function will 399// take arguments which are not actually formatted numbers, 400// which then trigger deprecation warnings in Language::formatNum*. 401// Instead emit a tracking category instead to allow linting. 402returnstaticfunction ( $number ) use ( $parser, $callback ) {
403 $validNumberRe =
'(-(?=[\d\.]))?(\d+|(?=\.\d))(\.\d*)?([Ee][-+]?\d+)?';
405 !is_numeric( $number ) &&
406 $number !== (string)NAN &&
407 $number !== (
string)INF &&
408 $number !== (string)-INF
411// Don't split on NAN/INF in the legacy case since they are 412// likely to be found embedded inside non-numeric text. 413return preg_replace_callback(
"/{$validNumberRe}/",
staticfunction ( $m ) use ( $callback ) {
414return $callback( $m[0] );
417return $callback( $number );
427publicstaticfunctiongrammar( $parser, $case =
'', $word =
'' ) {
438publicstaticfunctiongender( $parser, $username, ...$forms ) {
439// Some shortcuts to avoid loading user data unnecessarily 440if ( count( $forms ) === 0 ) {
442 } elseif ( count( $forms ) === 1 ) {
446 $username = trim( $username );
451// allow prefix and normalize (e.g. "*foo" -> "*foo" ). 452 $title = Title::newFromText( $username,
NS_USER );
458// check parameter, or use the ParserOptions if in interface message 459 $user = User::newFromName( $username );
462 $gender = $genderCache->getGenderOf( $user, __METHOD__ );
463 } elseif ( $username ===
'' && $parser->
getOptions()->isMessage() ) {
464 $gender = $genderCache->getGenderOf( $parser->
getOptions()->getUserIdentity(), __METHOD__ );
476publicstaticfunctionplural( $parser, $text =
'', ...$forms ) {
478 settype( $text, ctype_digit( $text ) ?
'int' :
'float' );
479// @phan-suppress-next-line PhanTypeMismatchArgument Phan does not handle settype 484 $index = $parser->getTargetLanguage()->getFormalityIndex();
485return $forms[$index] ?? $forms[0];
493publicstaticfunctionbidi( $parser, $text =
'' ) {
513 [
'displaytitle_noerror',
'displaytitle_noreplace' ] );
517// parse a limited subset of wiki markup (just the single quote items) 520// remove stripped text (e.g. the UNIQ-QINU stuff) that was generated by tag extensions/whatever 523// See T28547 for rationale for this processing. 524// list of disallowed tags for DISPLAYTITLE 525// these will be escaped even though they are allowed in normal wiki text 526 $bad = [
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'div',
'blockquote',
'ol',
'ul',
'li',
'hr',
527'table',
'tr',
'th',
'td',
'dl',
'dd',
'caption',
'p',
'ruby',
'rb',
'rt',
'rtc',
'rp',
'br' ];
529// disallow some styles that could be used to bypass $wgRestrictDisplayTitle 530if ( $restrictDisplayTitle ) {
531// This code is tested with the cases marked T28547 in 533 $htmlTagsCallback =
staticfunction ( Attributes $attr ): Attributes {
534 $decoded = $attr->getValues();
536if ( isset( $decoded[
'style'] ) ) {
537// this is called later anyway, but we need it right now for the regexes below to be safe 538// calling it twice doesn't hurt 541if ( preg_match(
'/(display|user-select|visibility)\s*:/i', $decoded[
'style'] ) ) {
542 $decoded[
'style'] =
'/* attempt to bypass $wgRestrictDisplayTitle */';
546returnnew PlainAttributes( $decoded );
549 $htmlTagsCallback =
null;
552// only requested titles that normalize to the actual title are allowed through 553// if $wgRestrictDisplayTitle is true (it is by default) 554// mimic the escaping process that occurs in OutputPage::setPageTitle 556'attrCallback' => $htmlTagsCallback,
560// Decode entities in $text the same way that Title::newFromText does 563if ( !$restrictDisplayTitle ||
564 ( $title instanceof
Title 568 $old = $parser->
getOutput()->getPageProperty(
'displaytitle' );
569if ( $old ===
null || $arg !==
'displaytitle_noreplace' ) {
570 $parser->
getOutput()->setDisplayTitle( $text );
572if ( $old !==
null && $old !== $text && !$arg ) {
575return'<span class="error">' .
576 $parser->
msg(
'duplicate-displaytitle',
577// Message should be parsed, but these params should only be escaped. 587'restricted-displaytitle',
588// Message should be parsed, but this param should only be escaped. 604privatestaticfunction matchAgainstMagicword(
607 $value = trim( strval( $value ) );
611 $mwObject = $magicWordFactory->
get( $magicword );
612return $mwObject->matchStartToEnd( $value );
627if ( $raw !==
null && $raw !==
'' ) {
628if ( !$magicWordFactory ) {
631if ( self::matchAgainstMagicword( $magicWordFactory,
'rawsuffix', $raw ) ) {
635return $language->formatNum( $num );
647return self::formatRaw( SiteStats::activeUsers(), $raw, $parser->
getTargetLanguage() );
651return self::formatRaw( SiteStats::articles(), $raw, $parser->
getTargetLanguage() );
655return self::formatRaw( SiteStats::images(), $raw, $parser->
getTargetLanguage() );
659return self::formatRaw(
660 SiteStats::numberingroup(
'sysop' ),
671return self::formatRaw(
672 SiteStats::pagesInNs( intval( $namespace ) ),
679return self::formatRaw(
680 SiteStats::numberingroup( strtolower( $name ) ),
694privatestaticfunction makeTitle(
Parser $parser, ?
string $t ) {
696// For consistency with magic variable forms 699 $title = Title::newFromText( $t );
712publicstaticfunctionnamespace( $parser, $title = null ) {
713$t = self::makeTitle( $parser, $title );
717return str_replace(
'_',
' ', $t->getNsText() );
721 $t = self::makeTitle( $parser, $title );
729 $t = self::makeTitle( $parser, $title );
733return (
string)$t->getNamespace();
737 $t = self::makeTitle( $parser, $title );
738if ( $t ===
null || !$t->canHaveTalkPage() ) {
741return str_replace(
'_',
' ', $t->getTalkNsText() );
745 $t = self::makeTitle( $parser, $title );
746if ( $t ===
null || !$t->canHaveTalkPage() ) {
753 $t = self::makeTitle( $parser, $title );
757return str_replace(
'_',
' ', $t->getSubjectNsText() );
761 $t = self::makeTitle( $parser, $title );
775publicstaticfunctionpagename( $parser, $title =
null ) {
776 $t = self::makeTitle( $parser, $title );
784 $t = self::makeTitle( $parser, $title );
792 $t = self::makeTitle( $parser, $title );
800 $t = self::makeTitle( $parser, $title );
808 $t = self::makeTitle( $parser, $title );
816 $t = self::makeTitle( $parser, $title );
824 $t = self::makeTitle( $parser, $title );
832 $t = self::makeTitle( $parser, $title );
840 $t = self::makeTitle( $parser, $title );
848 $t = self::makeTitle( $parser, $title );
856 $t = self::makeTitle( $parser, $title );
857if ( $t ===
null || !$t->canHaveTalkPage() ) {
864 $t = self::makeTitle( $parser, $title );
865if ( $t ===
null || !$t->canHaveTalkPage() ) {
872 $t = self::makeTitle( $parser, $title );
880 $t = self::makeTitle( $parser, $title );
901'pagesincategory_all',
902'pagesincategory_pages',
903'pagesincategory_subcats',
904'pagesincategory_files' 909// split the given option to its variable 911// {{pagesincategory:|raw[|type]}} 915// {{pagesincategory:[|type[|raw]]}} 919if ( !$type ) {
// backward compatibility 920 $type =
'pagesincategory_all';
923 $title = Title::makeTitleSafe(
NS_CATEGORY, $name );
924if ( !$title ) { # invalid title
928 ->getLanguageConverterFactory()
930 $languageConverter->findVariantLink( $name, $title,
true );
932// Normalize name for cache 933 $name = $title->getDBkey();
935if ( !isset( $cache[$name] ) ) {
936 $category = Category::newFromTitle( $title );
938 $allCount = $subcatCount = $fileCount = $pageCount = 0;
940 $allCount = $category->getMemberCount();
941 $subcatCount = $category->getSubcatCount();
942 $fileCount = $category->getFileCount();
943 $pageCount = $category->getPageCount( Category::COUNT_CONTENT_PAGES );
945 $cache[$name][
'pagesincategory_all'] = $allCount;
946 $cache[$name][
'pagesincategory_pages'] = $pageCount;
947 $cache[$name][
'pagesincategory_subcats'] = $subcatCount;
948 $cache[$name][
'pagesincategory_files'] = $fileCount;
951 $count = $cache[$name][$type];
964publicstaticfunctionpagesize( $parser, $page =
'', $raw =
null ) {
965 $title = Title::newFromText( $page );
967if ( !is_object( $title ) || $title->isExternal() ) {
971// fetch revision from cache/database and return the value 972 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_SHA1 );
973 $length = $rev ? $rev->getSize() : 0;
974if ( $length ===
null ) {
975// We've had bugs where rev_len was not being recorded for empty pages, see T135414 994 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
997 $restrictions = $restrictionStore->getRestrictions( $titleObject, strtolower( $type ) );
998 # RestrictionStore::getRestrictions returns an array, its possible it may have 999 # multiple values in the future 1000return implode(
',', $restrictions );
1018 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1021// getRestrictionExpiry() returns null on invalid type; trying to 1022// match protectionlevel() function that returns empty string instead 1023return $restrictionStore->getRestrictionExpiry( $titleObject, strtolower( $type ) ) ??
'';
1036publicstaticfunctionlanguage( $parser, $code =
'', $inLanguage =
'' ) {
1040if ( $inLanguage ===
'' ) {
1041 $inLanguage = LanguageNameUtils::AUTONYMS;
1044 ->getLanguageNameUtils()
1045 ->getLanguageName( $code, $inLanguage );
1046return $lang !==
'' ? $lang : LanguageCode::bcp47( $code );
1060publicstaticfunctiondir(
Parser $parser,
string $code =
'',
string $arg =
'' ): string {
1071if (
$magicWords->matchStartToEnd( $arg ) ===
'language_option_bcp47' ) {
1072// Prefer the BCP-47 interpretation of this code. 1073 $code =
new Bcp47CodeValue( $code );
1077 $lang = $languageFactory->getLanguage( $code );
1078 }
catch ( InvalidArgumentException ) {
1083return $lang->getDir();
1098return LanguageCode::bcp47( $code );
1112 $parser, $string, $length, $padding =
'0', $direction = STR_PAD_RIGHT
1115 $lengthOfPadding = mb_strlen( $padding );
1116if ( $lengthOfPadding == 0 ) {
1120 # The remaining length to add counts down to 0 as padding is added 1121 $length = min( (
int)$length, 500 ) - mb_strlen( $string );
1122if ( $length <= 0 ) {
1127 # $finalPadding is just $padding repeated enough times so that 1128 # mb_strlen( $string ) + mb_strlen( $finalPadding ) == $length 1130while ( $length > 0 ) {
1131 # If $length < $lengthofPadding, truncate $padding so we get the 1132 # exact length desired. 1133 $finalPadding .= mb_substr( $padding, 0, $length );
1134 $length -= $lengthOfPadding;
1137if ( $direction == STR_PAD_LEFT ) {
1138return $finalPadding . $string;
1140return $string . $finalPadding;
1144publicstaticfunctionpadleft( $parser, $string =
'', $length = 0, $padding =
'0' ) {
1145return self::pad( $parser, $string, $length, $padding, STR_PAD_LEFT );
1148publicstaticfunctionpadright( $parser, $string =
'', $length = 0, $padding =
'0' ) {
1149return self::pad( $parser, $string, $length, $padding );
1160return Sanitizer::safeEncodeAttribute( $section );
1164 [ $page, $subpage ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
1165 resolveAlias( $text );
1167 $title = SpecialPage::getTitleFor( $page, $subpage );
1168return $title->getPrefixedText();
1170// unknown special page, just use the given text as its title, if at all possible 1171 $title = Title::makeTitleSafe(
NS_SPECIAL, $text );
1172return $title ? $title->getPrefixedText() : self::special( $parser,
'Badtitle' );
1177returnwfUrlencode( str_replace(
' ',
'_', self::special( $parser, $text ) ) );
1192 [
'defaultsort_noerror',
'defaultsort_noreplace' ] );
1196 $text = trim( $text );
1200 $old = $parser->
getOutput()->getPageProperty(
'defaultsort' );
1201if ( $old ===
null || $arg !==
'defaultsort_noreplace' ) {
1202 $parser->
getOutput()->setPageProperty(
'defaultsort', $text );
1205if ( $old ===
null || $old == $text || $arg ) {
1209return'<span class="error">' .
1210 $parser->
msg(
'duplicate-defaultsort',
1211// Message should be parsed, but these params should only be escaped. 1230publicstaticfunctionfilepath( $parser, $name =
'', $argA =
'', $argB =
'' ) {
1231 $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $name );
1233if ( $argA ==
'nowiki' ) {
1234// {{filepath: | option [| size] }} 1238// {{filepath: [| size [|option]] }} 1240 $isNowiki = ( $argB ==
'nowiki' );
1244$url = $file->getFullUrl();
1246// If a size is requested... 1247if ( count( $parsedWidthParam ) ) {
1248 $mto = $file->transform( $parsedWidthParam );
1250if ( $mto && !$mto->isError() ) {
1251// ... change the URL to point to a thumbnail. 1252 $urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
1257return [
$url,
'nowiki' => true ];
1272publicstaticfunctiontagObj( $parser, $frame, $args ) {
1273if ( !count( $args ) ) {
1276 $tagName = strtolower( trim( $parser->
killMarkers(
1277 $frame->expand( array_shift( $args ) )
1281if ( count( $args ) ) {
1282// With Parsoid Fragment support, $processNoWiki flag 1283// isn't actually required as a ::expand() flag, but it 1284// doesn't do any harm. 1285 $inner = $frame->expand( array_shift( $args ), $processNowiki );
1286if ( $processNowiki ) {
1287// This is the T299103 workaround for <syntaxhighlight>, 1288// and reproduces the code in SyntaxHighlight::parserHook. 1289// The Parsoid extension API (SyntaxHighlight::sourceToDom) 1290// doesn't (yet) know about strip state, and so can't do 1299foreach ( $args as $arg ) {
1300 $bits = $arg->splitArg();
1301if ( strval( $bits[
'index'] ) ===
'' ) {
1302 $name = trim( $frame->expand( $bits[
'name'], PPFrame::STRIP_COMMENTS ) );
1303 $value = trim( $frame->expand( $bits[
'value'] ) );
1304if ( preg_match(
'/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m ) ) {
1305 $value = $m[1] ??
'';
1307 $attributes[$name] = $value;
1312if ( !in_array( $tagName, $stripList ) ) {
1313// we can't handle this tag (at least not now), so just re-emit it as an ordinary tag 1315foreach ( $attributes as $name => $value ) {
1316 $attrText .=
' ' . htmlspecialchars( $name ) .
1317'="' . htmlspecialchars( $parser->
killMarkers( $value ), ENT_COMPAT ) .
'"';
1319if ( $inner ===
null ) {
1320return"<$tagName$attrText/>";
1322return"<$tagName$attrText>$inner</$tagName>";
1328'attributes' => $attributes,
1329'close' =>
"</$tagName>",
1347privatestaticfunction getCachedRevisionObject( $parser, $title, $vary ) {
1352 $revisionRecord =
null;
1354 $isSelfReferential = $title->equals( $parser->
getTitle() );
1355if ( $isSelfReferential ) {
1356// Revision is for the same title that is currently being parsed. Only use the last 1357// saved revision, regardless of Parser::getRevisionId() or fake revision injection 1358// callbacks against the current title. 1360// FIXME (T318278): the above is the intention, but doesn't 1361// describe the actual current behavior of this code, since 1362// ->isCurrent() for the last saved revision will return 1363// false so we're going to fall through and end up calling 1364// ->getCurrentRevisionRecordOfTitle(). 1366if ( $parserRevisionRecord && $parserRevisionRecord->isCurrent() ) {
1367 $revisionRecord = $parserRevisionRecord;
1372if ( !$revisionRecord ) {
1377returnnull;
// not allowed 1379// Get the current revision, ignoring Parser::getRevisionId() being null/old 1381if ( !$revisionRecord ) {
1382// Convert `false` error return to `null` 1383 $revisionRecord =
null;
1385// Register dependency in templatelinks 1386 $parserOutput->addTemplate(
1388 $revisionRecord ? $revisionRecord->getPageId() : 0,
1389 $revisionRecord ? $revisionRecord->getId() : 0
1393if ( $isSelfReferential ) {
1394wfDebug( __METHOD__ .
": used current revision, setting {$vary->value}" );
1395// Upon page save, the result of the parser function using this might change 1396 $parserOutput->setOutputFlag( $vary );
1397if ( $vary === ParserOutputFlags::VARY_REVISION_SHA1 && $revisionRecord ) {
1399 $sha1 = $revisionRecord->getSha1();
1400 }
catch ( RevisionAccessException ) {
1403 $parserOutput->setRevisionUsedSha1Base36( $sha1 );
1407return $revisionRecord;
1417publicstaticfunctionpageid( $parser, $title =
null ) {
1418 $t = self::makeTitle( $parser, $title );
1421 } elseif ( !$t->canExist() || $t->isExternal() ) {
1422return 0;
// e.g. special page or interwiki link 1427if ( $t->equals( $parser->
getTitle() ) ) {
1428// Revision is for the same title that is currently being parsed. 1429// Use the title from Parser in case a new page ID was injected into it. 1430 $parserOutput->setOutputFlag( ParserOutputFlags::VARY_PAGE_ID );
1431 $id = $parser->
getTitle()->getArticleID();
1433 $parserOutput->setSpeculativePageIdUsed( $id );
1439// Check the link cache for the title 1440 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
1441 $pdbk = $t->getPrefixedDBkey();
1442 $id = $linkCache->getGoodLinkID( $pdbk );
1443if ( $id != 0 || $linkCache->isBadLink( $pdbk ) ) {
1444 $parserOutput->addLink( $t, $id );
1449// We need to load it from the DB, so mark expensive 1451 $id = $t->getArticleID();
1452 $parserOutput->addLink( $t, $id );
1468 $t = self::makeTitle( $parser, $title );
1469if ( $t ===
null || $t->isExternal() ) {
1473 $services = MediaWikiServices::getInstance();
1475 $t->equals( $parser->
getTitle() ) &&
1476 $services->getMainConfig()->get( MainConfigNames::MiserMode ) &&
1477 !$parser->
getOptions()->getInterfaceMessage() &&
1478// @TODO: disallow this word on all namespaces (T235957) 1479 $services->getNamespaceInfo()->isSubject( $t->getNamespace() )
1481// Use a stub result instead of the actual revision ID in order to avoid 1482// double parses on page save but still allow preview detection (T137900) 1486 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS );
1490// Fetch revision from cache/database and return the value. 1491// Inform the edit saving system that getting the canonical output 1492// after revision insertion requires a parse that used that exact 1494if ( $t->equals( $parser->
getTitle() ) && $title ===
null ) {
1495// special handling for no-arg case: use speculative rev id 1497 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_ID );
1502 $id = $rev->getId();
1506 $id = $parser->
getOptions()->getSpeculativeRevId();
1508 $parser->
getOutput()->setSpeculativeRevIdUsed( $id );
1513 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_REVISION_ID );
1514return $rev ? $rev->getId() :
'';
1517privatestaticfunction getRevisionTimestampSubstring(
1524// If fetching the revision timestamp of the current page, substitute the 1525// speculative timestamp to be used when this revision is saved. This 1526// avoids having to invalidate the cache immediately by assuming the "last 1527// saved revision" will in fact be this one. 1528// Don't do this for interface messages (eg, edit notices) however; in that 1529// case fall through and use the actual timestamp of the last saved revision. 1530if ( $title->equals( $parser->
getTitle() ) && !$parser->getOptions()->getInterfaceMessage() ) {
1531// Get the timezone-adjusted timestamp to be used for this revision 1533// Possibly set vary-revision if there is not yet an associated revision 1535// Get the timezone-adjusted timestamp $mtts seconds in the future. 1536// This future is relative to the current time and not that of the 1537// parser options. The rendered timestamp can be compared to that 1538// of the timestamp specified by the parser options. 1545if ( $resNow !== $resThen ) {
1546// Inform the edit saving system that getting the canonical output after 1547// revision insertion requires a parse that used an actual revision timestamp 1548 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1554 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1573 $t = self::makeTitle( $parser, $title );
1574if ( $t ===
null || $t->isExternal() ) {
1577return strval( (
int)self::getRevisionTimestampSubstring(
1578 $parser, $t, 6, 2, self::MAX_TTS
1590 $t = self::makeTitle( $parser, $title );
1591if ( $t ===
null || $t->isExternal() ) {
1594return self::getRevisionTimestampSubstring(
1595 $parser, $t, 6, 2, self::MAX_TTS
1607 $t = self::makeTitle( $parser, $title );
1608if ( $t ===
null || $t->isExternal() ) {
1611return self::getRevisionTimestampSubstring(
1612 $parser, $t, 4, 2, self::MAX_TTS
1624 $t = self::makeTitle( $parser, $title );
1625if ( $t ===
null || $t->isExternal() ) {
1628return strval( (
int)self::getRevisionTimestampSubstring(
1629 $parser, $t, 4, 2, self::MAX_TTS
1641 $t = self::makeTitle( $parser, $title );
1642if ( $t ===
null || $t->isExternal() ) {
1645return self::getRevisionTimestampSubstring(
1646 $parser, $t, 0, 4, self::MAX_TTS
1658 $t = self::makeTitle( $parser, $title );
1659if ( $t ===
null || $t->isExternal() ) {
1662return self::getRevisionTimestampSubstring(
1663 $parser, $t, 0, 14, self::MAX_TTS
1675 $t = self::makeTitle( $parser, $title );
1676if ( $t ===
null || $t->isExternal() ) {
1679// VARY_USER informs the edit saving system that getting the canonical 1680// output after revision insertion requires a parse that used the 1682if ( $t->equals( $parser->
getTitle() ) ) {
1683// Fall back to Parser's "revision user" for the current title 1684 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_USER );
1685// Note that getRevisionUser() can return null; we need to 1686// be sure to cast this to (an empty) string, since returning 1687// null means "magic variable not handled". 1690// Fetch revision from cache/database and return the value. 1691 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_USER );
1692 $user = ( $rev !== null ) ? $rev->getUser() :
null;
1693return $user ? $user->getName() :
'';
1709 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1710 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
1711if ( $restrictionStore->areCascadeProtectionSourcesLoaded( $titleObject )
1715 $sources = $restrictionStore->getCascadeProtectionSources( $titleObject );
1716 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
1717foreach ( $sources[0] as $sourcePageIdentity ) {
1718 $names[] = $titleFormatter->getPrefixedText( $sourcePageIdentity );
1720return implode(
'|', $names );
1725publicstaticfunctioninterwikilink( $parser, $prefix =
'', $title =
'', $linkText =
null ) {
1726 $services = MediaWikiServices::getInstance();
1729 $services->getInterwikiLookup()->isValidInterwiki( $prefix )
1731if ( $linkText !==
null ) {
1732 $linkText = Parser::stripOuterParagraph(
1733 # FIXME T382287: when
usingParsoid
this may leave
1734 # strip markers behind for embedded extension tags. 1738 [ $title, $frag ] = array_pad( explode(
'#', $title, 2 ), 2,
'' );
1740 $parser->
getOutput()->addInterwikiLink( $target );
1742'text' => Linker::link( $target, $linkText ),
1746// Invalid interwiki link, render as plain text 1747return [
'found' => false ];
1751 $services = MediaWikiServices::getInstance();
1752 $extraInterlanguageLinkPrefixes = $services->getMainConfig()->get(
1753 MainConfigNames::ExtraInterlanguageLinkPrefixes
1757 $services->getInterwikiLookup()->isValidInterwiki( $prefix ) &&
1759 $services->getLanguageNameUtils()->getLanguageName(
1760 $prefix, LanguageNameUtils::AUTONYMS, LanguageNameUtils::DEFINED
1761 ) || in_array( $prefix, $extraInterlanguageLinkPrefixes,
true )
1764// $linkText is ignored for language links, but fragment is kept 1765 [ $title, $frag ] = array_pad( explode(
'#', $title, 2 ), 2,
'' );
1768NS_MAIN, $title, $frag, $prefix
1773// Invalid language link, render as plain text 1774return [
'found' => false ];
1779class_alias( CoreParserFunctions::class,
'CoreParserFunctions' );
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,...
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
if(!defined('MW_SETUP_CALLBACK'))
Category objects are immutable, strictly speaking.
A class for passing options to services.
Methods for dealing with language codes.
Base class for language-specific code.
Some internal bits split of from Skin.php.
A class containing constants representing the names of configuration variables.
const AllowSlowParserFunctions
Name constant for the AllowSlowParserFunctions setting, for use with Config::get()
const AllowDisplayTitle
Name constant for the AllowDisplayTitle setting, for use with Config::get()
const RestrictDisplayTitle
Name constant for the RestrictDisplayTitle setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
The Message class deals with fetching and processing of interface message into a variety of formats.
static plaintextParam( $plaintext)
Various core parser functions, registered in every Parser.
static pagesize( $parser, $page='', $raw=null)
Return the size of the given page, or 0 if it's nonexistent.
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 lc( $parser, $s='')
static speciale( $parser, $text)
static language( $parser, $code='', $inLanguage='')
Gives language names.
static bcp47(Parser $parser, string $code='')
Gives the BCP-47 code for a language given the mediawiki internal language code.
static numberofedits( $parser, $raw=null)
static bidi( $parser, $text='')
static namespacenumber( $parser, $title=null)
static revisiontimestamp( $parser, $title=null)
Get the timestamp from the last revision of a specified page.
static defaultsort( $parser, $text, $uarg='')
static basepagename( $parser, $title=null)
static formal(Parser $parser, string ... $forms)
static namespacee( $parser, $title=null)
static numberofpages( $parser, $raw=null)
static dir(Parser $parser, string $code='', string $arg='')
Gives direction of script of a language given a language code.
static pagesinnamespace( $parser, $namespace=0, $raw=null)
static numberingroup( $parser, $name='', $raw=null)
static interlanguagelink( $parser, $prefix='', $title='', $linkText=null)
static fullurle( $parser, $s='', $arg=null)
static pageid( $parser, $title=null)
Get the pageid of a specified page.
static intFunction( $parser, $part1='',... $params)
static tagObj( $parser, $frame, $args)
Parser function to extension tag adaptor.
static numberofusers( $parser, $raw=null)
static formatDate( $parser, $date, $defaultPref=null)
static talkspacee( $parser, $title=null)
static subjectpagenamee( $parser, $title=null)
static rootpagename( $parser, $title=null)
static cascadingsources( $parser, $title='')
Returns the sources of any cascading protection acting on a specified page.
static fullpagename( $parser, $title=null)
static formatRaw( $num, $raw, $language, ?MagicWordFactory $magicWordFactory=null)
Formats a number according to a language.
static plural( $parser, $text='',... $forms)
static subpagenamee( $parser, $title=null)
static fullpagenamee( $parser, $title=null)
static padright( $parser, $string='', $length=0, $padding='0')
static urlFunction( $func, $s='', $arg=null)
static talkpagenamee( $parser, $title=null)
static canonicalurl( $parser, $s='', $arg=null)
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 revisionday2( $parser, $title=null)
Get the day with leading zeros from the last revision of a specified page.
static protectionexpiry( $parser, $type='', $title='')
Returns the requested protection expiry for the current page.
static ucfirst( $parser, $s='')
static uc( $parser, $s='')
static talkspace( $parser, $title=null)
static anchorencode( $parser, $text)
static numberofactiveusers( $parser, $raw=null)
static subjectspacee( $parser, $title=null)
static pagenamee( $parser, $title=null)
static grammar( $parser, $case='', $word='')
static revisionuser( $parser, $title=null)
Get the user from the last revision of a specified page.
static padleft( $parser, $string='', $length=0, $padding='0')
static localurl( $parser, $s='', $arg=null)
static urlencode( $parser, $s='', $arg=null)
urlencodes a string according to one of three patterns: (T24474)
static localurle( $parser, $s='', $arg=null)
static nse( $parser, $part1='')
static formatnum( $parser, $num='', $arg1='', $arg2='')
static rootpagenamee( $parser, $title=null)
static protectionlevel( $parser, $type='', $title='')
Returns the requested protection level for the current page.
static subpagename( $parser, $title=null)
static subjectpagename( $parser, $title=null)
static interwikilink( $parser, $prefix='', $title='', $linkText=null)
static numberofadmins( $parser, $raw=null)
static revisionday( $parser, $title=null)
Get the day from the last revision of a specified page.
static canonicalurle( $parser, $s='', $arg=null)
static subjectspace( $parser, $title=null)
static ns( $parser, $part1='')
static revisionmonth( $parser, $title=null)
Get the month with leading zeros from the last revision of a specified page.
static basepagenamee( $parser, $title=null)
static gender( $parser, $username,... $forms)
static revisionyear( $parser, $title=null)
Get the year from the last revision of a specified page.
static pagename( $parser, $title=null)
Functions to get and normalize pagenames, corresponding to the magic words of the same names.
static fullurl( $parser, $s='', $arg=null)
static numberofarticles( $parser, $raw=null)
static special( $parser, $text)
static lcfirst( $parser, $s='')
static revisionmonth1( $parser, $title=null)
Get the month from the last revision of a specified page.
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 talkpagename( $parser, $title=null)
static revisionid( $parser, $title=null)
Get the id from the last revision of a specified page.
static numberoffiles( $parser, $raw=null)
static filepath( $parser, $name='', $argA='', $argB='')
Usage {{filepath|300}}, {{filepath|nowiki}}, {{filepath|nowiki|300}} or {{filepath|300|nowiki}} or {{...
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...
getTargetLanguageConverter()
Shorthand for getting a Language Converter for Target language.
addTrackingCategory( $msg)
markerSkipCallback( $s, callable $callback)
Call a callback function on all regions of the given text that are not inside strip markers,...
tagNeedsNowikiStrippedInTagPF(string $lowerTagName)
getMagicWordFactory()
Get the MagicWordFactory that this Parser is using.
setFunctionHook( $id, callable $callback, $flags=0)
Create a function, e.g.
guessSectionNameFromWikiText( $text)
Try to guess the section anchor name based on a wikitext fragment presumably extracted from a heading...
incrementExpensiveFunctionCount()
isCurrentRevisionOfTitleCached(LinkTarget $link)
getRevisionId()
Get the ID of the revision we are parsing.
getContentLanguage()
Get the content language that this Parser is using.
parseWidthParam( $value, $parseHeight=true, bool $localized=false)
Parsed a width param of imagelink like 300px or 200x300px.
killMarkers( $text)
Remove any strip markers found in the given text.
getRevisionUser()
Get the name of the user that edited the last revision.
getTargetLanguage()
Get the target language for the content being parsed.
msg(string $msg,... $params)
Helper function to correctly set the target language and title of a message based on the parser conte...
getStripList()
Get a list of strippable XML-like elements.
extensionSubstitution(array $params, PPFrame $frame, bool $processNowiki=false)
Return the text to be used for a given extension tag.
getRevisionRecordObject()
Get the revision record object for $this->mRevisionId.
getRevisionTimestamp()
Get the timestamp associated with the current revision, adjusted for the default server-local timesta...
doQuotes( $text)
Helper function for handleAllQuotes()
recursiveTagParseFully( $text, $frame=false)
Fully parse wikitext to fully parsed HTML.
fetchCurrentRevisionRecordOfTitle(LinkTarget $link)
Fetch the current revision of a given title as a RevisionRecord.
static stripAllTags(string $html)
Take a fragment of (potentially invalid) HTML and return a version with any tags removed,...
static checkCss( $value)
Pick apart some CSS and check it for forbidden or unsafe structures.
static decodeCharReferencesAndNormalize(string $text)
Decode any character references, numeric or named entities, in the next and normalize the resulting s...
static removeSomeTags(string $text, array $options=[])
Cleans up HTML, removes dangerous tags and attributes, and removes HTML comments; the result will alw...
Exception representing a failure to look up a revision.
Page revision base class.
Static accessor class for site_stats and related things.
Parent class for all special pages.
Represents the target of a wiki link.
Represents a title within MediaWiki.
inNamespace(int $ns)
Returns true if the title is inside the specified namespace.
equals(object $other)
Compares with another Title.
getDBkey()
Get the main part with underscores.
getText()
Get the text form (spaces not underscores) of the main part.
getDefaultOption(string $opt, ?UserIdentity $userIdentity=null)
Get a given default option value.
User class for the MediaWiki software.
hasFragment()
Whether the link target has a fragment.