81 # Flags for Parser::setFunctionHook
85 # Constants needed for external link processing
86 # Everything except bracket, space, or control characters
87 # \p{Zs} is unicode 'separator, space' category. It covers the space 0x20
88 # as well as U+3000 is IDEOGRAPHIC SPACE for bug 19052
90 # Simplified expression to match an IPv4 or IPv6 address, or
91 # at least one character of a host name (embeds EXT_LINK_URL_CLASS)
92 const EXT_LINK_ADDR =
'(?:[0-9.]+|\\[(?i:[0-9a-f:.]+)\\]|[^][<>"\\x00-\\x20\\x7F\p{Zs}])';
93 # RegExp to make image URLs (embeds IPv6 part of EXT_LINK_ADDR)
95 const EXT_IMAGE_REGEX =
'/^(http:\/\/|https:\/\/)((?:\\[(?i:[0-9a-f:.]+)\\])?[^][<>"\\x00-\\x20\\x7F\p{Zs}]+)
96 \\/([A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]+)\\.((?i)gif|png|jpg|jpeg)$/Sxu';
99 # Regular expression for a non-newline space
102 # State constants for the definition list colon extraction
112 # Flags for preprocessToDom
115 # Allowed values for $this->mOutputType
116 # Parameter to startExternalParse().
121 const
OT_PLAIN = 4;
# like extractSections() - portions of the original are returned unchanged.
140 const MARKER_SUFFIX =
"-QINU`\"'\x7f";
143 # Markers used for wrapping the table of contents
161 # Initialised by initialiseVariables()
172 # Initialised in constructor
175 # Initialized in getPreprocessor()
179 # Cleared with clearState():
210 # These are variables reset at least once per parse regardless of $clearState
223 public $mRevisionObject;
# The revision object of the specified revision ID
256 public $mInParse =
false;
265 $this->mConf = $conf;
267 $this->mExtLinkBracketedRegex =
'/\[(((?i)' . $this->mUrlProtocols .
')' .
268 self::EXT_LINK_ADDR .
269 self::EXT_LINK_URL_CLASS .
'*)\p{Zs}*([^\]\\x00-\\x08\\x0a-\\x1F]*?)\]/Su';
270 if ( isset( $conf[
'preprocessorClass'] ) ) {
271 $this->mPreprocessorClass = $conf[
'preprocessorClass'];
272 } elseif ( defined(
'HPHP_VERSION' ) ) {
273 # Preprocessor_Hash is much faster than Preprocessor_DOM under HipHop
274 $this->mPreprocessorClass =
'Preprocessor_Hash';
275 } elseif ( extension_loaded(
'domxml' ) ) {
276 # PECL extension that conflicts with the core DOM extension (bug 13770)
277 wfDebug(
"Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
278 $this->mPreprocessorClass =
'Preprocessor_Hash';
279 } elseif ( extension_loaded(
'dom' ) ) {
280 $this->mPreprocessorClass =
'Preprocessor_DOM';
282 $this->mPreprocessorClass =
'Preprocessor_Hash';
284 wfDebug( __CLASS__ .
": using preprocessor: {$this->mPreprocessorClass}\n" );
291 if ( isset( $this->mLinkHolders ) ) {
292 unset( $this->mLinkHolders );
295 unset( $this->
$name );
303 $this->mInParse =
false;
311 foreach ( [
'mStripState',
'mVarCache' ]
as $k ) {
326 if ( !$this->mFirstCall ) {
329 $this->mFirstCall =
false;
335 Hooks::run(
'ParserFirstCallInit', [ &$this ] );
344 if ( $this->mFirstCall ) {
348 $this->mOptions->registerWatcher( [ $this->mOutput,
'recordOption' ] );
349 $this->mAutonumber = 0;
350 $this->mLastSection =
'';
351 $this->mDTopen =
false;
352 $this->mIncludeCount = [];
353 $this->mArgStack =
false;
354 $this->mInPre =
false;
357 $this->mRevisionObject = $this->mRevisionTimestamp =
358 $this->mRevisionId = $this->mRevisionUser = $this->mRevisionSize = null;
359 $this->mVarCache = [];
361 $this->mLangLinkLanguages = [];
362 $this->currentRevisionCache = null;
366 # Clear these on every parse, bug 4549
367 $this->mTplRedirCache = $this->mTplDomCache = [];
369 $this->mShowToc =
true;
370 $this->mForceTocPosition =
false;
371 $this->mIncludeSizes = [
375 $this->mPPNodeCount = 0;
376 $this->mGeneratedPPNodeCount = 0;
377 $this->mHighestExpansionDepth = 0;
378 $this->mDefaultSort =
false;
379 $this->mHeadings = [];
380 $this->mDoubleUnderscores = [];
381 $this->mExpensiveFunctionCount = 0;
384 if ( isset( $this->mPreprocessor ) && $this->mPreprocessor->parser !== $this ) {
385 $this->mPreprocessor = null;
406 $linestart =
true, $clearState =
true, $revid = null
418 $text = strtr( $text,
"\x7f",
"?" );
419 $magicScopeVariable = $this->
lock();
424 $this->currentRevisionCache = null;
425 $this->mInputSize = strlen( $text );
426 if ( $this->mOptions->getEnableLimitReport() ) {
427 $this->mOutput->resetParseStartTime();
435 if ( $revid !== null ) {
436 $this->mRevisionId = $revid;
437 $this->mRevisionObject = null;
438 $this->mRevisionTimestamp = null;
439 $this->mRevisionUser = null;
440 $this->mRevisionSize = null;
443 Hooks::run(
'ParserBeforeStrip', [ &$this, &$text, &$this->mStripState ] );
445 Hooks::run(
'ParserAfterStrip', [ &$this, &$text, &$this->mStripState ] );
447 Hooks::run(
'ParserAfterParse', [ &$this, &$text, &$this->mStripState ] );
459 || isset( $this->mDoubleUnderscores[
'nocontentconvert'] )
460 || isset( $this->mDoubleUnderscores[
'notitleconvert'] )
461 || $this->mOutput->getDisplayTitle() !==
false )
464 if ( $convruletitle ) {
465 $this->mOutput->setTitleText( $convruletitle );
468 $this->mOutput->setTitleText( $titleText );
472 if ( $this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit() ) {
474 $this->mExpensiveFunctionCount,
475 $this->mOptions->getExpensiveParserFunctionLimit()
479 # Information on include size limits, for the benefit of users who try to skirt them
480 if ( $this->mOptions->getEnableLimitReport() ) {
481 $max = $this->mOptions->getMaxIncludeSize();
483 $cpuTime = $this->mOutput->getTimeSinceStart(
'cpu' );
484 if ( $cpuTime !== null ) {
485 $this->mOutput->setLimitReportData(
'limitreport-cputime',
486 sprintf(
"%.3f", $cpuTime )
490 $wallTime = $this->mOutput->getTimeSinceStart(
'wall' );
491 $this->mOutput->setLimitReportData(
'limitreport-walltime',
492 sprintf(
"%.3f", $wallTime )
495 $this->mOutput->setLimitReportData(
'limitreport-ppvisitednodes',
496 [ $this->mPPNodeCount, $this->mOptions->getMaxPPNodeCount() ]
498 $this->mOutput->setLimitReportData(
'limitreport-ppgeneratednodes',
499 [ $this->mGeneratedPPNodeCount, $this->mOptions->getMaxGeneratedPPNodeCount() ]
501 $this->mOutput->setLimitReportData(
'limitreport-postexpandincludesize',
502 [ $this->mIncludeSizes[
'post-expand'], $max ]
504 $this->mOutput->setLimitReportData(
'limitreport-templateargumentsize',
505 [ $this->mIncludeSizes[
'arg'], $max ]
507 $this->mOutput->setLimitReportData(
'limitreport-expansiondepth',
508 [ $this->mHighestExpansionDepth, $this->mOptions->getMaxPPExpandDepth() ]
510 $this->mOutput->setLimitReportData(
'limitreport-expensivefunctioncount',
511 [ $this->mExpensiveFunctionCount, $this->mOptions->getExpensiveParserFunctionLimit() ]
513 Hooks::run(
'ParserLimitReportPrepare', [ $this, $this->mOutput ] );
515 $limitReport =
"NewPP limit report\n";
516 if ( $wgShowHostnames ) {
517 $limitReport .=
'Parsed by ' .
wfHostname() .
"\n";
519 $limitReport .=
'Cached time: ' . $this->mOutput->getCacheTime() .
"\n";
520 $limitReport .=
'Cache expiry: ' . $this->mOutput->getCacheExpiry() .
"\n";
521 $limitReport .=
'Dynamic content: ' .
522 ( $this->mOutput->hasDynamicContent() ?
'true' :
'false' ) .
525 foreach ( $this->mOutput->getLimitReportData()
as $key =>
$value ) {
527 [ $key, &
$value, &$limitReport,
false,
false ]
529 $keyMsg =
wfMessage( $key )->inLanguage(
'en' )->useDatabase(
false );
530 $valueMsg =
wfMessage( [
"$key-value-text",
"$key-value" ] )
531 ->inLanguage(
'en' )->useDatabase(
false );
532 if ( !$valueMsg->exists() ) {
535 if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
536 $valueMsg->params(
$value );
537 $limitReport .=
"{$keyMsg->text()}: {$valueMsg->text()}\n";
543 $limitReport = htmlspecialchars_decode( $limitReport );
544 Hooks::run(
'ParserLimitReport', [ $this, &$limitReport ] );
548 $limitReport = str_replace( [
'-',
'&' ], [
'‐',
'&' ], $limitReport );
549 $text .=
"\n<!-- \n$limitReport-->\n";
552 $dataByFunc = $this->mProfiler->getFunctionStats();
553 uasort( $dataByFunc,
function ( $a, $b ) {
554 return $a[
'real'] < $b[
'real'];
556 $profileReport =
"Transclusion expansion time report (%,ms,calls,template)\n";
557 foreach ( array_slice( $dataByFunc, 0, 10 )
as $item ) {
558 $profileReport .= sprintf(
"%6.2f%% %8.3f %6d - %s\n",
559 $item[
'%real'], $item[
'real'], $item[
'calls'],
560 htmlspecialchars( $item[
'name'] ) );
562 $text .=
"\n<!-- \n$profileReport-->\n";
564 if ( $this->mGeneratedPPNodeCount > $this->mOptions->getMaxGeneratedPPNodeCount() / 10 ) {
565 wfDebugLog(
'generated-pp-node-count', $this->mGeneratedPPNodeCount .
' ' .
566 $this->mTitle->getPrefixedDBkey() );
569 $this->mOutput->setText( $text );
571 $this->mRevisionId = $oldRevisionId;
572 $this->mRevisionObject = $oldRevisionObject;
573 $this->mRevisionTimestamp = $oldRevisionTimestamp;
574 $this->mRevisionUser = $oldRevisionUser;
575 $this->mRevisionSize = $oldRevisionSize;
576 $this->mInputSize =
false;
577 $this->currentRevisionCache = null;
605 Hooks::run(
'ParserBeforeStrip', [ &$this, &$text, &$this->mStripState ] );
606 Hooks::run(
'ParserAfterStrip', [ &$this, &$text, &$this->mStripState ] );
648 $magicScopeVariable = $this->
lock();
650 if ( $revid !== null ) {
651 $this->mRevisionId = $revid;
653 Hooks::run(
'ParserBeforeStrip', [ &$this, &$text, &$this->mStripState ] );
654 Hooks::run(
'ParserAfterStrip', [ &$this, &$text, &$this->mStripState ] );
656 $text = $this->mStripState->unstripBoth( $text );
671 $text = $this->mStripState->unstripBoth( $text );
690 $text = $msg->params(
$params )->plain();
692 # Parser (re)initialisation
693 $magicScopeVariable = $this->
lock();
699 $text = $this->mStripState->unstripBoth( $text );
721 $this->mUser =
$user;
732 return self::MARKER_PREFIX;
745 if (
$t->hasFragment() ) {
746 # Strip the fragment to avoid various odd effects
747 $this->mTitle =
$t->createFragmentTarget(
'' );
768 public function Title( $x = null ) {
769 return wfSetVar( $this->mTitle, $x );
778 $this->mOutputType =
$ot;
795 return wfSetVar( $this->mOutputType, $x );
823 return wfSetVar( $this->mOptions, $x );
830 return $this->mLinkID++;
837 $this->mLinkID = $id;
858 $target = $this->mOptions->getTargetLanguage();
860 if ( $target !== null ) {
862 } elseif ( $this->mOptions->getInterfaceMessage() ) {
863 return $this->mOptions->getUserLangObj();
864 } elseif ( is_null( $this->mTitle ) ) {
865 throw new MWException( __METHOD__ .
': $this->mTitle is null' );
868 return $this->mTitle->getPageLanguage();
886 if ( !is_null( $this->mUser ) ) {
889 return $this->mOptions->getUser();
898 if ( !isset( $this->mPreprocessor ) ) {
899 $class = $this->mPreprocessorClass;
900 $this->mPreprocessor =
new $class( $this );
927 if ( $uniq_prefix !== null ) {
928 wfDeprecated( __METHOD__ .
' called with $prefix argument',
'1.26' );
934 $taglist = implode(
'|', $elements );
935 $start =
"/<($taglist)(\\s+[^>]*?|\\s*?)(\/?" .
">)|<(!--)/i";
937 while ( $text !=
'' ) {
938 $p = preg_split( $start, $text, 2, PREG_SPLIT_DELIM_CAPTURE );
940 if ( count( $p ) < 5 ) {
943 if ( count( $p ) > 5 ) {
957 $marker = self::MARKER_PREFIX .
"-$element-" . sprintf(
'%08X', $n++ ) . self::MARKER_SUFFIX;
958 $stripped .= $marker;
960 if ( $close ===
'/>' ) {
961 # Empty element tag, <tag />
966 if ( $element ===
'!--' ) {
969 $end =
"/(<\\/$element\\s*>)/i";
971 $q = preg_split( $end, $inside, 2, PREG_SPLIT_DELIM_CAPTURE );
973 if ( count( $q ) < 3 ) {
974 # No end tag -- let it run out to the end of the text.
986 "<$element$attributes$close$content$tail" ];
1010 $marker = self::MARKER_PREFIX .
"-item-{$this->mMarkerIndex}-" . self::MARKER_SUFFIX;
1011 $this->mMarkerIndex++;
1012 $this->mStripState->addGeneral( $marker, $text );
1027 $td_history = []; # Is currently a td tag
open?
1028 $last_tag_history = []; # Save
history of last lag activated (td, th
or caption)
1029 $tr_history = []; # Is currently a tr tag
open?
1031 $has_opened_tr = []; # Did
this table open a <tr> element?
1032 $indent_level = 0; # indent level
of the table
1035 $line = trim( $outLine );
1037 if (
$line ===
'' ) { # empty
line, go to next line
1038 $out .= $outLine .
"\n";
1042 $first_character =
$line[0];
1043 $first_two = substr(
$line, 0, 2 );
1046 if ( preg_match(
'/^(:*)\s*\{\|(.*)$/',
$line,
$matches ) ) {
1047 # First check if we are starting a new table
1048 $indent_level = strlen(
$matches[1] );
1050 $attributes = $this->mStripState->unstripBoth(
$matches[2] );
1053 $outLine = str_repeat(
'<dl><dd>', $indent_level ) .
"<table{$attributes}>";
1054 array_push( $td_history,
false );
1055 array_push( $last_tag_history,
'' );
1056 array_push( $tr_history,
false );
1057 array_push( $tr_attributes,
'' );
1058 array_push( $has_opened_tr,
false );
1059 } elseif ( count( $td_history ) == 0 ) {
1060 # Don't do any of the following
1061 $out .= $outLine .
"\n";
1063 } elseif ( $first_two ===
'|}' ) {
1064 # We are ending a table
1066 $last_tag = array_pop( $last_tag_history );
1068 if ( !array_pop( $has_opened_tr ) ) {
1069 $line =
"<tr><td></td></tr>{$line}";
1072 if ( array_pop( $tr_history ) ) {
1073 $line =
"</tr>{$line}";
1076 if ( array_pop( $td_history ) ) {
1077 $line =
"</{$last_tag}>{$line}";
1079 array_pop( $tr_attributes );
1080 $outLine =
$line . str_repeat(
'</dd></dl>', $indent_level );
1081 } elseif ( $first_two ===
'|-' ) {
1082 # Now we have a table row
1083 $line = preg_replace(
'#^\|-+#',
'',
$line );
1085 # Whats after the tag is now only attributes
1086 $attributes = $this->mStripState->unstripBoth(
$line );
1088 array_pop( $tr_attributes );
1089 array_push( $tr_attributes, $attributes );
1092 $last_tag = array_pop( $last_tag_history );
1093 array_pop( $has_opened_tr );
1094 array_push( $has_opened_tr,
true );
1096 if ( array_pop( $tr_history ) ) {
1100 if ( array_pop( $td_history ) ) {
1101 $line =
"</{$last_tag}>{$line}";
1105 array_push( $tr_history,
false );
1106 array_push( $td_history,
false );
1107 array_push( $last_tag_history,
'' );
1108 } elseif ( $first_character ===
'|'
1109 || $first_character ===
'!'
1110 || $first_two ===
'|+'
1112 # This might be cell elements, td, th or captions
1113 if ( $first_two ===
'|+' ) {
1114 $first_character =
'+';
1121 if ( $first_character ===
'!' ) {
1125 # Split up multiple cells on the same line.
1126 # FIXME : This can result in improper nesting of tags processed
1127 # by earlier parser steps.
1128 $cells = explode(
'||',
$line );
1132 # Loop through each table cell
1133 foreach ( $cells
as $cell ) {
1135 if ( $first_character !==
'+' ) {
1136 $tr_after = array_pop( $tr_attributes );
1137 if ( !array_pop( $tr_history ) ) {
1138 $previous =
"<tr{$tr_after}>\n";
1140 array_push( $tr_history,
true );
1141 array_push( $tr_attributes,
'' );
1142 array_pop( $has_opened_tr );
1143 array_push( $has_opened_tr,
true );
1146 $last_tag = array_pop( $last_tag_history );
1148 if ( array_pop( $td_history ) ) {
1149 $previous =
"</{$last_tag}>\n{$previous}";
1152 if ( $first_character ===
'|' ) {
1154 } elseif ( $first_character ===
'!' ) {
1156 } elseif ( $first_character ===
'+' ) {
1157 $last_tag =
'caption';
1162 array_push( $last_tag_history, $last_tag );
1164 # A cell could contain both parameters and data
1165 $cell_data = explode(
'|', $cell, 2 );
1167 # Bug 553: Note that a '|' inside an invalid link should not
1168 # be mistaken as delimiting cell parameters
1169 if ( strpos( $cell_data[0],
'[[' ) !==
false ) {
1170 $cell =
"{$previous}<{$last_tag}>{$cell}";
1171 } elseif ( count( $cell_data ) == 1 ) {
1172 $cell =
"{$previous}<{$last_tag}>{$cell_data[0]}";
1174 $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
1176 $cell =
"{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
1180 array_push( $td_history,
true );
1183 $out .= $outLine .
"\n";
1186 # Closing open td, tr && table
1187 while ( count( $td_history ) > 0 ) {
1188 if ( array_pop( $td_history ) ) {
1191 if ( array_pop( $tr_history ) ) {
1194 if ( !array_pop( $has_opened_tr ) ) {
1195 $out .=
"<tr><td></td></tr>\n";
1198 $out .=
"</table>\n";
1201 # Remove trailing line-ending (b/c)
1202 if ( substr(
$out, -1 ) ===
"\n" ) {
1206 # special case: don't return empty table
1207 if (
$out ===
"<table>\n<tr><td></td></tr>\n</table>" ) {
1230 # Hook to suspend the parser in this state
1231 if ( !
Hooks::run(
'ParserBeforeInternalParse', [ &$this, &$text, &$this->mStripState ] ) ) {
1235 # if $frame is provided, then use $frame for replacing any variables
1237 # use frame depth to infer how include/noinclude tags should be handled
1238 # depth=0 means this is the top-level document; otherwise it's an included document
1239 if ( !$frame->depth ) {
1245 $text = $frame->expand( $dom );
1247 # if $frame is not provided, then use old-style replaceVariables
1251 Hooks::run(
'InternalParseBeforeSanitize', [ &$this, &$text, &$this->mStripState ] );
1254 [ &$this,
'attributeStripCallback' ],
1256 array_keys( $this->mTransparentTagHooks )
1258 Hooks::run(
'InternalParseBeforeLinks', [ &$this, &$text, &$this->mStripState ] );
1260 # Tables need to come after variable replacement for things to work
1261 # properly; putting them before other transformations should keep
1262 # exciting things like link expansions from showing up in surprising
1266 $text = preg_replace(
'/(^|\n)-----*/',
'\\1<hr />', $text );
1275 # replaceInternalLinks may sometimes leave behind
1276 # absolute URLs, which have to be masked to hide them from replaceExternalLinks
1277 $text = str_replace( self::MARKER_PREFIX .
'NOPARSE',
'', $text );
1295 $text = $this->mStripState->unstripGeneral( $text );
1298 Hooks::run(
'ParserAfterUnstrip', [ &$this, &$text ] );
1301 # Clean up special characters, only run once, next-to-last before doBlockLevels
1303 # french spaces, last one Guillemet-left
1304 # only if there is something before the space
1305 '/(.) (?=\\?|:|;|!|%|\\302\\273)/' =>
'\\1 ',
1306 # french spaces, Guillemet-right
1307 '/(\\302\\253) /' =>
'\\1 ',
1308 '/ (!\s*important)/' =>
' \\1', # Beware
of CSS magic word !important, bug #11874.
1310 $text = preg_replace( array_keys( $fixtags ), array_values( $fixtags ), $text );
1323 if ( !( $this->mOptions->getDisableContentConversion()
1324 || isset( $this->mDoubleUnderscores[
'nocontentconvert'] ) )
1326 if ( !$this->mOptions->getInterfaceMessage() ) {
1327 # The position of the convert() call should not be changed. it
1328 # assumes that the links are all replaced and the only thing left
1329 # is the <nowiki> mark.
1334 $text = $this->mStripState->unstripNoWiki( $text );
1337 Hooks::run(
'ParserBeforeTidy', [ &$this, &$text ] );
1341 $text = $this->mStripState->unstripGeneral( $text );
1349 # attempt to sanitize at least some nesting problems
1350 # (bug #2702 and quite a few others)
1352 # ''Something [http://www.cool.com cool''] -->
1353 # <i>Something</i><a href="http://www.cool.com"..><i>cool></i></a>
1354 '/(<([bi])>)(<([bi])>)?([^<]*)(<\/?a[^<]*>)([^<]*)(<\/\\4>)?(<\/\\2>)/' =>
1355 '\\1\\3\\5\\8\\9\\6\\1\\3\\7\\8\\9',
1356 # fix up an anchor inside another anchor, only
1357 # at least for a single single nested link (bug 3695)
1358 '/(<a[^>]+>)([^<]*)(<a[^>]+>[^<]*)<\/a>(.*)<\/a>/' =>
1359 '\\1\\2</a>\\3</a>\\1\\4</a>',
1360 # fix div inside inline elements- doBlockLevels won't wrap a line which
1361 # contains a div, so fix it up here; replace
1362 # div with escaped text
1363 '/(<([aib]) [^>]+>)([^<]*)(<div([^>]*)>)(.*)(<\/div>)([^<]*)(<\/\\2>)/' =>
1364 '\\1\\3<div\\5>\\6</div>\\8\\9',
1365 # remove empty italic or bold tag pairs, some
1366 # introduced by rules above
1367 '/<([bi])><\/\\1>/' =>
'',
1370 $text = preg_replace(
1371 array_keys( $tidyregs ),
1372 array_values( $tidyregs ),
1377 Hooks::run(
'ParserAfterTidy', [ &$this, &$text ] );
1396 $urlChar = self::EXT_LINK_URL_CLASS;
1397 $addr = self::EXT_LINK_ADDR;
1398 $space = self::SPACE_NOT_NL; # non-newline space
1399 $spdash =
"(?:-|$space)"; # a dash
or a non-newline space
1400 $spaces =
"$space++"; # possessive match
of 1
or more spaces
1401 $text = preg_replace_callback(
1403 (<a[ \t\r\n>].*?</a>) | # m[1]: Skip link text
1404 (<.*?>) | # m[2]: Skip stuff inside
1405 # HTML elements' .
"
1406 (\b(?i:$prots)($addr$urlChar*)) | # m[3]: Free external links
1407 # m[4]: Post-protocol path
1408 \b(?:RFC|PMID) $spaces # m[5]: RFC or PMID, capture number
1410 \bISBN $spaces ( # m[6]: ISBN, capture number
1411 (?: 97[89] $spdash? )? # optional 13-digit ISBN prefix
1412 (?: [0-9] $spdash? ){9} # 9 digits with opt. delimiters
1413 [0-9Xx] # check digit
1415 )!xu", [ &$this,
'magicLinkCallback' ], $text );
1425 if ( isset( $m[1] ) && $m[1] !==
'' ) {
1428 } elseif ( isset( $m[2] ) && $m[2] !==
'' ) {
1431 } elseif ( isset( $m[3] ) && $m[3] !==
'' ) {
1432 # Free external link
1434 } elseif ( isset( $m[5] ) && $m[5] !==
'' ) {
1436 if ( substr( $m[0], 0, 3 ) ===
'RFC' ) {
1439 $cssClass =
'mw-magiclink-rfc';
1441 } elseif ( substr( $m[0], 0, 4 ) ===
'PMID' ) {
1443 $urlmsg =
'pubmedurl';
1444 $cssClass =
'mw-magiclink-pmid';
1447 throw new MWException( __METHOD__ .
': unrecognised match type "' .
1448 substr( $m[0], 0, 20 ) .
'"' );
1450 $url =
wfMessage( $urlmsg, $id )->inContentLanguage()->text();
1452 } elseif ( isset( $m[6] ) && $m[6] !==
'' ) {
1455 $space = self::SPACE_NOT_NL; # non-newline space
1456 $isbn = preg_replace(
"/$space/",
' ', $isbn );
1457 $num = strtr( $isbn, [
1463 return '<a href="' .
1464 htmlspecialchars( $titleObj->getLocalURL() ) .
1465 "\" class=\"internal mw-magiclink-isbn\">ISBN $isbn</a>";
1483 # The characters '<' and '>' (which were escaped by
1484 # removeHTMLtags()) should not be included in
1485 # URLs, per RFC 2396.
1486 # Make terminate a URL as well (bug T84937)
1489 '/&(lt|gt|nbsp|#x0*(3[CcEe]|[Aa]0)|#0*(60|62|160));/',
1494 $trail = substr( $url, $m2[0][1] ) . $trail;
1495 $url = substr( $url, 0, $m2[0][1] );
1498 # Move trailing punctuation to $trail
1500 # If there is no left bracket, then consider right brackets fair game too
1501 if ( strpos( $url,
'(' ) ===
false ) {
1505 $urlRev = strrev( $url );
1506 $numSepChars = strspn( $urlRev, $sep );
1507 # Don't break a trailing HTML entity by moving the ; into $trail
1508 # This is in hot code, so use substr_compare to avoid having to
1509 # create a new string object for the comparison
1510 if ( $numSepChars && substr_compare( $url,
";", -$numSepChars, 1 ) === 0 ) {
1511 # more optimization: instead of running preg_match with a $
1512 # anchor, which can be slow, do the match on the reversed
1513 # string starting at the desired offset.
1514 # un-reversed regexp is: /&([a-z]+|#x[\da-f]+|#\d+)$/i
1515 if ( preg_match(
'/\G([a-z]+|[\da-f]+x#|\d+#)&/i', $urlRev, $m2, 0, $numSepChars ) ) {
1519 if ( $numSepChars ) {
1520 $trail = substr( $url, -$numSepChars ) . $trail;
1521 $url = substr( $url, 0, -$numSepChars );
1524 # Verify that we still have a real URL after trail removal, and
1525 # not just lone protocol
1526 if ( strlen( $trail ) >= $numPostProto ) {
1527 return $url . $trail;
1532 # Is this an external image?
1534 if ( $text ===
false ) {
1535 # Not an image, make a link
1540 # Register it in the output object...
1541 # Replace unnecessary URL escape codes with their equivalent characters
1542 $pasteurized = self::normalizeLinkUrl( $url );
1543 $this->mOutput->addExternalLink( $pasteurized );
1545 return $text . $trail;
1558 for ( $i = 6; $i >= 1; --$i ) {
1559 $h = str_repeat(
'=', $i );
1560 $text = preg_replace(
"/^$h(.+)$h\\s*$/m",
"<h$i>\\1</h$i>", $text );
1577 $outtext .= $this->
doQuotes( $line ) .
"\n";
1579 $outtext = substr( $outtext, 0, -1 );
1591 $arr = preg_split(
"/(''+)/", $text, -1, PREG_SPLIT_DELIM_CAPTURE );
1592 $countarr = count( $arr );
1593 if ( $countarr == 1 ) {
1602 for ( $i = 1; $i < $countarr; $i += 2 ) {
1603 $thislen = strlen( $arr[$i] );
1607 if ( $thislen == 4 ) {
1608 $arr[$i - 1] .=
"'";
1611 } elseif ( $thislen > 5 ) {
1615 $arr[$i - 1] .= str_repeat(
"'", $thislen - 5 );
1620 if ( $thislen == 2 ) {
1622 } elseif ( $thislen == 3 ) {
1624 } elseif ( $thislen == 5 ) {
1634 if ( ( $numbold % 2 == 1 ) && ( $numitalics % 2 == 1 ) ) {
1635 $firstsingleletterword = -1;
1636 $firstmultiletterword = -1;
1638 for ( $i = 1; $i < $countarr; $i += 2 ) {
1639 if ( strlen( $arr[$i] ) == 3 ) {
1640 $x1 = substr( $arr[$i - 1], -1 );
1641 $x2 = substr( $arr[$i - 1], -2, 1 );
1642 if ( $x1 ===
' ' ) {
1643 if ( $firstspace == -1 ) {
1646 } elseif ( $x2 ===
' ' ) {
1647 $firstsingleletterword = $i;
1652 if ( $firstmultiletterword == -1 ) {
1653 $firstmultiletterword = $i;
1660 if ( $firstsingleletterword > -1 ) {
1661 $arr[$firstsingleletterword] =
"''";
1662 $arr[$firstsingleletterword - 1] .=
"'";
1663 } elseif ( $firstmultiletterword > -1 ) {
1665 $arr[$firstmultiletterword] =
"''";
1666 $arr[$firstmultiletterword - 1] .=
"'";
1667 } elseif ( $firstspace > -1 ) {
1671 $arr[$firstspace] =
"''";
1672 $arr[$firstspace - 1] .=
"'";
1681 foreach ( $arr
as $r ) {
1682 if ( ( $i % 2 ) == 0 ) {
1683 if ( $state ===
'both' ) {
1689 $thislen = strlen( $r );
1690 if ( $thislen == 2 ) {
1691 if ( $state ===
'i' ) {
1694 } elseif ( $state ===
'bi' ) {
1697 } elseif ( $state ===
'ib' ) {
1700 } elseif ( $state ===
'both' ) {
1707 } elseif ( $thislen == 3 ) {
1708 if ( $state ===
'b' ) {
1711 } elseif ( $state ===
'bi' ) {
1714 } elseif ( $state ===
'ib' ) {
1717 } elseif ( $state ===
'both' ) {
1724 } elseif ( $thislen == 5 ) {
1725 if ( $state ===
'b' ) {
1728 } elseif ( $state ===
'i' ) {
1731 } elseif ( $state ===
'bi' ) {
1734 } elseif ( $state ===
'ib' ) {
1737 } elseif ( $state ===
'both' ) {
1749 if ( $state ===
'b' || $state ===
'ib' ) {
1752 if ( $state ===
'i' || $state ===
'bi' || $state ===
'ib' ) {
1755 if ( $state ===
'bi' ) {
1759 if ( $state ===
'both' &&
$buffer ) {
1780 $bits = preg_split( $this->mExtLinkBracketedRegex, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
1781 if ( $bits ===
false ) {
1782 throw new MWException(
"PCRE needs to be compiled with "
1783 .
"--enable-unicode-properties in order for MediaWiki to function" );
1785 $s = array_shift( $bits );
1788 while ( $i < count( $bits ) ) {
1791 $text = $bits[$i++];
1792 $trail = $bits[$i++];
1794 # The characters '<' and '>' (which were escaped by
1795 # removeHTMLtags()) should not be included in
1796 # URLs, per RFC 2396.
1798 if ( preg_match(
'/&(lt|gt);/', $url, $m2, PREG_OFFSET_CAPTURE ) ) {
1799 $text = substr( $url, $m2[0][1] ) .
' ' . $text;
1800 $url = substr( $url, 0, $m2[0][1] );
1803 # If the link text is an image URL, replace it with an <img> tag
1804 # This happened by accident in the original parser, but some people used it extensively
1806 if ( $img !==
false ) {
1812 # Set linktype for CSS - if URL==text, link is essentially free
1813 $linktype = ( $text === $url ) ?
'free' :
'text';
1815 # No link text, e.g. [http://domain.tld/some.link]
1816 if ( $text ==
'' ) {
1819 $text =
'[' . $langObj->formatNum( ++$this->mAutonumber ) .
']';
1820 $linktype =
'autonumber';
1822 # Have link text, e.g. [http://domain.tld/some.link text]s
1831 # Use the encoded URL
1832 # This means that users can paste URLs directly into the text
1833 # Funny characters like ö aren't valid in URLs anyway
1834 # This was changed in August 2004
1838 # Register link in the output object.
1839 # Replace unnecessary URL escape codes with the referenced character
1840 # This prevents spammers from hiding links from the filters
1841 $pasteurized = self::normalizeLinkUrl( $url );
1842 $this->mOutput->addExternalLink( $pasteurized );
1858 global $wgNoFollowLinks, $wgNoFollowNsExceptions, $wgNoFollowDomainExceptions;
1860 if ( $wgNoFollowLinks && !in_array( $ns, $wgNoFollowNsExceptions )
1880 $rel = self::getExternalLinkRel( $url, $this->mTitle );
1882 $target = $this->mOptions->getExternalLinkTarget();
1885 if ( !in_array( $target, [
'_self',
'_parent',
'_top' ] ) ) {
1889 if ( $rel !==
'' ) {
1892 $rel .=
'noreferrer noopener';
1908 return self::normalizeLinkUrl( $url );
1921 # First, make sure unsafe characters are encoded
1922 $url = preg_replace_callback(
'/[\x00-\x20"<>\[\\\\\]^`{|}\x7F-\xFF]/',
1924 return rawurlencode( $m[0] );
1930 $end = strlen( $url );
1932 # Fragment part - 'fragment'
1933 $start = strpos( $url,
'#' );
1934 if ( $start !==
false && $start < $end ) {
1935 $ret = self::normalizeUrlComponent(
1936 substr( $url, $start, $end - $start ),
'"#%<>[\]^`{|}' ) .
$ret;
1940 # Query part - 'query' minus &=+;
1941 $start = strpos( $url,
'?' );
1942 if ( $start !==
false && $start < $end ) {
1943 $ret = self::normalizeUrlComponent(
1944 substr( $url, $start, $end - $start ),
'"#%<>[\]^`{|}&=+;' ) .
$ret;
1948 # Scheme and path part - 'pchar'
1949 # (we assume no userinfo or encoded colons in the host)
1950 $ret = self::normalizeUrlComponent(
1951 substr( $url, 0, $end ),
'"#%<>[\]^`{|}/?' ) .
$ret;
1957 $callback =
function (
$matches )
use ( $unsafe ) {
1959 $ord = ord( $char );
1960 if ( $ord > 32 && $ord < 127 && strpos( $unsafe, $char ) ===
false ) {
1964 # Leave it escaped, but use uppercase for a-f
1968 return preg_replace_callback(
'/%[0-9A-Fa-f]{2}/', $callback, $component );
1980 $imagesfrom = $this->mOptions->getAllowExternalImagesFrom();
1981 $imagesexception = !empty( $imagesfrom );
1983 # $imagesfrom could be either a single string or an array of strings, parse out the latter
1984 if ( $imagesexception && is_array( $imagesfrom ) ) {
1985 $imagematch =
false;
1986 foreach ( $imagesfrom
as $match ) {
1987 if ( strpos( $url, $match ) === 0 ) {
1992 } elseif ( $imagesexception ) {
1993 $imagematch = ( strpos( $url, $imagesfrom ) === 0 );
1995 $imagematch =
false;
1998 if ( $this->mOptions->getAllowExternalImages()
1999 || ( $imagesexception && $imagematch )
2001 if ( preg_match( self::EXT_IMAGE_REGEX, $url ) ) {
2006 if ( !$text && $this->mOptions->getEnableImageWhitelist()
2007 && preg_match( self::EXT_IMAGE_REGEX, $url )
2009 $whitelist = explode(
2011 wfMessage(
'external_image_whitelist' )->inContentLanguage()->
text()
2014 foreach ( $whitelist
as $entry ) {
2015 # Sanitize the regex fragment, make it case-insensitive, ignore blank entries/comments
2016 if ( strpos( $entry,
'#' ) === 0 || $entry ===
'' ) {
2019 if ( preg_match(
'/' . str_replace(
'/',
'\\/', $entry ) .
'/i', $url ) ) {
2020 # Image matches a whitelist entry
2054 static $tc =
false, $e1, $e1_img;
2055 # the % is needed to support urlencoded titles as well
2058 # Match a link having the form [[namespace:link|alternate]]trail
2059 $e1 =
"/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD";
2060 # Match cases where there is no "]]", which might still be images
2061 $e1_img =
"/^([{$tc}]+)\\|(.*)\$/sD";
2066 # split the entire text string on occurrences of [[
2068 # get the first element (all text up to first [[), and remove the space we added
2071 $line = $a->current(); # Workaround
for broken ArrayIterator::next()
that returns
"void"
2072 $s = substr( $s, 1 );
2076 if ( $useLinkPrefixExtension ) {
2077 # Match the end of a line for a word that's not followed by whitespace,
2078 # e.g. in the case of 'The Arab al[[Razi]]', 'al' will be matched
2080 $charset = $wgContLang->linkPrefixCharset();
2081 $e2 =
"/^((?>.*[^$charset]|))(.+)$/sDu";
2084 if ( is_null( $this->mTitle ) ) {
2085 throw new MWException( __METHOD__ .
": \$this->mTitle is null\n" );
2087 $nottalk = !$this->mTitle->isTalkPage();
2089 if ( $useLinkPrefixExtension ) {
2091 if ( preg_match( $e2, $s, $m ) ) {
2092 $first_prefix = $m[2];
2094 $first_prefix =
false;
2103 # Loop for each link
2104 for ( ;
$line !==
false &&
$line !== null; $a->next(),
$line = $a->current() ) {
2107 # Check for excessive memory usage
2108 if ( $holders->isBig() ) {
2110 # Do the existence check, replace the link holders and clear the array
2111 $holders->replace( $s );
2115 if ( $useLinkPrefixExtension ) {
2116 if ( preg_match( $e2, $s, $m ) ) {
2123 if ( $first_prefix ) {
2124 $prefix = $first_prefix;
2125 $first_prefix =
false;
2129 $might_be_img =
false;
2133 # If we get a ] at the beginning of $m[3] that means we have a link that's something like:
2134 # [[Image:Foo.jpg|[http://example.com desc]]] <- having three ] in a row fucks up,
2135 # the real problem is with the $e1 regex
2137 # Still some problems for cases where the ] is meant to be outside punctuation,
2138 # and no image is in sight. See bug 2095.
2140 && substr( $m[3], 0, 1 ) ===
']'
2141 && strpos( $text,
'[' ) !==
false
2144 $m[3] = substr( $m[3], 1 );
2146 # fix up urlencoded title texts
2147 if ( strpos( $m[1],
'%' ) !==
false ) {
2148 # Should anchors '#' also be rejected?
2149 $m[1] = str_replace( [
'<',
'>' ], [
'<',
'>' ], rawurldecode( $m[1] ) );
2152 } elseif ( preg_match( $e1_img,
$line, $m ) ) {
2153 # Invalid, but might be an image with a link in its caption
2154 $might_be_img =
true;
2156 if ( strpos( $m[1],
'%' ) !==
false ) {
2157 $m[1] = str_replace( [
'<',
'>' ], [
'<',
'>' ], rawurldecode( $m[1] ) );
2161 $s .= $prefix .
'[[' .
$line;
2167 # Don't allow internal links to pages containing
2168 # PROTO: where PROTO is a valid URL protocol; these
2169 # should be external links.
2170 if ( preg_match(
'/^(?i:' . $this->mUrlProtocols .
')/', $origLink ) ) {
2171 $s .= $prefix .
'[[' .
$line;
2175 # Make subpage if necessary
2176 if ( $useSubpages ) {
2182 $noforce = ( substr( $origLink, 0, 1 ) !==
':' );
2184 # Strip off leading ':'
2188 $unstrip = $this->mStripState->unstripNoWiki(
$link );
2190 if ( $nt === null ) {
2191 $s .= $prefix .
'[[' .
$line;
2195 $ns = $nt->getNamespace();
2196 $iw = $nt->getInterwiki();
2198 if ( $might_be_img ) { #
if this is actually an invalid
link
2199 if ( $ns ==
NS_FILE && $noforce ) { # but might be an image
2202 # look at the next 'line' to see if we can close it there
2204 $next_line = $a->current();
2205 if ( $next_line ===
false || $next_line === null ) {
2208 $m = explode(
']]', $next_line, 3 );
2209 if ( count( $m ) == 3 ) {
2210 # the first ]] closes the inner link, the second the image
2212 $text .=
"[[{$m[0]}]]{$m[1]}";
2215 } elseif ( count( $m ) == 2 ) {
2216 # if there's exactly one ]] that's fine, we'll keep looking
2217 $text .=
"[[{$m[0]}]]{$m[1]}";
2219 # if $next_line is invalid too, we need look no further
2220 $text .=
'[[' . $next_line;
2225 # we couldn't find the end of this imageLink, so output it raw
2226 # but don't ignore what might be perfectly normal links in the text we've examined
2228 $s .=
"{$prefix}[[$link|$text";
2229 # note: no $trail, because without an end, there *is* no trail
2232 }
else { #
it's not an image, so output it raw
2233 $s .= "{$prefix}[[$link|$text";
2234 # note: no $trail, because without an end, there *is* no trail
2239 $wasblank = ( $text == '' );
2243 # Bug 4598 madness. Handle the quotes only if they come from the alternate part
2244 # [[Lista d''e paise d''o munno]] -> <a href="...">Lista d''e paise d''o munno</a>
2245 # [[Criticism of Harry Potter|Criticism of ''Harry Potter'']]
2246 # -> <a href="Criticism of Harry Potter">Criticism of <i>Harry Potter</i></a>
2247 $text = $this->doQuotes( $text );
2250 # Link not escaped by : , create the various objects
2251 if ( $noforce && !$nt->wasLocalInterwiki() ) {
2254 $iw && $this->mOptions->getInterwikiMagic() && $nottalk && (
2255 Language::fetchLanguageName( $iw, null, 'mw
' ) ||
2256 in_array( $iw, $wgExtraInterlanguageLinkPrefixes )
2259 # Bug 24502: filter duplicates
2260 if ( !isset( $this->mLangLinkLanguages[$iw] ) ) {
2261 $this->mLangLinkLanguages[$iw] = true;
2262 $this->mOutput->addLanguageLink( $nt->getFullText() );
2265 $s = rtrim( $s . $prefix );
2266 $s .= trim( $trail, "\n" ) == '' ? '': $prefix . $trail;
2270 if ( $ns == NS_FILE ) {
2271 if ( !wfIsBadImage( $nt->getDBkey(), $this->mTitle ) ) {
2273 # if no parameters were passed, $text
2274 # becomes something like "File:Foo.png",
2275 # which we don't want to pass
on to
the
2279 # recursively parse links inside the image caption
2280 # actually, this will parse them in any other parameters, too,
2281 # but it might be hard to fix that, and it doesn't matter ATM
2285 # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them
2287 $this->
makeImage( $nt, $text, $holders ) ) . $trail;
2289 $s .= $prefix . $trail;
2295 $s = rtrim( $s .
"\n" ); # bug 87
2303 $sortkey = str_replace(
"\n",
'', $sortkey );
2305 $this->mOutput->addCategory( $nt->getDBkey(), $sortkey );
2310 $s .= trim( $prefix . $trail,
"\n" ) ==
'' ?
'' : $prefix . $trail;
2316 # Self-link checking. For some languages, variants of the title are checked in
2317 # LinkHolderArray::doVariants() to allow batching the existence checks necessary
2318 # for linking to a different variant.
2319 if ( $ns !=
NS_SPECIAL && $nt->equals( $this->mTitle ) && !$nt->hasFragment() ) {
2324 # NS_MEDIA is a pseudo-namespace for linking directly to a file
2325 # @todo FIXME: Should do batch file existence checks, see comment below
2327 # Give extensions a chance to select the file revision for us
2331 [ $this, $nt, &
$options, &$descQuery ] );
2332 # Fetch and register the file (file title may be different via hooks)
2334 # Cloak with NOPARSE to avoid replacement in replaceExternalLinks
2340 # Some titles, such as valid special pages or files in foreign repos, should
2341 # be shown as bluelinks even though they're not included in the page table
2342 # @todo FIXME: isAlwaysKnown() can be expensive for file links; we should really do
2343 # batch file existence checks for NS_FILE and NS_MEDIA
2344 if ( $iw ==
'' && $nt->isAlwaysKnown() ) {
2345 $this->mOutput->addLink( $nt );
2348 # Links will be added to the output link list after checking
2349 $s .= $holders->makeHolder( $nt, $text, [], $trail, $prefix );
2372 if ( is_string(
$query ) ) {
2375 if ( $text ==
'' ) {
2376 $text = htmlspecialchars( $nt->getPrefixedText() );
2381 return $this->armorLinks(
$link ) . $trail;
2395 return preg_replace(
'/\b((?i)' . $this->mUrlProtocols .
')/',
2396 self::MARKER_PREFIX .
"NOPARSE$1", $text );
2404 # Some namespaces don't allow subpages
2428 if ( $this->mLastSection !=
'' ) {
2429 $result =
'</' . $this->mLastSection .
">\n";
2431 $this->mInPre =
false;
2432 $this->mLastSection =
'';
2447 $fl = strlen( $st1 );
2448 $shorter = strlen( $st2 );
2449 if ( $fl < $shorter ) {
2453 for ( $i = 0; $i < $shorter; ++$i ) {
2454 if ( $st1[$i] != $st2[$i] ) {
2471 $result = $this->closeParagraph();
2473 if (
'*' === $char ) {
2475 } elseif (
'#' === $char ) {
2477 } elseif (
':' === $char ) {
2479 } elseif (
';' === $char ) {
2481 $this->mDTopen =
true;
2497 if (
'*' === $char ||
'#' === $char ) {
2498 return "</li>\n<li>";
2499 } elseif (
':' === $char ||
';' === $char ) {
2501 if ( $this->mDTopen ) {
2504 if (
';' === $char ) {
2505 $this->mDTopen =
true;
2506 return $close .
'<dt>';
2508 $this->mDTopen =
false;
2509 return $close .
'<dd>';
2512 return '<!-- ERR 2 -->';
2523 if (
'*' === $char ) {
2524 $text =
"</li></ul>";
2525 } elseif (
'#' === $char ) {
2526 $text =
"</li></ol>";
2527 } elseif (
':' === $char ) {
2528 if ( $this->mDTopen ) {
2529 $this->mDTopen =
false;
2530 $text =
"</dt></dl>";
2532 $text =
"</dd></dl>";
2535 return '<!-- ERR 3 -->';
2551 # Parsing through the text line by line. The main thing
2552 # happening here is handling of block-level elements p, pre,
2553 # and making lists from lines starting with * # : etc.
2557 $this->mDTopen = $inBlockElem =
false;
2559 $paragraphStack =
false;
2560 $inBlockquote =
false;
2562 foreach ( $textLines
as $oLine ) {
2564 if ( !$linestart ) {
2574 $lastPrefixLength = strlen( $lastPrefix );
2575 $preCloseMatch = preg_match(
'/<\\/pre/i', $oLine );
2576 $preOpenMatch = preg_match(
'/<pre/i', $oLine );
2577 # If not in a <pre> element, scan for and figure out what prefixes are there.
2578 if ( !$this->mInPre ) {
2579 # Multiple prefixes may abut each other for nested lists.
2580 $prefixLength = strspn( $oLine,
'*#:;' );
2581 $prefix = substr( $oLine, 0, $prefixLength );
2584 # ; and : are both from definition-lists, so they're equivalent
2585 # for the purposes of determining whether or not we need to open/close
2587 $prefix2 = str_replace(
';',
':', $prefix );
2588 $t = substr( $oLine, $prefixLength );
2589 $this->mInPre = (bool)$preOpenMatch;
2591 # Don't interpret any other prefixes in preformatted text
2593 $prefix = $prefix2 =
'';
2598 if ( $prefixLength && $lastPrefix === $prefix2 ) {
2599 # Same as the last item, so no need to deal with nesting or opening stuff
2600 $output .= $this->nextItem( substr( $prefix, -1 ) );
2601 $paragraphStack =
false;
2603 if ( substr( $prefix, -1 ) ===
';' ) {
2604 # The one nasty exception: definition lists work like this:
2605 # ; title : definition text
2606 # So we check for : in the remainder text to split up the
2607 # title and definition, without b0rking links.
2609 if ( $this->findColonNoLinks(
$t,
$term, $t2 ) !==
false ) {
2614 } elseif ( $prefixLength || $lastPrefixLength ) {
2615 # We need to open or close prefixes, or both.
2617 # Either open or close a level...
2618 $commonPrefixLength = $this->getCommon( $prefix, $lastPrefix );
2619 $paragraphStack =
false;
2621 # Close all the prefixes which aren't shared.
2622 while ( $commonPrefixLength < $lastPrefixLength ) {
2623 $output .= $this->closeList( $lastPrefix[$lastPrefixLength - 1] );
2624 --$lastPrefixLength;
2627 # Continue the current prefix if appropriate.
2628 if ( $prefixLength <= $commonPrefixLength && $commonPrefixLength > 0 ) {
2629 $output .= $this->nextItem( $prefix[$commonPrefixLength - 1] );
2632 # Open prefixes where appropriate.
2633 if ( $lastPrefix && $prefixLength > $commonPrefixLength ) {
2636 while ( $prefixLength > $commonPrefixLength ) {
2637 $char = substr( $prefix, $commonPrefixLength, 1 );
2638 $output .= $this->openList( $char );
2640 if (
';' === $char ) {
2641 # @todo FIXME: This is dupe of code above
2642 if ( $this->findColonNoLinks(
$t,
$term, $t2 ) !==
false ) {
2647 ++$commonPrefixLength;
2649 if ( !$prefixLength && $lastPrefix ) {
2652 $lastPrefix = $prefix2;
2655 # If we have no prefixes, go to paragraph mode.
2656 if ( 0 == $prefixLength ) {
2657 # No prefix (not in list)--go to paragraph mode
2658 # XXX: use a stack for nestable elements like span, table and div
2659 $openmatch = preg_match(
2660 '/(?:<table|<h1|<h2|<h3|<h4|<h5|<h6|<pre|<tr|'
2661 .
'<p|<ul|<ol|<dl|<li|<\\/tr|<\\/td|<\\/th)/iS',
2664 $closematch = preg_match(
2665 '/(?:<\\/table|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6|'
2666 .
'<td|<th|<\\/?blockquote|<\\/?div|<hr|<\\/pre|<\\/p|<\\/mw:|'
2667 . self::MARKER_PREFIX
2668 .
'-pre|<\\/li|<\\/ul|<\\/ol|<\\/dl|<\\/?center)/iS',
2672 if ( $openmatch || $closematch ) {
2673 $paragraphStack =
false;
2674 # @todo bug 5718: paragraph closed
2675 $output .= $this->closeParagraph();
2676 if ( $preOpenMatch && !$preCloseMatch ) {
2677 $this->mInPre =
true;
2680 while ( preg_match(
'/<(\\/?)blockquote[\s>]/i',
$t,
2681 $bqMatch, PREG_OFFSET_CAPTURE, $bqOffset )
2683 $inBlockquote = !$bqMatch[1][0];
2684 $bqOffset = $bqMatch[0][1] + strlen( $bqMatch[0][0] );
2686 $inBlockElem = !$closematch;
2687 } elseif ( !$inBlockElem && !$this->mInPre ) {
2688 if (
' ' == substr(
$t, 0, 1 )
2689 && ( $this->mLastSection ===
'pre' || trim(
$t ) !=
'' )
2693 if ( $this->mLastSection !==
'pre' ) {
2694 $paragraphStack =
false;
2695 $output .= $this->closeParagraph() .
'<pre>';
2696 $this->mLastSection =
'pre';
2698 $t = substr(
$t, 1 );
2701 if ( trim(
$t ) ===
'' ) {
2702 if ( $paragraphStack ) {
2703 $output .= $paragraphStack .
'<br />';
2704 $paragraphStack =
false;
2705 $this->mLastSection =
'p';
2707 if ( $this->mLastSection !==
'p' ) {
2708 $output .= $this->closeParagraph();
2709 $this->mLastSection =
'';
2710 $paragraphStack =
'<p>';
2712 $paragraphStack =
'</p><p>';
2716 if ( $paragraphStack ) {
2718 $paragraphStack =
false;
2719 $this->mLastSection =
'p';
2720 } elseif ( $this->mLastSection !==
'p' ) {
2721 $output .= $this->closeParagraph() .
'<p>';
2722 $this->mLastSection =
'p';
2728 # somewhere above we forget to get out of pre block (bug 785)
2729 if ( $preCloseMatch && $this->mInPre ) {
2730 $this->mInPre =
false;
2732 if ( $paragraphStack ===
false ) {
2734 if ( $prefixLength === 0 ) {
2739 while ( $prefixLength ) {
2740 $output .= $this->closeList( $prefix2[$prefixLength - 1] );
2742 if ( !$prefixLength ) {
2746 if ( $this->mLastSection !=
'' ) {
2747 $output .=
'</' . $this->mLastSection .
'>';
2748 $this->mLastSection =
'';
2766 $pos = strpos( $str,
':' );
2767 if ( $pos ===
false ) {
2772 $lt = strpos( $str,
'<' );
2773 if ( $lt ===
false || $lt > $pos ) {
2774 # Easy; no tag nesting to worry about
2775 $before = substr( $str, 0, $pos );
2776 $after = substr( $str, $pos + 1 );
2780 # Ugly state machine to walk through avoiding tags.
2781 $state = self::COLON_STATE_TEXT;
2783 $len = strlen( $str );
2784 for ( $i = 0; $i < $len; $i++ ) {
2788 # (Using the number is a performance hack for common cases)
2789 case 0: # self::COLON_STATE_TEXT:
2792 # Could be either a <start> tag or an </end> tag
2793 $state = self::COLON_STATE_TAGSTART;
2796 if ( $stack == 0 ) {
2798 $before = substr( $str, 0, $i );
2799 $after = substr( $str, $i + 1 );
2802 # Embedded in a tag; don't break it.
2805 # Skip ahead looking for something interesting
2806 $colon = strpos( $str,
':', $i );
2807 if ( $colon ===
false ) {
2808 # Nothing else interesting
2811 $lt = strpos( $str,
'<', $i );
2812 if ( $stack === 0 ) {
2813 if ( $lt ===
false || $colon < $lt ) {
2815 $before = substr( $str, 0, $colon );
2816 $after = substr( $str, $colon + 1 );
2820 if ( $lt ===
false ) {
2821 # Nothing else interesting to find; abort!
2822 # We're nested, but there's no close tags left. Abort!
2825 # Skip ahead to next tag start
2827 $state = self::COLON_STATE_TAGSTART;
2830 case 1: # self::COLON_STATE_TAG:
2835 $state = self::COLON_STATE_TEXT;
2838 # Slash may be followed by >?
2839 $state = self::COLON_STATE_TAGSLASH;
2845 case 2: # self::COLON_STATE_TAGSTART:
2848 $state = self::COLON_STATE_CLOSETAG;
2851 $state = self::COLON_STATE_COMMENT;
2854 # Illegal early close? This shouldn't happen D:
2855 $state = self::COLON_STATE_TEXT;
2858 $state = self::COLON_STATE_TAG;
2861 case 3: # self::COLON_STATE_CLOSETAG:
2866 wfDebug( __METHOD__ .
": Invalid input; too many close tags\n" );
2869 $state = self::COLON_STATE_TEXT;
2872 case self::COLON_STATE_TAGSLASH:
2874 # Yes, a self-closed tag <blah/>
2875 $state = self::COLON_STATE_TEXT;
2877 # Probably we're jumping the gun, and this is an attribute
2878 $state = self::COLON_STATE_TAG;
2881 case 5: # self::COLON_STATE_COMMENT:
2883 $state = self::COLON_STATE_COMMENTDASH;
2886 case self::COLON_STATE_COMMENTDASH:
2888 $state = self::COLON_STATE_COMMENTDASHDASH;
2890 $state = self::COLON_STATE_COMMENT;
2893 case self::COLON_STATE_COMMENTDASHDASH:
2895 $state = self::COLON_STATE_TEXT;
2897 $state = self::COLON_STATE_COMMENT;
2901 throw new MWException(
"State machine error in " . __METHOD__ );
2905 wfDebug( __METHOD__ .
": Invalid input; not enough close tags (stack $stack, state $state)\n" );
2926 if ( is_null( $this->mTitle ) ) {
2931 throw new MWException( __METHOD__ .
' Should only be '
2932 .
' called while parsing (no title set)' );
2939 if (
Hooks::run(
'ParserGetVariableValueVarCache', [ &$this, &$this->mVarCache ] ) ) {
2940 if ( isset( $this->mVarCache[$index] ) ) {
2941 return $this->mVarCache[$index];
2946 Hooks::run(
'ParserGetVariableValueTs', [ &$this, &$ts ] );
2948 $pageLang = $this->getFunctionLang();
2954 case 'currentmonth':
2957 case 'currentmonth1':
2960 case 'currentmonthname':
2963 case 'currentmonthnamegen':
2966 case 'currentmonthabbrev':
2981 case 'localmonthname':
2984 case 'localmonthnamegen':
2987 case 'localmonthabbrev':
3002 case 'fullpagename':
3005 case 'fullpagenamee':
3011 case 'subpagenamee':
3014 case 'rootpagename':
3017 case 'rootpagenamee':
3021 $this->mTitle->getRootText()
3024 case 'basepagename':
3027 case 'basepagenamee':
3031 $this->mTitle->getBaseText()
3034 case 'talkpagename':
3035 if ( $this->mTitle->canTalk() ) {
3036 $talkPage = $this->mTitle->getTalkPage();
3042 case 'talkpagenamee':
3043 if ( $this->mTitle->canTalk() ) {
3044 $talkPage = $this->mTitle->getTalkPage();
3050 case 'subjectpagename':
3051 $subjPage = $this->mTitle->getSubjectPage();
3054 case 'subjectpagenamee':
3055 $subjPage = $this->mTitle->getSubjectPage();
3059 $pageid = $this->getTitle()->getArticleID();
3060 if ( $pageid == 0 ) {
3061 # 0 means the page doesn't exist in the database,
3062 # which means the user is previewing a new page.
3063 # The vary-revision flag must be set, because the magic word
3064 # will have a different value once the page is saved.
3065 $this->mOutput->setFlag(
'vary-revision' );
3066 wfDebug( __METHOD__ .
": {{PAGEID}} used in a new page, setting vary-revision...\n" );
3068 $value = $pageid ? $pageid : null;
3071 # Let the edit saving system know we should parse the page
3072 # *after* a revision ID has been assigned.
3073 $this->mOutput->setFlag(
'vary-revision' );
3074 wfDebug( __METHOD__ .
": {{REVISIONID}} used, setting vary-revision...\n" );
3075 $value = $this->mRevisionId;
3078 # Let the edit saving system know we should parse the page
3079 # *after* a revision ID has been assigned. This is for null edits.
3080 $this->mOutput->setFlag(
'vary-revision' );
3081 wfDebug( __METHOD__ .
": {{REVISIONDAY}} used, setting vary-revision...\n" );
3082 $value = intval( substr( $this->getRevisionTimestamp(), 6, 2 ) );
3084 case 'revisionday2':
3085 # Let the edit saving system know we should parse the page
3086 # *after* a revision ID has been assigned. This is for null edits.
3087 $this->mOutput->setFlag(
'vary-revision' );
3088 wfDebug( __METHOD__ .
": {{REVISIONDAY2}} used, setting vary-revision...\n" );
3089 $value = substr( $this->getRevisionTimestamp(), 6, 2 );
3091 case 'revisionmonth':
3092 # Let the edit saving system know we should parse the page
3093 # *after* a revision ID has been assigned. This is for null edits.
3094 $this->mOutput->setFlag(
'vary-revision' );
3095 wfDebug( __METHOD__ .
": {{REVISIONMONTH}} used, setting vary-revision...\n" );
3096 $value = substr( $this->getRevisionTimestamp(), 4, 2 );
3098 case 'revisionmonth1':
3099 # Let the edit saving system know we should parse the page
3100 # *after* a revision ID has been assigned. This is for null edits.
3101 $this->mOutput->setFlag(
'vary-revision' );
3102 wfDebug( __METHOD__ .
": {{REVISIONMONTH1}} used, setting vary-revision...\n" );
3103 $value = intval( substr( $this->getRevisionTimestamp(), 4, 2 ) );
3105 case 'revisionyear':
3106 # Let the edit saving system know we should parse the page
3107 # *after* a revision ID has been assigned. This is for null edits.
3108 $this->mOutput->setFlag(
'vary-revision' );
3109 wfDebug( __METHOD__ .
": {{REVISIONYEAR}} used, setting vary-revision...\n" );
3110 $value = substr( $this->getRevisionTimestamp(), 0, 4 );
3112 case 'revisiontimestamp':
3113 # Let the edit saving system know we should parse the page
3114 # *after* a revision ID has been assigned. This is for null edits.
3115 $this->mOutput->setFlag(
'vary-revision' );
3116 wfDebug( __METHOD__ .
": {{REVISIONTIMESTAMP}} used, setting vary-revision...\n" );
3117 $value = $this->getRevisionTimestamp();
3119 case 'revisionuser':
3120 # Let the edit saving system know we should parse the page
3121 # *after* a revision ID has been assigned. This is for null edits.
3122 $this->mOutput->setFlag(
'vary-revision' );
3123 wfDebug( __METHOD__ .
": {{REVISIONUSER}} used, setting vary-revision...\n" );
3124 $value = $this->getRevisionUser();
3126 case 'revisionsize':
3127 # Let the edit saving system know we should parse the page
3128 # *after* a revision ID has been assigned. This is for null edits.
3129 $this->mOutput->setFlag(
'vary-revision' );
3130 wfDebug( __METHOD__ .
": {{REVISIONSIZE}} used, setting vary-revision...\n" );
3131 $value = $this->getRevisionSize();
3134 $value = str_replace(
'_',
' ', $wgContLang->getNsText( $this->mTitle->getNamespace() ) );
3137 $value =
wfUrlencode( $wgContLang->getNsText( $this->mTitle->getNamespace() ) );
3139 case 'namespacenumber':
3140 $value = $this->mTitle->getNamespace();
3143 $value = $this->mTitle->canTalk()
3144 ? str_replace(
'_',
' ', $this->mTitle->getTalkNsText() )
3148 $value = $this->mTitle->canTalk() ?
wfUrlencode( $this->mTitle->getTalkNsText() ) :
'';
3150 case 'subjectspace':
3151 $value = str_replace(
'_',
' ', $this->mTitle->getSubjectNsText() );
3153 case 'subjectspacee':
3156 case 'currentdayname':
3169 # @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
3170 # int to remove the padding
3176 case 'localdayname':
3177 $value = $pageLang->getWeekdayName(
3185 $value = $pageLang->time(
3195 # @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
3196 # int to remove the padding
3202 case 'numberofarticles':
3205 case 'numberoffiles':
3208 case 'numberofusers':
3211 case 'numberofactiveusers':
3214 case 'numberofpages':
3217 case 'numberofadmins':
3220 case 'numberofedits':
3223 case 'currenttimestamp':
3226 case 'localtimestamp':
3229 case 'currentversion':
3244 case 'directionmark':
3245 return $pageLang->getDirMark();
3246 case 'contentlanguage':
3249 case 'cascadingsources':
3255 'ParserGetVariableValueSwitch',
3256 [ &$this, &$this->mVarCache, &$index, &
$ret, &$frame ]
3263 $this->mVarCache[$index] =
$value;
3305 $dom = $this->getPreprocessor()->preprocessToObj( $text,
$flags );
3317 $ltrimmed = ltrim(
$s );
3318 $w1 = substr(
$s, 0, strlen(
$s ) - strlen( $ltrimmed ) );
3319 $trimmed = rtrim( $ltrimmed );
3320 $diff = strlen( $ltrimmed ) - strlen( $trimmed );
3322 $w2 = substr( $ltrimmed, -$diff );
3326 return [ $w1, $trimmed, $w2 ];
3350 # Is there any text? Also, Prevent too big inclusions!
3351 $textSize = strlen( $text );
3352 if ( $textSize < 1 || $textSize > $this->mOptions->getMaxIncludeSize() ) {
3356 if ( $frame ===
false ) {
3357 $frame = $this->getPreprocessor()->newFrame();
3358 } elseif ( !( $frame instanceof
PPFrame ) ) {
3359 wfDebug( __METHOD__ .
" called using plain parameters instead of "
3360 .
"a PPFrame instance. Creating custom frame.\n" );
3361 $frame = $this->getPreprocessor()->newCustomFrame( $frame );
3364 $dom = $this->preprocessToDom( $text );
3366 $text = $frame->expand( $dom,
$flags );
3382 $eqpos = strpos( $arg,
'=' );
3383 if ( $eqpos ===
false ) {
3384 $assocArgs[$index++] = $arg;
3386 $name = trim( substr( $arg, 0, $eqpos ) );
3387 $value = trim( substr( $arg, $eqpos + 1 ) );
3388 if (
$value ===
false ) {
3391 if (
$name !==
false ) {
3427 # does no harm if $current and $max are present but are unnecessary for the message
3428 # Not doing ->inLanguage( $this->mOptions->getUserLangObj() ), since this is shown
3429 # only during preview, and that would split the parser cache unnecessarily.
3430 $warning =
wfMessage(
"$limitationType-warning" )->numParams( $current, $max )
3432 $this->mOutput->addWarning( $warning );
3433 $this->addTrackingCategory(
"$limitationType-category" );
3459 $forceRawInterwiki =
false;
3461 $isChildObj =
false;
3463 $isLocalObj =
false;
3465 # Title object, where $text came from
3468 # $part1 is the bit before the first |, and must contain only title characters.
3469 # Various prefixes will be stripped from it later.
3470 $titleWithSpaces = $frame->expand( $piece[
'title'] );
3471 $part1 = trim( $titleWithSpaces );
3474 # Original title text preserved for various purposes
3475 $originalTitle = $part1;
3477 # $args is a list of argument nodes, starting from index 0, not including $part1
3478 # @todo FIXME: If piece['parts'] is null then the call to getLength()
3479 # below won't work b/c this $args isn't an object
3480 $args = ( null == $piece[
'parts'] ) ? [] : $piece[
'parts'];
3482 $profileSection = null;
3486 $substMatch = $this->mSubstWords->matchStartAndRemove( $part1 );
3488 # Possibilities for substMatch: "subst", "safesubst" or FALSE
3489 # Decide whether to expand template or keep wikitext as-is.
3490 if ( $this->ot[
'wiki'] ) {
3491 if ( $substMatch ===
false ) {
3492 $literal =
true; # literal when
in PST with no prefix
3494 $literal =
false; # expand when
in PST with subst:
or safesubst:
3497 if ( $substMatch ==
'subst' ) {
3498 $literal =
true; # literal when not
in PST with plain subst:
3500 $literal =
false; # expand when not
in PST with safesubst:
or no prefix
3504 $text = $frame->virtualBracketedImplode(
'{{',
'|',
'}}', $titleWithSpaces,
$args );
3511 if ( !$found &&
$args->getLength() == 0 ) {
3512 $id = $this->mVariables->matchStartToEnd( $part1 );
3513 if ( $id !==
false ) {
3514 $text = $this->getVariableValue( $id, $frame );
3522 # MSG, MSGNW and RAW
3526 if ( $mwMsgnw->matchStartAndRemove( $part1 ) ) {
3529 # Remove obsolete MSG:
3531 $mwMsg->matchStartAndRemove( $part1 );
3536 if ( $mwRaw->matchStartAndRemove( $part1 ) ) {
3537 $forceRawInterwiki =
true;
3543 $colonPos = strpos( $part1,
':' );
3544 if ( $colonPos !==
false ) {
3545 $func = substr( $part1, 0, $colonPos );
3546 $funcArgs = [ trim( substr( $part1, $colonPos + 1 ) ) ];
3547 $argsLength =
$args->getLength();
3548 for ( $i = 0; $i < $argsLength; $i++ ) {
3549 $funcArgs[] =
$args->item( $i );
3552 $result = $this->callParserFunction( $frame, $func, $funcArgs );
3557 # The interface for parser functions allows for extracting
3558 # flags into the local scope. Extract any forwarded flags
3564 # Finish mangling title and then check for loops.
3565 # Set $title to a Title object and $titleText to the PDBK
3568 # Split the title into page and subpage
3570 $relative = $this->maybeDoSubpageLink( $part1, $subpage );
3571 if ( $part1 !== $relative ) {
3573 $ns = $this->mTitle->getNamespace();
3577 $titleText =
$title->getPrefixedText();
3578 # Check for language variants if the template is not found
3579 if ( $this->getConverterLanguage()->hasVariants() &&
$title->getArticleID() == 0 ) {
3580 $this->getConverterLanguage()->findVariantLink( $part1,
$title,
true );
3582 # Do recursion depth check
3583 $limit = $this->mOptions->getMaxTemplateDepth();
3584 if ( $frame->depth >=
$limit ) {
3586 $text =
'<span class="error">'
3587 .
wfMessage(
'parser-template-recursion-depth-warning' )
3588 ->numParams(
$limit )->inContentLanguage()->text()
3594 # Load from database
3595 if ( !$found &&
$title ) {
3596 $profileSection = $this->mProfiler->scopedProfileIn(
$title->getPrefixedDBkey() );
3597 if ( !
$title->isExternal() ) {
3598 if (
$title->isSpecialPage()
3599 && $this->mOptions->getAllowSpecialInclusion()
3600 && $this->ot[
'html']
3606 $argsLength =
$args->getLength();
3607 for ( $i = 0; $i < $argsLength; $i++ ) {
3608 $bits =
$args->item( $i )->splitArg();
3609 if ( strval( $bits[
'index'] ) ===
'' ) {
3611 $value = trim( $frame->expand( $bits[
'value'] ) );
3620 $context->setUser( $this->getUser() );
3621 $context->setLanguage( $this->mOptions->getUserLangObj() );
3624 $text =
$context->getOutput()->getHTML();
3625 $this->mOutput->addOutputPageMetadata(
$context->getOutput() );
3628 $this->disableCache();
3631 $found =
false; # access denied
3632 wfDebug( __METHOD__ .
": template inclusion denied for " .
3633 $title->getPrefixedDBkey() .
"\n" );
3636 if ( $text !==
false ) {
3642 # If the title is valid but undisplayable, make a link to it
3643 if ( !$found && ( $this->ot[
'html'] || $this->ot[
'pre'] ) ) {
3644 $text =
"[[:$titleText]]";
3647 } elseif (
$title->isTrans() ) {
3648 # Interwiki transclusion
3649 if ( $this->ot[
'html'] && !$forceRawInterwiki ) {
3650 $text = $this->interwikiTransclude(
$title,
'render' );
3653 $text = $this->interwikiTransclude(
$title,
'raw' );
3654 # Preprocess it like a template
3655 $text = $this->preprocessToDom( $text, self::PTD_FOR_INCLUSION );
3661 # Do infinite loop check
3662 # This has to be done after redirect resolution to avoid infinite loops via redirects
3663 if ( !$frame->loopCheck(
$title ) ) {
3665 $text =
'<span class="error">'
3666 .
wfMessage(
'parser-template-loop-warning', $titleText )->inContentLanguage()->text()
3668 wfDebug( __METHOD__ .
": template loop broken at '$titleText'\n" );
3672 # If we haven't found text to substitute by now, we're done
3673 # Recover the source wikitext and return it
3675 $text = $frame->virtualBracketedImplode(
'{{',
'|',
'}}', $titleWithSpaces,
$args );
3676 if ( $profileSection ) {
3677 $this->mProfiler->scopedProfileOut( $profileSection );
3679 return [
'object' => $text ];
3682 # Expand DOM-style return values in a child frame
3683 if ( $isChildObj ) {
3684 # Clean up argument array
3689 } elseif ( $titleText !==
false && $newFrame->isEmpty() ) {
3690 # Expansion is eligible for the empty-frame cache
3691 $text = $newFrame->cachedExpand( $titleText, $text );
3693 # Uncached expansion
3694 $text = $newFrame->expand( $text );
3697 if ( $isLocalObj && $nowiki ) {
3699 $isLocalObj =
false;
3702 if ( $profileSection ) {
3703 $this->mProfiler->scopedProfileOut( $profileSection );
3706 # Replace raw HTML by a placeholder
3708 $text = $this->insertStripItem( $text );
3709 } elseif ( $nowiki && ( $this->ot[
'html'] || $this->ot[
'pre'] ) ) {
3710 # Escape nowiki-style return values
3712 } elseif ( is_string( $text )
3713 && !$piece[
'lineStart']
3714 && preg_match(
'/^(?:{\\||:|;|#|\*)/', $text )
3716 # Bug 529: if the template begins with a table or block-level
3717 # element, it should be treated as beginning a new line.
3718 # This behavior is somewhat controversial.
3719 $text =
"\n" . $text;
3722 if ( is_string( $text ) && !$this->incrementIncludeSize(
'post-expand', strlen( $text ) ) ) {
3723 # Error, oversize inclusion
3724 if ( $titleText !==
false ) {
3725 # Make a working, properly escaped link if possible (bug 23588)
3726 $text =
"[[:$titleText]]";
3728 # This will probably not be a working link, but at least it may
3729 # provide some hint of where the problem is
3730 preg_replace(
'/^:/',
'', $originalTitle );
3731 $text =
"[[:$originalTitle]]";
3733 $text .= $this->insertStripItem(
'<!-- WARNING: template omitted, '
3734 .
'post-expand include size too large -->' );
3735 $this->limitationWarn(
'post-expand-template-inclusion' );
3738 if ( $isLocalObj ) {
3739 $ret = [
'object' => $text ];
3741 $ret = [
'text' => $text ];
3769 # Case sensitive functions
3770 if ( isset( $this->mFunctionSynonyms[1][$function] ) ) {
3771 $function = $this->mFunctionSynonyms[1][$function];
3773 # Case insensitive functions
3774 $function = $wgContLang->lc( $function );
3775 if ( isset( $this->mFunctionSynonyms[0][$function] ) ) {
3776 $function = $this->mFunctionSynonyms[0][$function];
3778 return [
'found' =>
false ];
3782 list( $callback,
$flags ) = $this->mFunctionHooks[$function];
3784 # Workaround for PHP bug 35229 and similar
3785 if ( !is_callable( $callback ) ) {
3786 throw new MWException(
"Tag hook for $function is not callable\n" );
3789 $allArgs = [ &$this ];
3791 # Convert arguments to PPNodes and collect for appending to $allArgs
3793 foreach (
$args as $k => $v ) {
3794 if ( $v instanceof
PPNode || $k === 0 ) {
3797 $funcArgs[] = $this->mPreprocessor->newPartNodeArray( [ $k => $v ] )->item( 0 );
3801 # Add a frame parameter, and pass the arguments as an array
3802 $allArgs[] = $frame;
3803 $allArgs[] = $funcArgs;
3805 # Convert arguments to plain text and append to $allArgs
3806 foreach (
$args as $k => $v ) {
3807 if ( $v instanceof
PPNode ) {
3808 $allArgs[] = trim( $frame->expand( $v ) );
3809 } elseif ( is_int( $k ) && $k >= 0 ) {
3810 $allArgs[] = trim( $v );
3812 $allArgs[] = trim(
"$k=$v" );
3817 $result = call_user_func_array( $callback, $allArgs );
3819 # The interface for function hooks allows them to return a wikitext
3820 # string or an array containing the string and any flags. This mungs
3821 # things around to match what this method should return.
3838 $preprocessFlags = 0;
3839 if ( isset(
$result[
'noparse'] ) ) {
3840 $noparse =
$result[
'noparse'];
3842 if ( isset(
$result[
'preprocessFlags'] ) ) {
3843 $preprocessFlags =
$result[
'preprocessFlags'];
3847 $result[
'text'] = $this->preprocessToDom(
$result[
'text'], $preprocessFlags );
3864 $titleText =
$title->getPrefixedDBkey();
3866 if ( isset( $this->mTplRedirCache[$titleText] ) ) {
3867 list( $ns, $dbk ) = $this->mTplRedirCache[$titleText];
3869 $titleText =
$title->getPrefixedDBkey();
3871 if ( isset( $this->mTplDomCache[$titleText] ) ) {
3872 return [ $this->mTplDomCache[$titleText],
$title ];
3875 # Cache miss, go to the database
3878 if ( $text ===
false ) {
3879 $this->mTplDomCache[$titleText] =
false;
3880 return [
false,
$title ];
3883 $dom = $this->preprocessToDom( $text, self::PTD_FOR_INCLUSION );
3884 $this->mTplDomCache[$titleText] = $dom;
3886 if ( !
$title->equals( $cacheTitle ) ) {
3887 $this->mTplRedirCache[$cacheTitle->getPrefixedDBkey()] =
3906 $cacheKey =
$title->getPrefixedDBkey();
3907 if ( !$this->currentRevisionCache ) {
3908 $this->currentRevisionCache =
new MapCacheLRU( 100 );
3910 if ( !$this->currentRevisionCache->has( $cacheKey ) ) {
3911 $this->currentRevisionCache->set( $cacheKey,
3913 call_user_func( $this->mOptions->getCurrentRevisionCallback(),
$title, $this )
3916 return $this->currentRevisionCache->get( $cacheKey );
3939 $templateCb = $this->mOptions->getTemplateCallback();
3940 $stuff = call_user_func( $templateCb,
$title, $this );
3942 $text = $stuff[
'text'];
3943 if ( is_string( $stuff[
'text'] ) ) {
3944 $text = strtr( $text,
"\x7f",
"?" );
3946 $finalTitle = isset( $stuff[
'finalTitle'] ) ? $stuff[
'finalTitle'] :
$title;
3947 if ( isset( $stuff[
'deps'] ) ) {
3948 foreach ( $stuff[
'deps']
as $dep ) {
3949 $this->mOutput->addTemplate( $dep[
'title'], $dep[
'page_id'], $dep[
'rev_id'] );
3950 if ( $dep[
'title']->equals( $this->getTitle() ) ) {
3953 $this->mOutput->setFlag(
'vary-revision' );
3957 return [ $text, $finalTitle ];
3966 return $this->fetchTemplateAndTitle(
$title )[0];
3979 $text = $skip =
false;
3983 # Loop to fetch the article, with up to 1 redirect
3985 for ( $i = 0; $i < 2 && is_object(
$title ); $i++ ) {
3987 # Give extensions a chance to select the revision instead
3988 $id =
false; # Assume current
3989 Hooks::run(
'BeforeParserFetchTemplateAndtitle',
3996 'page_id' =>
$title->getArticleID(),
4009 $rev_id =
$rev ?
$rev->getId() : 0;
4010 # If there is no current revision, there is no page
4011 if ( $id ===
false && !
$rev ) {
4013 $linkCache->addBadLinkObj(
$title );
4018 'page_id' =>
$title->getArticleID(),
4019 'rev_id' => $rev_id ];
4021 # We fetched a rev from a different title; register it too...
4023 'title' =>
$rev->getTitle(),
4024 'page_id' =>
$rev->getPage(),
4025 'rev_id' => $rev_id ];
4032 if ( $text ===
false || $text === null ) {
4038 $message =
wfMessage( $wgContLang->lcfirst(
$title->getText() ) )->inContentLanguage();
4039 if ( !$message->exists() ) {
4044 $text = $message->plain();
4057 'finalTitle' => $finalTitle,
4082 $time = $file ? $file->getTimestamp() :
false;
4083 $sha1 = $file ? $file->getSha1() :
false;
4084 # Register the file as a dependency...
4085 $this->mOutput->addImage(
$title->getDBkey(),
$time, $sha1 );
4086 if ( $file && !
$title->equals( $file->getTitle() ) ) {
4087 # Update fetched file title
4088 $title = $file->getTitle();
4089 $this->mOutput->addImage(
$title->getDBkey(),
$time, $sha1 );
4091 return [ $file,
$title ];
4105 if ( isset(
$options[
'broken'] ) ) {
4107 } elseif ( isset(
$options[
'sha1'] ) ) {
4124 global $wgEnableScaryTranscluding;
4126 if ( !$wgEnableScaryTranscluding ) {
4127 return wfMessage(
'scarytranscludedisabled' )->inContentLanguage()->text();
4130 $url =
$title->getFullURL( [
'action' => $action ] );
4132 if ( strlen( $url ) > 255 ) {
4133 return wfMessage(
'scarytranscludetoolong' )->inContentLanguage()->text();
4135 return $this->fetchScaryTemplateMaybeFromCache( $url );
4143 global $wgTranscludeCacheExpiry;
4145 $tsCond =
$dbr->timestamp( time() - $wgTranscludeCacheExpiry );
4146 $obj =
$dbr->selectRow(
'transcache', [
'tc_time',
'tc_contents' ],
4147 [
'tc_url' => $url,
"tc_time >= " .
$dbr->addQuotes( $tsCond ) ] );
4149 return $obj->tc_contents;
4155 $text =
$req->getContent();
4156 } elseif (
$req->getStatus() != 200 ) {
4158 return wfMessage(
'scarytranscludefailed-httpstatus' )
4159 ->params( $url,
$req->getStatus() )->inContentLanguage()->text();
4161 return wfMessage(
'scarytranscludefailed', $url )->inContentLanguage()->text();
4165 $dbw->replace(
'transcache', [
'tc_url' ], [
4167 'tc_time' => $dbw->timestamp( time() ),
4168 'tc_contents' => $text
4185 $parts = $piece[
'parts'];
4186 $nameWithSpaces = $frame->expand( $piece[
'title'] );
4187 $argName = trim( $nameWithSpaces );
4189 $text = $frame->getArgument( $argName );
4190 if ( $text ===
false && $parts->getLength() > 0
4191 && ( $this->ot[
'html']
4193 || ( $this->ot[
'wiki'] && $frame->isTemplate() )
4196 # No match in frame, use the supplied default
4197 $object = $parts->item( 0 )->getChildren();
4199 if ( !$this->incrementIncludeSize(
'arg', strlen( $text ) ) ) {
4200 $error =
'<!-- WARNING: argument omitted, expansion size too large -->';
4201 $this->limitationWarn(
'post-expand-template-argument' );
4204 if ( $text ===
false && $object ===
false ) {
4206 $object = $frame->virtualBracketedImplode(
'{{{',
'|',
'}}}', $nameWithSpaces, $parts );
4208 if ( $error !==
false ) {
4211 if ( $object !==
false ) {
4212 $ret = [
'object' => $object ];
4214 $ret = [
'text' => $text ];
4237 $attrText = !isset(
$params[
'attr'] ) ? null : $frame->expand(
$params[
'attr'] );
4239 $marker = self::MARKER_PREFIX .
"-$name-"
4240 . sprintf(
'%08X', $this->mMarkerIndex++ ) . self::MARKER_SUFFIX;
4242 $isFunctionTag = isset( $this->mFunctionTagHooks[strtolower(
$name )] ) &&
4243 ( $this->ot[
'html'] || $this->ot[
'pre'] );
4244 if ( $isFunctionTag ) {
4245 $markerType =
'none';
4247 $markerType =
'general';
4249 if ( $this->ot[
'html'] || $isFunctionTag ) {
4252 if ( isset(
$params[
'attributes'] ) ) {
4253 $attributes = $attributes +
$params[
'attributes'];
4256 if ( isset( $this->mTagHooks[
$name] ) ) {
4257 # Workaround for PHP bug 35229 and similar
4258 if ( !is_callable( $this->mTagHooks[$name] ) ) {
4259 throw new MWException(
"Tag hook for $name is not callable\n" );
4261 $output = call_user_func_array( $this->mTagHooks[$name],
4262 [
$content, $attributes, $this, $frame ] );
4263 } elseif ( isset( $this->mFunctionTagHooks[$name] ) ) {
4264 list( $callback, ) = $this->mFunctionTagHooks[
$name];
4265 if ( !is_callable( $callback ) ) {
4266 throw new MWException(
"Tag hook for $name is not callable\n" );
4269 $output = call_user_func_array( $callback, [ &$this, $frame,
$content, $attributes ] );
4271 $output =
'<span class="error">Invalid tag extension name: ' .
4272 htmlspecialchars( $name ) .
'</span>';
4276 # Extract flags to local scope (to override $markerType)
4283 if ( is_null( $attrText ) ) {
4286 if ( isset(
$params[
'attributes'] ) ) {
4287 foreach (
$params[
'attributes']
as $attrName => $attrValue ) {
4288 $attrText .=
' ' . htmlspecialchars( $attrName ) .
'="' .
4289 htmlspecialchars( $attrValue ) .
'"';
4293 $output =
"<$name$attrText/>";
4295 $close = is_null(
$params[
'close'] ) ?
'' : $frame->expand(
$params[
'close'] );
4296 $output =
"<$name$attrText>$content$close";
4300 if ( $markerType ===
'none' ) {
4302 } elseif ( $markerType ===
'nowiki' ) {
4303 $this->mStripState->addNoWiki( $marker,
$output );
4304 } elseif ( $markerType ===
'general' ) {
4305 $this->mStripState->addGeneral( $marker,
$output );
4307 throw new MWException( __METHOD__ .
': invalid marker type' );
4320 if ( $this->mIncludeSizes[
$type] + $size > $this->mOptions->getMaxIncludeSize() ) {
4323 $this->mIncludeSizes[
$type] += $size;
4334 $this->mExpensiveFunctionCount++;
4335 return $this->mExpensiveFunctionCount <= $this->mOptions->getExpensiveParserFunctionLimit();
4348 # The position of __TOC__ needs to be recorded
4350 if ( $mw->match( $text ) ) {
4351 $this->mShowToc =
true;
4352 $this->mForceTocPosition =
true;
4354 # Set a placeholder. At the end we'll fill it in with the TOC.
4355 $text = $mw->replace(
'<!--MWTOC-->', $text, 1 );
4357 # Only keep the first one.
4358 $text = $mw->replace(
'', $text );
4361 # Now match and remove the rest of them
4363 $this->mDoubleUnderscores = $mwa->matchAndRemove( $text );
4365 if ( isset( $this->mDoubleUnderscores[
'nogallery'] ) ) {
4366 $this->mOutput->mNoGallery =
true;
4368 if ( isset( $this->mDoubleUnderscores[
'notoc'] ) && !$this->mForceTocPosition ) {
4369 $this->mShowToc =
false;
4371 if ( isset( $this->mDoubleUnderscores[
'hiddencat'] )
4374 $this->addTrackingCategory(
'hidden-category-category' );
4376 # (bug 8068) Allow control over whether robots index a page.
4377 # @todo FIXME: Bug 14899: __INDEX__ always overrides __NOINDEX__ here! This
4378 # is not desirable, the last one on the page should win.
4379 if ( isset( $this->mDoubleUnderscores[
'noindex'] ) && $this->mTitle->canUseNoindex() ) {
4380 $this->mOutput->setIndexPolicy(
'noindex' );
4381 $this->addTrackingCategory(
'noindex-category' );
4383 if ( isset( $this->mDoubleUnderscores[
'index'] ) && $this->mTitle->canUseNoindex() ) {
4384 $this->mOutput->setIndexPolicy(
'index' );
4385 $this->addTrackingCategory(
'index-category' );
4388 # Cache all double underscores in the database
4389 foreach ( $this->mDoubleUnderscores
as $key => $val ) {
4390 $this->mOutput->setProperty(
$key,
'' );
4402 return $this->mOutput->addTrackingCategory( $msg, $this->mTitle );
4424 # Inhibit editsection links if requested in the page
4425 if ( isset( $this->mDoubleUnderscores[
'noeditsection'] ) ) {
4426 $maybeShowEditLink = $showEditLink =
false;
4428 $maybeShowEditLink =
true;
4429 $showEditLink = $this->mOptions->getEditSection();
4431 if ( $showEditLink ) {
4432 $this->mOutput->setEditSectionTokens(
true );
4435 # Get all headlines for numbering them and adding funky stuff like [edit]
4436 # links - this is for later, but we need the number of headlines right now
4438 $numMatches = preg_match_all(
4439 '/<H(?P<level>[1-6])(?P<attrib>.*?>)\s*(?P<header>[\s\S]*?)\s*<\/H[1-6] *>/i',
4444 # if there are fewer than 4 headlines in the article, do not show TOC
4445 # unless it's been explicitly enabled.
4446 $enoughToc = $this->mShowToc &&
4447 ( ( $numMatches >= 4 ) || $this->mForceTocPosition );
4449 # Allow user to stipulate that a page should have a "new section"
4450 # link added via __NEWSECTIONLINK__
4451 if ( isset( $this->mDoubleUnderscores[
'newsectionlink'] ) ) {
4452 $this->mOutput->setNewSection(
true );
4455 # Allow user to remove the "new section"
4456 # link via __NONEWSECTIONLINK__
4457 if ( isset( $this->mDoubleUnderscores[
'nonewsectionlink'] ) ) {
4458 $this->mOutput->hideNewSection(
true );
4461 # if the string __FORCETOC__ (not case-sensitive) occurs in the HTML,
4462 # override above conditions and always show TOC above first header
4463 if ( isset( $this->mDoubleUnderscores[
'forcetoc'] ) ) {
4464 $this->mShowToc =
true;
4472 # Ugh .. the TOC should have neat indentation levels which can be
4473 # passed to the skin functions. These are determined here
4477 $sublevelCount = [];
4483 $markerRegex = self::MARKER_PREFIX .
"-h-(\d+)-" . self::MARKER_SUFFIX;
4484 $baseTitleText = $this->mTitle->getPrefixedDBkey();
4485 $oldType = $this->mOutputType;
4487 $frame = $this->getPreprocessor()->newFrame();
4488 $root = $this->preprocessToDom( $origText );
4489 $node = $root->getFirstChild();
4494 $headlines = $numMatches !==
false ?
$matches[3] : [];
4496 foreach ( $headlines
as $headline ) {
4497 $isTemplate =
false;
4499 $sectionIndex =
false;
4501 $markerMatches = [];
4502 if ( preg_match(
"/^$markerRegex/", $headline, $markerMatches ) ) {
4503 $serial = $markerMatches[1];
4504 list( $titleText, $sectionIndex ) = $this->mHeadings[$serial];
4505 $isTemplate = ( $titleText != $baseTitleText );
4506 $headline = preg_replace(
"/^$markerRegex\\s*/",
"", $headline );
4510 $prevlevel = $level;
4512 $level =
$matches[1][$headlineCount];
4514 if ( $level > $prevlevel ) {
4515 # Increase TOC level
4517 $sublevelCount[$toclevel] = 0;
4518 if ( $toclevel < $wgMaxTocLevel ) {
4519 $prevtoclevel = $toclevel;
4523 } elseif ( $level < $prevlevel && $toclevel > 1 ) {
4524 # Decrease TOC level, find level to jump to
4526 for ( $i = $toclevel; $i > 0; $i-- ) {
4527 if ( $levelCount[$i] == $level ) {
4528 # Found last matching level
4531 } elseif ( $levelCount[$i] < $level ) {
4532 # Found first matching level below current level
4540 if ( $toclevel < $wgMaxTocLevel ) {
4541 if ( $prevtoclevel < $wgMaxTocLevel ) {
4542 # Unindent only if the previous toc level was shown :p
4544 $prevtoclevel = $toclevel;
4550 # No change in level, end TOC line
4551 if ( $toclevel < $wgMaxTocLevel ) {
4556 $levelCount[$toclevel] = $level;
4558 # count number of headlines for each level
4559 $sublevelCount[$toclevel]++;
4561 for ( $i = 1; $i <= $toclevel; $i++ ) {
4562 if ( !empty( $sublevelCount[$i] ) ) {
4566 $numbering .= $this->getTargetLanguage()->formatNum( $sublevelCount[$i] );
4571 # The safe header is a version of the header text safe to use for links
4573 # Remove link placeholders by the link text.
4574 # <!--LINK number-->
4576 # link text with suffix
4577 # Do this before unstrip since link text can contain strip markers
4578 $safeHeadline = $this->replaceLinkHoldersText( $headline );
4580 # Avoid insertion of weird stuff like <math> by expanding the relevant sections
4581 $safeHeadline = $this->mStripState->unstripBoth( $safeHeadline );
4583 # Strip out HTML (first regex removes any tag not allowed)
4585 # * <sup> and <sub> (bug 8393)
4588 # * <bdi> (bug 72884)
4589 # * <span dir="rtl"> and <span dir="ltr"> (bug 35167)
4590 # We strip any parameter from accepted tags (second regex), except dir="rtl|ltr" from <span>,
4591 # to allow setting directionality in toc items.
4592 $tocline = preg_replace(
4594 '#<(?!/?(span|sup|sub|bdi|i|b)(?: [^>]*)?>).*?>#',
4595 '#<(/?(?:span(?: dir="(?:rtl|ltr)")?|sup|sub|bdi|i|b))(?: .*?)?>#'
4601 # Strip '<span></span>', which is the result from the above if
4602 # <span id="foo"></span> is used to produce an additional anchor
4604 $tocline = str_replace(
'<span></span>',
'', $tocline );
4606 $tocline = trim( $tocline );
4608 # For the anchor, strip out HTML-y stuff period
4609 $safeHeadline = preg_replace(
'/<.*?>/',
'', $safeHeadline );
4612 # Save headline for section edit hint before it's escaped
4613 $headlineHint = $safeHeadline;
4615 if ( $wgExperimentalHtmlIds ) {
4616 # For reverse compatibility, provide an id that's
4617 # HTML4-compatible, like we used to.
4618 # It may be worth noting, academically, that it's possible for
4619 # the legacy anchor to conflict with a non-legacy headline
4620 # anchor on the page. In this case likely the "correct" thing
4621 # would be to either drop the legacy anchors or make sure
4622 # they're numbered first. However, this would require people
4623 # to type in section names like "abc_.D7.93.D7.90.D7.A4"
4624 # manually, so let's not bother worrying about it.
4626 [
'noninitial',
'legacy' ] );
4629 if ( $legacyHeadline == $safeHeadline ) {
4630 # No reason to have both (in fact, we can't)
4631 $legacyHeadline =
false;
4634 $legacyHeadline =
false;
4639 # HTML names must be case-insensitively unique (bug 10721).
4640 # This does not apply to Unicode characters per
4641 # http://www.w3.org/TR/html5/infrastructure.html#case-sensitivity-and-string-comparison
4642 # @todo FIXME: We may be changing them depending on the current locale.
4643 $arrayKey = strtolower( $safeHeadline );
4644 if ( $legacyHeadline ===
false ) {
4645 $legacyArrayKey =
false;
4647 $legacyArrayKey = strtolower( $legacyHeadline );
4650 # Create the anchor for linking from the TOC to the section
4651 $anchor = $safeHeadline;
4652 $legacyAnchor = $legacyHeadline;
4653 if ( isset( $refers[$arrayKey] ) ) {
4655 for ( $i = 2; isset( $refers[
"${arrayKey}_$i"] ); ++$i );
4658 $refers[
"${arrayKey}_$i"] =
true;
4660 $refers[$arrayKey] =
true;
4662 if ( $legacyHeadline !==
false && isset( $refers[$legacyArrayKey] ) ) {
4664 for ( $i = 2; isset( $refers[
"${legacyArrayKey}_$i"] ); ++$i );
4666 $legacyAnchor .=
"_$i";
4667 $refers[
"${legacyArrayKey}_$i"] =
true;
4669 $refers[$legacyArrayKey] =
true;
4672 # Don't number the heading if it is the only one (looks silly)
4673 if ( count(
$matches[3] ) > 1 && $this->mOptions->getNumberHeadings() ) {
4674 # the two are different if the line contains a link
4677 [
'class' =>
'mw-headline-number' ],
4679 ) .
' ' . $headline;
4682 if ( $enoughToc && ( !isset( $wgMaxTocLevel ) || $toclevel < $wgMaxTocLevel ) ) {
4684 $numbering, $toclevel, ( $isTemplate ?
false : $sectionIndex ) );
4687 # Add the section to the section tree
4688 # Find the DOM node for this header
4689 $noOffset = ( $isTemplate || $sectionIndex ===
false );
4690 while ( $node && !$noOffset ) {
4691 if ( $node->getName() ===
'h' ) {
4692 $bits = $node->splitHeading();
4693 if ( $bits[
'i'] == $sectionIndex ) {
4697 $byteOffset += mb_strlen( $this->mStripState->unstripBoth(
4699 $node = $node->getNextSibling();
4702 'toclevel' => $toclevel,
4705 'number' => $numbering,
4706 'index' => ( $isTemplate ?
'T-' :
'' ) . $sectionIndex,
4707 'fromtitle' => $titleText,
4708 'byteoffset' => ( $noOffset ? null : $byteOffset ),
4709 'anchor' => $anchor,
4712 # give headline the correct <h#> tag
4713 if ( $maybeShowEditLink && $sectionIndex !==
false ) {
4715 if ( $isTemplate ) {
4716 # Put a T flag in the section identifier, to indicate to extractSections()
4717 # that sections inside <includeonly> should be counted.
4718 $editsectionPage = $titleText;
4719 $editsectionSection =
"T-$sectionIndex";
4720 $editsectionContent = null;
4722 $editsectionPage = $this->mTitle->getPrefixedText();
4723 $editsectionSection = $sectionIndex;
4724 $editsectionContent = $headlineHint;
4738 $editlink =
'<mw:editsection page="' . htmlspecialchars( $editsectionPage );
4739 $editlink .=
'" section="' . htmlspecialchars( $editsectionSection ) .
'"';
4740 if ( $editsectionContent !== null ) {
4741 $editlink .=
'>' . $editsectionContent .
'</mw:editsection>';
4749 $matches[
'attrib'][$headlineCount], $anchor, $headline,
4750 $editlink, $legacyAnchor );
4755 $this->setOutputType( $oldType );
4757 # Never ever show TOC if no headers
4758 if ( $numVisible < 1 ) {
4763 if ( $prevtoclevel > 0 && $prevtoclevel < $wgMaxTocLevel ) {
4767 $this->mOutput->setTOCHTML( $toc );
4768 $toc = self::TOC_START . $toc . self::TOC_END;
4769 $this->mOutput->addModules(
'mediawiki.toc' );
4773 $this->mOutput->setSections( $tocraw );
4776 # split up and insert constructed headlines
4777 $blocks = preg_split(
'/<H[1-6].*?>[\s\S]*?<\/H[1-6]>/i', $text );
4782 foreach ( $blocks
as $block ) {
4784 if ( empty( $head[$i - 1] ) ) {
4785 $sections[$i] = $block;
4787 $sections[$i] = $head[$i - 1] . $block;
4800 Hooks::run(
'ParserSectionCreate', [ $this, $i, &$sections[$i], $showEditLink ] );
4805 if ( $enoughToc && $isMain && !$this->mForceTocPosition ) {
4808 $sections[0] = $sections[0] . $toc .
"\n";
4811 $full .= implode(
'', $sections );
4813 if ( $this->mForceTocPosition ) {
4814 return str_replace(
'<!--MWTOC-->', $toc, $full );
4834 if ( $clearState ) {
4835 $magicScopeVariable = $this->lock();
4837 $this->startParse( $title, $options,
self::OT_WIKI, $clearState );
4838 $this->setUser( $user );
4844 $text = str_replace( array_keys( $pairs ), array_values( $pairs ), $text );
4846 $text = $this->pstPass2( $text, $user );
4848 $text = $this->mStripState->unstripBoth( $text );
4850 $this->setUser( null ); # Reset
4866 # Note: This is the timestamp saved as hardcoded wikitext to
4867 # the database, we use $wgContLang here in order to give
4868 # everyone the same signature and use the default one rather
4869 # than the one selected in each user's preferences.
4870 # (see also bug 12815)
4871 $ts = $this->mOptions->getTimestamp();
4874 $tzMsg =
$timestamp->getTimezoneMessage()->inContentLanguage()->text();
4876 $d = $wgContLang->timeanddate( $ts,
false,
false ) .
" ($tzMsg)";
4878 # Variable replacement
4879 # Because mOutputType is OT_WIKI, this will only process {{subst:xxx}} type tags
4880 $text = $this->replaceVariables( $text );
4882 # This works almost by chance, as the replaceVariables are done before the getUserSig(),
4883 # which may corrupt this parser instance via its wfMessage()->text() call-
4886 $sigText = $this->getUserSig(
$user );
4887 $text = strtr( $text, [
4889 '~~~~' =>
"$sigText $d",
4893 # Context links ("pipe tricks"): [[|name]] and [[name (context)|]]
4895 $nc =
'[ _0-9A-Za-z\x80-\xff-]'; # Namespaces can
use non-ascii!
4898 $p1 =
"/\[\[(:?$nc+:|:|)($tc+?)( ?\\($tc+\\))\\|]]/";
4900 $p4 =
"/\[\[(:?$nc+:|:|)($tc+?)( ?($tc+))\\|]]/";
4902 $p3 =
"/\[\[(:?$nc+:|:|)($tc+?)( ?\\($tc+\\)|)((?:, |,)$tc+|)\\|]]/";
4904 $p2 =
"/\[\[\\|($tc+)]]/";
4906 # try $p1 first, to turn "[[A, B (C)|]]" into "[[A, B (C)|A, B]]"
4907 $text = preg_replace( $p1,
'[[\\1\\2\\3|\\2]]', $text );
4908 $text = preg_replace( $p4,
'[[\\1\\2\\3|\\2]]', $text );
4909 $text = preg_replace( $p3,
'[[\\1\\2\\3\\4|\\2]]', $text );
4911 $t = $this->mTitle->getText();
4913 if ( preg_match(
"/^($nc+:|)$tc+?( \\($tc+\\))$/",
$t, $m ) ) {
4914 $text = preg_replace( $p2,
"[[$m[1]\\1$m[2]|\\1]]", $text );
4915 } elseif ( preg_match(
"/^($nc+:|)$tc+?(, $tc+|)$/",
$t, $m ) &&
"$m[1]$m[2]" !=
'' ) {
4916 $text = preg_replace( $p2,
"[[$m[1]\\1$m[2]|\\1]]", $text );
4918 # if there's no context, don't bother duplicating the title
4919 $text = preg_replace( $p2,
'[[\\1]]', $text );
4922 # Trim trailing whitespace
4923 $text = rtrim( $text );
4947 # If not given, retrieve from the user object.
4948 if ( $nickname ===
false ) {
4949 $nickname =
$user->getOption(
'nickname' );
4952 if ( is_null( $fancySig ) ) {
4953 $fancySig =
$user->getBoolOption(
'fancysig' );
4956 $nickname = $nickname == null ?
$username : $nickname;
4958 if ( mb_strlen( $nickname ) > $wgMaxSigChars ) {
4960 wfDebug( __METHOD__ .
": $username has overlong signature.\n" );
4961 } elseif ( $fancySig !==
false ) {
4962 # Sig. might contain markup; validate this
4963 if ( $this->validateSig( $nickname ) !==
false ) {
4964 # Validated; clean up (if needed) and return it
4965 return $this->cleanSig( $nickname,
true );
4967 # Failed to validate; fall back to the default
4969 wfDebug( __METHOD__ .
": $username has bad XML tags in signature.\n" );
4973 # Make sure nickname doesnt get a sig in a sig
4974 $nickname = self::cleanSigInSig( $nickname );
4976 # If we're still here, make it a link to the user page
4979 $msgName =
$user->isAnon() ?
'signature-anon' :
'signature';
4981 return wfMessage( $msgName, $userText, $nickText )->inContentLanguage()
4982 ->title( $this->getTitle() )->text();
5008 $magicScopeVariable = $this->lock();
5012 # Option to disable this feature
5013 if ( !$this->mOptions->getCleanSignatures() ) {
5017 # @todo FIXME: Regex doesn't respect extension tags or nowiki
5018 # => Move this logic to braceSubstitution()
5020 $substRegex =
'/\{\{(?!(?:' . $substWord->getBaseRegex() .
'))/x' . $substWord->getRegexCase();
5021 $substText =
'{{' . $substWord->getSynonym( 0 );
5023 $text = preg_replace( $substRegex, $substText, $text );
5024 $text = self::cleanSigInSig( $text );
5025 $dom = $this->preprocessToDom( $text );
5026 $frame = $this->getPreprocessor()->newFrame();
5027 $text = $frame->expand( $dom );
5030 $text = $this->mStripState->unstripBoth( $text );
5043 $text = preg_replace(
'/~{3,5}/',
'', $text );
5057 $outputType, $clearState =
true
5059 $this->startParse(
$title, $options, $outputType, $clearState );
5069 $outputType, $clearState =
true
5071 $this->setTitle(
$title );
5073 $this->setOutputType( $outputType );
5074 if ( $clearState ) {
5075 $this->clearState();
5088 static $executing =
false;
5090 # Guard against infinite recursion
5133 if ( preg_match(
'/[<>\r\n]/',
$tag, $m ) ) {
5134 throw new MWException(
"Invalid character {$m[0]} in setHook('$tag', ...) call" );
5136 $oldVal = isset( $this->mTagHooks[
$tag] ) ? $this->mTagHooks[
$tag] : null;
5137 $this->mTagHooks[
$tag] = $callback;
5138 if ( !in_array( $tag, $this->mStripList ) ) {
5139 $this->mStripList[] =
$tag;
5164 if ( preg_match(
'/[<>\r\n]/',
$tag, $m ) ) {
5165 throw new MWException(
"Invalid character {$m[0]} in setTransparentHook('$tag', ...) call" );
5167 $oldVal = isset( $this->mTransparentTagHooks[
$tag] ) ? $this->mTransparentTagHooks[
$tag] : null;
5168 $this->mTransparentTagHooks[
$tag] = $callback;
5177 $this->mTagHooks = [];
5178 $this->mFunctionTagHooks = [];
5179 $this->mStripList = $this->mDefaultStripList;
5228 $oldVal = isset( $this->mFunctionHooks[$id] ) ? $this->mFunctionHooks[$id][0] : null;
5229 $this->mFunctionHooks[$id] = [ $callback,
$flags ];
5231 # Add to function cache
5234 throw new MWException( __METHOD__ .
'() expecting a magic word identifier.' );
5237 $synonyms = $mw->getSynonyms();
5238 $sensitive = intval( $mw->isCaseSensitive() );
5240 foreach ( $synonyms
as $syn ) {
5242 if ( !$sensitive ) {
5243 $syn = $wgContLang->lc( $syn );
5249 # Remove trailing colon
5250 if ( substr( $syn, -1, 1 ) ===
':' ) {
5251 $syn = substr( $syn, 0, -1 );
5253 $this->mFunctionSynonyms[$sensitive][$syn] = $id;
5264 return array_keys( $this->mFunctionHooks );
5279 if ( preg_match(
'/[<>\r\n]/',
$tag, $m ) ) {
5280 throw new MWException(
"Invalid character {$m[0]} in setFunctionTagHook('$tag', ...) call" );
5282 $old = isset( $this->mFunctionTagHooks[
$tag] ) ?
5283 $this->mFunctionTagHooks[
$tag] : null;
5284 $this->mFunctionTagHooks[
$tag] = [ $callback,
$flags ];
5286 if ( !in_array( $tag, $this->mStripList ) ) {
5287 $this->mStripList[] =
$tag;
5301 $this->mLinkHolders->replace( $text );
5312 return $this->mLinkHolders->replaceText( $text );
5331 if ( isset(
$params[
'mode'] ) ) {
5342 $ig->setContextTitle( $this->mTitle );
5343 $ig->setShowBytes(
false );
5344 $ig->setShowFilename(
false );
5345 $ig->setParser( $this );
5346 $ig->setHideBadImages();
5349 if ( isset(
$params[
'showfilename'] ) ) {
5350 $ig->setShowFilename(
true );
5352 $ig->setShowFilename(
false );
5354 if ( isset(
$params[
'caption'] ) ) {
5355 $caption =
$params[
'caption'];
5356 $caption = htmlspecialchars( $caption );
5357 $caption = $this->replaceInternalLinks( $caption );
5358 $ig->setCaptionHtml( $caption );
5360 if ( isset(
$params[
'perrow'] ) ) {
5361 $ig->setPerRow(
$params[
'perrow'] );
5363 if ( isset(
$params[
'widths'] ) ) {
5364 $ig->setWidths(
$params[
'widths'] );
5366 if ( isset(
$params[
'heights'] ) ) {
5367 $ig->setHeights(
$params[
'heights'] );
5369 $ig->setAdditionalOptions(
$params );
5371 Hooks::run(
'BeforeParserrenderImageGallery', [ &$this, &$ig ] );
5375 # match lines like these:
5376 # Image:someimage.jpg|This is some image
5378 preg_match(
"/^([^|]+)(\\|(.*))?$/", $line,
$matches );
5384 if ( strpos(
$matches[0],
'%' ) !==
false ) {
5388 if ( is_null(
$title ) ) {
5389 # Bogus title. Ignore these so we don't bomb out later.
5393 # We need to get what handler the file uses, to figure out parameters.
5394 # Note, a hook can overide the file name, and chose an entirely different
5395 # file (which potentially could be of a different type and have different handler).
5400 # Don't register it now, as ImageGallery does that later.
5402 $handler = $file ? $file->getHandler() :
false;
5405 'img_alt' =>
'gallery-internal-alt',
5406 'img_link' =>
'gallery-internal-link',
5409 $paramMap = $paramMap +
$handler->getParamMap();
5412 unset( $paramMap[
'img_width'] );
5420 $handlerOptions = [];
5431 foreach ( $parameterMatches
as $parameterMatch ) {
5432 list( $magicName, $match ) = $mwArray->matchVariableStartToEnd( $parameterMatch );
5434 $paramName = $paramMap[$magicName];
5436 switch ( $paramName ) {
5437 case 'gallery-internal-alt':
5438 $alt = $this->stripAltText( $match,
false );
5440 case 'gallery-internal-link':
5441 $linkValue = strip_tags( $this->replaceLinkHoldersText( $match ) );
5442 $chars = self::EXT_LINK_URL_CLASS;
5443 $addr = self::EXT_LINK_ADDR;
5444 $prots = $this->mUrlProtocols;
5446 if ( preg_match(
"/^($prots)$addr$chars*$/u", $linkValue ) ) {
5450 if ( $localLinkTitle !== null ) {
5457 if (
$handler->validateParam( $paramName, $match ) ) {
5458 $handlerOptions[$paramName] = $match;
5461 wfDebug(
"$parameterMatch failed parameter validation\n" );
5462 $label =
'|' . $parameterMatch;
5468 $label =
'|' . $parameterMatch;
5472 $label = substr( $label, 1 );
5475 $ig->add(
$title, $label, $alt,
$link, $handlerOptions );
5477 $html = $ig->toHTML();
5488 $handlerClass = get_class(
$handler );
5492 if ( !isset( $this->mImageParams[$handlerClass] ) ) {
5493 # Initialise static lists
5494 static $internalParamNames = [
5495 'horizAlign' => [
'left',
'right',
'center',
'none' ],
5496 'vertAlign' => [
'baseline',
'sub',
'super',
'top',
'text-top',
'middle',
5497 'bottom',
'text-bottom' ],
5498 'frame' => [
'thumbnail',
'manualthumb',
'framed',
'frameless',
5499 'upright',
'border',
'link',
'alt',
'class' ],
5501 static $internalParamMap;
5502 if ( !$internalParamMap ) {
5503 $internalParamMap = [];
5504 foreach ( $internalParamNames
as $type => $names ) {
5506 $magicName = str_replace(
'-',
'_',
"img_$name" );
5507 $internalParamMap[$magicName] = [
$type,
$name ];
5512 # Add handler params
5513 $paramMap = $internalParamMap;
5515 $handlerParamMap =
$handler->getParamMap();
5516 foreach ( $handlerParamMap
as $magic => $paramName ) {
5517 $paramMap[$magic] = [
'handler', $paramName ];
5520 $this->mImageParams[$handlerClass] = $paramMap;
5521 $this->mImageParamsMagicArray[$handlerClass] =
new MagicWordArray( array_keys( $paramMap ) );
5523 return [ $this->mImageParams[$handlerClass], $this->mImageParamsMagicArray[$handlerClass] ];
5535 # Check if the options text is of the form "options|alt text"
5537 # * thumbnail make a thumbnail with enlarge-icon and caption, alignment depends on lang
5538 # * left no resizing, just left align. label is used for alt= only
5539 # * right same, but right aligned
5540 # * none same, but not aligned
5541 # * ___px scale to ___ pixels width, no aligning. e.g. use in taxobox
5542 # * center center the image
5543 # * frame Keep original image size, no magnify-button.
5544 # * framed Same as "frame"
5545 # * frameless like 'thumb' but without a frame. Keeps user preferences for width
5546 # * upright reduce width for upright images, rounded to full __0 px
5547 # * border draw a 1px border around the image
5548 # * alt Text for HTML alt attribute (defaults to empty)
5549 # * class Set a class for img node
5550 # * link Set the target of the image link. Can be external, interwiki, or local
5551 # vertical-align values (no % or length right now):
5563 # Give extensions a chance to select the file revision for us
5568 # Fetch and register the file (file title may be different via hooks)
5572 $handler = $file ? $file->getHandler() :
false;
5574 list( $paramMap, $mwArray ) = $this->getImageParams(
$handler );
5577 $this->addTrackingCategory(
'broken-file-category' );
5580 # Process the input parameters
5582 $params = [
'frame' => [],
'handler' => [],
5583 'horizAlign' => [],
'vertAlign' => [] ];
5584 $seenformat =
false;
5585 foreach ( $parts
as $part ) {
5586 $part = trim( $part );
5587 list( $magicName,
$value ) = $mwArray->matchVariableStartToEnd( $part );
5589 if ( isset( $paramMap[$magicName] ) ) {
5590 list(
$type, $paramName ) = $paramMap[$magicName];
5592 # Special case; width and height come in one variable together
5593 if (
$type ===
'handler' && $paramName ===
'width' ) {
5594 $parsedWidthParam = $this->parseWidthParam(
$value );
5595 if ( isset( $parsedWidthParam[
'width'] ) ) {
5596 $width = $parsedWidthParam[
'width'];
5597 if (
$handler->validateParam(
'width', $width ) ) {
5602 if ( isset( $parsedWidthParam[
'height'] ) ) {
5603 $height = $parsedWidthParam[
'height'];
5604 if (
$handler->validateParam(
'height', $height ) ) {
5609 # else no validation -- bug 13436
5611 if (
$type ===
'handler' ) {
5612 # Validate handler parameter
5615 # Validate internal parameters
5616 switch ( $paramName ) {
5620 # @todo FIXME: Possibly check validity here for
5621 # manualthumb? downstream behavior seems odd with
5622 # missing manual thumbs.
5627 $chars = self::EXT_LINK_URL_CLASS;
5628 $addr = self::EXT_LINK_ADDR;
5629 $prots = $this->mUrlProtocols;
5631 $paramName =
'no-link';
5634 } elseif ( preg_match(
"/^((?i)$prots)/",
$value ) ) {
5635 if ( preg_match(
"/^((?i)$prots)$addr$chars*$/u",
$value, $m ) ) {
5636 $paramName =
'link-url';
5637 $this->mOutput->addExternalLink(
$value );
5638 if ( $this->mOptions->getExternalLinkTarget() ) {
5639 $params[
$type][
'link-target'] = $this->mOptions->getExternalLinkTarget();
5646 $paramName =
'link-title';
5648 $this->mOutput->addLink( $linkTitle );
5657 $validated = ! $seenformat;
5661 # Most other things appear to be empty or numeric...
5662 $validated = (
$value ===
false || is_numeric( trim(
$value ) ) );
5671 if ( !$validated ) {
5676 # Process alignment parameters
5677 if (
$params[
'horizAlign'] ) {
5684 $params[
'frame'][
'caption'] = $caption;
5686 # Will the image be presented in a frame, with the caption below?
5687 $imageIsFramed = isset(
$params[
'frame'][
'frame'] )
5688 || isset(
$params[
'frame'][
'framed'] )
5689 || isset(
$params[
'frame'][
'thumbnail'] )
5690 || isset(
$params[
'frame'][
'manualthumb'] );
5692 # In the old days, [[Image:Foo|text...]] would set alt text. Later it
5693 # came to also set the caption, ordinary text after the image -- which
5694 # makes no sense, because that just repeats the text multiple times in
5695 # screen readers. It *also* came to set the title attribute.
5696 # Now that we have an alt attribute, we should not set the alt text to
5697 # equal the caption: that's worse than useless, it just repeats the
5698 # text. This is the framed/thumbnail case. If there's no caption, we
5699 # use the unnamed parameter for alt text as well, just for the time be-
5700 # ing, if the unnamed param is set and the alt param is not.
5701 # For the future, we need to figure out if we want to tweak this more,
5702 # e.g., introducing a title= parameter for the title; ignoring the un-
5703 # named parameter entirely for images without a caption; adding an ex-
5704 # plicit caption= parameter and preserving the old magic unnamed para-
5706 if ( $imageIsFramed ) { # Framed image
5707 if ( $caption ===
'' && !isset(
$params[
'frame'][
'alt'] ) ) {
5708 # No caption or alt text, add the filename as the alt text so
5709 # that screen readers at least get some description of the image
5712 # Do not set $params['frame']['title'] because tooltips don't make sense
5714 }
else { # Inline image
5715 if ( !isset(
$params[
'frame'][
'alt'] ) ) {
5716 # No alt text, use the "caption" for the alt text
5717 if ( $caption !==
'' ) {
5718 $params[
'frame'][
'alt'] = $this->stripAltText( $caption, $holders );
5720 # No caption, fall back to using the filename for the
5725 # Use the "caption" for the tooltip text
5726 $params[
'frame'][
'title'] = $this->stripAltText( $caption, $holders );
5731 # Linker does the rest
5734 $time, $descQuery, $this->mOptions->getThumbSize() );
5736 # Give the handler a chance to modify the parser object
5738 $handler->parserTransformHook( $this, $file );
5750 # Strip bad stuff out of the title (tooltip). We can't just use
5751 # replaceLinkHoldersText() here, because if this function is called
5752 # from replaceInternalLinks2(), mLinkHolders won't be up-to-date.
5754 $tooltip = $holders->replaceText( $caption );
5756 $tooltip = $this->replaceLinkHoldersText( $caption );
5759 # make sure there are no placeholders in thumbnail attributes
5760 # that are later expanded to html- so expand them now and
5762 $tooltip = $this->mStripState->unstripBoth( $tooltip );
5773 wfDebug(
"Parser output marked as uncacheable.\n" );
5774 if ( !$this->mOutput ) {
5776 " can only be called when actually parsing something" );
5778 $this->mOutput->updateCacheExpiry( 0 );
5790 $text = $this->replaceVariables( $text, $frame );
5791 $text = $this->mStripState->unstripBoth( $text );
5802 array_keys( $this->mTransparentTagHooks ),
5803 array_keys( $this->mTagHooks ),
5804 array_keys( $this->mFunctionTagHooks )
5820 $elements = array_keys( $this->mTransparentTagHooks );
5821 $text = self::extractTagsAndParams( $elements, $text,
$matches );
5826 $tagName = strtolower( $element );
5827 if ( isset( $this->mTransparentTagHooks[$tagName] ) ) {
5828 $output = call_user_func_array(
5829 $this->mTransparentTagHooks[$tagName],
5835 $replacements[$marker] =
$output;
5837 return strtr( $text, $replacements );
5872 $magicScopeVariable = $this->lock();
5875 $frame = $this->getPreprocessor()->newFrame();
5877 # Process section extraction flags
5879 $sectionParts = explode(
'-', $sectionId );
5880 $sectionIndex = array_pop( $sectionParts );
5881 foreach ( $sectionParts
as $part ) {
5882 if ( $part ===
'T' ) {
5883 $flags |= self::PTD_FOR_INCLUSION;
5887 # Check for empty input
5888 if ( strval( $text ) ===
'' ) {
5889 # Only sections 0 and T-0 exist in an empty document
5890 if ( $sectionIndex == 0 ) {
5891 if ( $mode ===
'get' ) {
5897 if ( $mode ===
'get' ) {
5905 # Preprocess the text
5906 $root = $this->preprocessToDom( $text,
$flags );
5908 # <h> nodes indicate section breaks
5909 # They can only occur at the top level, so we can find them by iterating the root's children
5910 $node = $root->getFirstChild();
5912 # Find the target section
5913 if ( $sectionIndex == 0 ) {
5914 # Section zero doesn't nest, level=big
5915 $targetLevel = 1000;
5918 if ( $node->getName() ===
'h' ) {
5919 $bits = $node->splitHeading();
5920 if ( $bits[
'i'] == $sectionIndex ) {
5921 $targetLevel = $bits[
'level'];
5925 if ( $mode ===
'replace' ) {
5928 $node = $node->getNextSibling();
5934 if ( $mode ===
'get' ) {
5941 # Find the end of the section, including nested sections
5943 if ( $node->getName() ===
'h' ) {
5944 $bits = $node->splitHeading();
5945 $curLevel = $bits[
'level'];
5946 if ( $bits[
'i'] != $sectionIndex && $curLevel <= $targetLevel ) {
5950 if ( $mode ===
'get' ) {
5953 $node = $node->getNextSibling();
5956 # Write out the remainder (in replace mode only)
5957 if ( $mode ===
'replace' ) {
5958 # Output the replacement text
5959 # Add two newlines on -- trailing whitespace in $newText is conventionally
5960 # stripped by the editor, so we need both newlines to restore the paragraph gap
5961 # Only add trailing whitespace if there is newText
5962 if ( $newText !=
"" ) {
5963 $outText .= $newText .
"\n\n";
5968 $node = $node->getNextSibling();
5972 if ( is_string( $outText ) ) {
5973 # Re-insert stripped tags
5974 $outText = rtrim( $this->mStripState->unstripBoth( $outText ) );
5994 public function getSection( $text, $sectionId, $defaultText =
'' ) {
5995 return $this->extractSections( $text, $sectionId,
'get', $defaultText );
6011 return $this->extractSections( $oldText, $sectionId,
'replace', $newText );
6020 return $this->mRevisionId;
6030 if ( !is_null( $this->mRevisionObject ) ) {
6031 return $this->mRevisionObject;
6033 if ( is_null( $this->mRevisionId ) ) {
6037 $rev = call_user_func(
6038 $this->mOptions->getCurrentRevisionCallback(), $this->getTitle(), $this
6041 # If the parse is for a new revision, then the callback should have
6042 # already been set to force the object and should match mRevisionId.
6043 # If not, try to fetch by mRevisionId for sanity.
6044 if (
$rev &&
$rev->getId() != $this->mRevisionId ) {
6048 $this->mRevisionObject =
$rev;
6050 return $this->mRevisionObject;
6059 if ( is_null( $this->mRevisionTimestamp ) ) {
6062 $revObject = $this->getRevisionObject();
6065 # The cryptic '' timezone parameter tells to use the site-default
6066 # timezone offset instead of the user settings.
6067 # Since this value will be saved into the parser cache, served
6068 # to other users, and potentially even used inside links and such,
6069 # it needs to be consistent for all visitors.
6070 $this->mRevisionTimestamp = $wgContLang->userAdjust(
$timestamp,
'' );
6073 return $this->mRevisionTimestamp;
6082 if ( is_null( $this->mRevisionUser ) ) {
6083 $revObject = $this->getRevisionObject();
6085 # if this template is subst: the revision id will be blank,
6086 # so just use the current user's name
6088 $this->mRevisionUser = $revObject->getUserText();
6089 } elseif ( $this->ot[
'wiki'] || $this->mOptions->getIsPreview() ) {
6090 $this->mRevisionUser = $this->getUser()->getName();
6093 return $this->mRevisionUser;
6102 if ( is_null( $this->mRevisionSize ) ) {
6103 $revObject = $this->getRevisionObject();
6105 # if this variable is subst: the revision id will be blank,
6106 # so just use the parser input size, because the own substituation
6107 # will change the size.
6109 $this->mRevisionSize = $revObject->getSize();
6110 } elseif ( $this->ot[
'wiki'] || $this->mOptions->getIsPreview() ) {
6111 $this->mRevisionSize = $this->mInputSize;
6114 return $this->mRevisionSize;
6123 $this->mDefaultSort =
$sort;
6124 $this->mOutput->setProperty(
'defaultsort',
$sort );
6138 if ( $this->mDefaultSort !==
false ) {
6139 return $this->mDefaultSort;
6152 return $this->mDefaultSort;
6165 # Strip out wikitext links(they break the anchor)
6166 $text = $this->stripSectionName( $text );
6180 # Strip out wikitext links(they break the anchor)
6181 $text = $this->stripSectionName( $text );
6201 # Strip internal link markup
6202 $text = preg_replace(
'/\[\[:?([^[|]+)\|([^[]+)\]\]/',
'$2', $text );
6203 $text = preg_replace(
'/\[\[:?([^[]+)\|?\]\]/',
'$1', $text );
6205 # Strip external link markup
6206 # @todo FIXME: Not tolerant to blank link text
6207 # I.E. [https://www.mediawiki.org] will render as [1] or something depending
6208 # on how many empty links there are on the page - need to figure that out.
6209 $text = preg_replace(
'/\[(?i:' . $this->mUrlProtocols .
')([^ ]+?) ([^[]+)\]/',
'$2', $text );
6211 # Parse wikitext quotes (italics & bold)
6212 $text = $this->doQuotes( $text );
6232 $magicScopeVariable = $this->lock();
6233 $this->startParse( $title, $options, $outputType,
true );
6235 $text = $this->replaceVariables( $text );
6236 $text = $this->mStripState->unstripBoth( $text );
6248 return $this->preSaveTransform( $text, $title, $options->
getUser(),
$options );
6280 while ( $i < strlen(
$s ) ) {
6281 $markerStart = strpos(
$s, self::MARKER_PREFIX, $i );
6282 if ( $markerStart ===
false ) {
6283 $out .= call_user_func( $callback, substr(
$s, $i ) );
6286 $out .= call_user_func( $callback, substr(
$s, $i, $markerStart - $i ) );
6287 $markerEnd = strpos(
$s, self::MARKER_SUFFIX, $markerStart );
6288 if ( $markerEnd ===
false ) {
6289 $out .= substr(
$s, $markerStart );
6292 $markerEnd += strlen( self::MARKER_SUFFIX );
6293 $out .= substr(
$s, $markerStart, $markerEnd - $markerStart );
6308 return $this->mStripState->killMarkers( $text );
6330 'version' => self::HALF_PARSED_VERSION,
6331 'stripState' => $this->mStripState->getSubState( $text ),
6332 'linkHolders' => $this->mLinkHolders->getSubArray( $text )
6353 if ( !isset( $data[
'version'] ) || $data[
'version'] != self::HALF_PARSED_VERSION ) {
6354 throw new MWException( __METHOD__ .
': invalid version' );
6357 # First, extract the strip state.
6358 $texts = [ $data[
'text'] ];
6359 $texts = $this->mStripState->merge( $data[
'stripState'], $texts );
6361 # Now renumber links
6362 $texts = $this->mLinkHolders->mergeForeign( $data[
'linkHolders'], $texts );
6364 # Should be good to go.
6378 return isset( $data[
'version'] ) && $data[
'version'] == self::HALF_PARSED_VERSION;
6390 $parsedWidthParam = [];
6392 return $parsedWidthParam;
6395 # (bug 13500) In both cases (width/height and width only),
6396 # permit trailing "px" for backward compatibility.
6397 if ( preg_match(
'/^([0-9]*)x([0-9]*)\s*(?:px)?\s*$/',
$value, $m ) ) {
6398 $width = intval( $m[1] );
6399 $height = intval( $m[2] );
6400 $parsedWidthParam[
'width'] = $width;
6401 $parsedWidthParam[
'height'] = $height;
6402 } elseif ( preg_match(
'/^[0-9]*\s*(?:px)?\s*$/',
$value ) ) {
6403 $width = intval(
$value );
6404 $parsedWidthParam[
'width'] = $width;
6406 return $parsedWidthParam;
6419 if ( $this->mInParse ) {
6420 throw new MWException(
"Parser state cleared while parsing. "
6421 .
"Did you call Parser::parse recursively?" );
6423 $this->mInParse =
true;
6426 $this->mInParse =
false;
6429 return $recursiveCheck;
6444 if ( preg_match(
'/^<p>(.*)\n?<\/p>\n?$/sU',
$html, $m ) ) {
6445 if ( strpos( $m[1],
'</p>' ) ===
false ) {
6465 if ( $this->mInParse ) {
6466 return new $wgParserConf[
'class']( $wgParserConf );
6480 $this->mOutput->setEnableOOUI(
true );
getRevisionObject()
Get the revision object for $this->mRevisionId.
setTitle($t)
Set the context title.
markerSkipCallback($s, $callback)
Call a callback function on all regions of the given text that are not inside strip markers...
#define the
table suitable for use with IDatabase::select()
replaceInternalLinks2(&$s)
Process [[ ]] wikilinks (RIL)
static getVariableIDs()
Get an array of parser variable IDs.
you don t have to do a grep find to see where the $wgReverseTitle variable is used
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global then executing the whole list after the page is displayed We don t do anything smart like collating updates to the same table or such because the list is almost always going to have just one item on if that
external whereas SearchGetNearMatch runs after $term
isValidHalfParsedText($data)
Returns true if the given array, presumed to be generated by serializeHalfParsedText(), is compatible with the current version of the parser.
null means default in associative array form
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
static tocLineEnd()
End a Table Of Contents line.
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
getSection($text, $sectionId, $defaultText= '')
This function returns the text of a section, specified by a number ($section).
static decodeTagAttributes($text)
Return an associative array of attribute names and values from a partial tag string.
killMarkers($text)
Remove any strip markers found in the given text.
wfGetDB($db, $groups=[], $wiki=false)
Get a Database object.
static tocList($toc, $lang=false)
Wraps the TOC in a table and provides the hide/collapse javascript.
fetchTemplateAndTitle($title)
Fetch the unparsed text of a template and register a reference to it.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
getRevisionUser()
Get the name of the user that edited the last revision.
setFunctionTagHook($tag, $callback, $flags)
Create a tag function, e.g.
the array() calling protocol came about after MediaWiki 1.4rc1.
stripSectionName($text)
Strips a text string of wikitext for use in a section anchor.
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
static linkKnown($target, $html=null, $customAttribs=[], $query=[], $options=[ 'known', 'noclasses'])
Identical to link(), except $options defaults to 'known'.
magic word the default is to use $key to get the and $key value or $key value text $key value html to format the value $key
Group all the pieces relevant to the context of a request into one instance.
getPreloadText($text, Title $title, ParserOptions $options, $params=[])
Process the wikitext for the "?preload=" feature.
validateSig($text)
Check that the user's signature contains no bad XML.
MapCacheLRU null $currentRevisionCache
$wgSitename
Name of the site.
renderImageGallery($text, $params)
Renders an image gallery from a text with one line per image.
recursivePreprocess($text, $frame=false)
Recursive parser entry point that can be called from an extension tag hook.
replaceExternalLinks($text)
Replace external links (REL)
static isNonincludable($index)
It is not possible to use pages from this namespace as template?
static replaceUnusualEscapes($url)
Replace unusual escape codes in a URL with their equivalent characters.
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
doHeadings($text)
Parse headers and return html.
static getTitleFor($name, $subpage=false, $fragment= '')
Get a localised Title object for a specified special page name.
findColonNoLinks($str, &$before, &$after)
Split up a string on ':', ignoring any occurrences inside tags to prevent illegal overlapping...
static isWellFormedXmlFragment($text)
Check if a string is a well-formed XML fragment.
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
fetchFileAndTitle($title, $options=[])
Fetch a file and its title and register a reference to it.
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like please read the documentation for except in special pages derived from QueryPage It s a common pitfall for new developers to submit code containing SQL queries which examine huge numbers of rows Remember that COUNT * is(N), counting rows in atable is like counting beans in a bucket.------------------------------------------------------------------------Replication------------------------------------------------------------------------The largest installation of MediaWiki, Wikimedia, uses a large set ofslave MySQL servers replicating writes made to a master MySQL server.Itis important to understand the issues associated with this setup if youwant to write code destined for Wikipedia.It's often the case that the best algorithm to use for a given taskdepends on whether or not replication is in use.Due to our unabashedWikipedia-centrism, we often just use the replication-friendly version, but if you like, you can use wfGetLB() ->getServerCount() > 1 tocheck to see if replication is in use.===Lag===Lag primarily occurs when large write queries are sent to the master.Writes on the master are executed in parallel, but they are executed inserial when they are replicated to the slaves.The master writes thequery to the binlog when the transaction is committed.The slaves pollthe binlog and start executing the query as soon as it appears.They canservice reads while they are performing a write query, but will not readanything more from the binlog and thus will perform no more writes.Thismeans that if the write query runs for a long time, the slaves will lagbehind the master for the time it takes for the write query to complete.Lag can be exacerbated by high read load.MediaWiki's load balancer willstop sending reads to a slave when it is lagged by more than 30 seconds.If the load ratios are set incorrectly, or if there is too much loadgenerally, this may lead to a slave permanently hovering around 30seconds lag.If all slaves are lagged by more than 30 seconds, MediaWiki will stopwriting to the database.All edits and other write operations will berefused, with an error returned to the user.This gives the slaves achance to catch up.Before we had this mechanism, the slaves wouldregularly lag by several minutes, making review of recent editsdifficult.In addition to this, MediaWiki attempts to ensure that the user seesevents occurring on the wiki in chronological order.A few seconds of lagcan be tolerated, as long as the user sees a consistent picture fromsubsequent requests.This is done by saving the master binlog positionin the session, and then at the start of each request, waiting for theslave to catch up to that position before doing any reads from it.Ifthis wait times out, reads are allowed anyway, but the request isconsidered to be in"lagged slave mode".Lagged slave mode can bechecked by calling wfGetLB() ->getLaggedSlaveMode().The onlypractical consequence at present is a warning displayed in the pagefooter.===Lag avoidance===To avoid excessive lag, queries which write large numbers of rows shouldbe split up, generally to write one row at a time.Multi-row INSERT...SELECT queries are the worst offenders should be avoided altogether.Instead do the select first and then the insert.===Working with lag===Despite our best efforts, it's not practical to guarantee a low-lagenvironment.Lag will usually be less than one second, but mayoccasionally be up to 30 seconds.For scalability, it's very importantto keep load on the master low, so simply sending all your queries tothe master is not the answer.So when you have a genuine need forup-to-date data, the following approach is advised:1) Do a quick query to the master for a sequence number or timestamp 2) Run the full query on the slave and check if it matches the data you gotfrom the master 3) If it doesn't, run the full query on the masterTo avoid swamping the master every time the slaves lag, use of thisapproach should be kept to a minimum.In most cases you should just readfrom the slave and let the user deal with the delay.------------------------------------------------------------------------Lock contention------------------------------------------------------------------------Due to the high write rate on Wikipedia(and some other wikis), MediaWiki developers need to be very careful to structure their writesto avoid long-lasting locks.By default, MediaWiki opens a transactionat the first query, and commits it before the output is sent.Locks willbe held from the time when the query is done until the commit.So youcan reduce lock time by doing as much processing as possible before youdo your write queries.Often this approach is not good enough, and it becomes necessary toenclose small groups of queries in their own transaction.Use thefollowing syntax:$dbw=wfGetDB(DB_MASTER
initialiseVariables()
initialise the magic variables (like CURRENTMONTHNAME) and substitution modifiers ...
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Set options of the Parser.
static tidy($text)
Interface with html tidy.
getFunctionHooks()
Get all registered function hook identifiers.
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
database rows
wfHostname()
Fetch server name for use in error reporting etc.
getFunctionLang()
Get a language object for use in parser functions such as {{FORMATNUM:}}.
processing should stop and the error should be shown to the user * false
argSubstitution($piece, $frame)
Triple brace replacement – used for template arguments.
testSrvus($text, Title $title, ParserOptions $options, $outputType=self::OT_HTML)
strip/replaceVariables/unstrip for preprocessor regression testing
uniqPrefix()
Accessor for mUniqPrefix.
Title($x=null)
Accessor/mutator for the Title object.
SectionProfiler $mProfiler
fetchFileNoRegister($title, $options=[])
Helper function for fetchFileAndTitle.
null for the local wiki Added in
There are three types of nodes:
clearTagHooks()
Remove all tag hooks.
const COLON_STATE_TAGSLASH
static makeSelfLinkObj($nt, $html= '', $query= '', $trail= '', $prefix= '')
Make appropriate markup for a link to the current article.
clearState()
Clear Parser state.
interwikiTransclude($title, $action)
Transclude an interwiki link.
pstPass2($text, $user)
Pre-save transform helper function.
guessLegacySectionNameFromWikiText($text)
Same as guessSectionNameFromWikiText(), but produces legacy anchors instead.
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
Options($x=null)
Accessor/mutator for the ParserOptions object.
it s the revision text itself In either if gzip is the revision text is gzipped $flags
serializeHalfParsedText($text)
Save the parser state required to convert the given half-parsed text to HTML.
replaceLinkHolders(&$text, $options=0)
Replace "" link placeholders with actual links, in the buffer Placeholders created in Link...
doQuotes($text)
Helper function for doAllQuotes()
preprocessToDom($text, $flags=0)
Preprocess some wikitext and return the document tree.
limitationWarn($limitationType, $current= '', $max= '')
Warn the user when a parser limitation is reached Will warn at most once the user per limitation type...
wfUrlencode($s)
We want some things to be included as literal characters in our title URLs for prettiness, which urlencode encodes by default.
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Represents a title within MediaWiki.
static getRandomString()
Get a random string.
static stripAllTags($text)
Take a fragment of (potentially invalid) HTML and return a version with any tags removed, encoded as plain text.
when a variable name is used in a it is silently declared as a new local masking the global
doBlockLevels($text, $linestart)
#@-
OutputType($x=null)
Accessor/mutator for the output type.
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target...
const COLON_STATE_COMMENTDASHDASH
getVariableValue($index, $frame=false)
Return value of a magic variable (like PAGENAME)
recursiveTagParse($text, $frame=false)
Half-parse wikitext to half-parsed HTML.
MagicWordArray $mVariables
static validateTagAttributes($attribs, $element)
Take an array of attribute names and values and normalize or discard illegal values for the given ele...
const COLON_STATE_COMMENTDASH
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest object
wfRandomString($length=32)
Get a random string containing a number of pseudo-random hex characters.
preprocess($text, Title $title=null, ParserOptions $options, $revid=null, $frame=false)
Expand templates and variables in the text, producing valid, static wikitext.
static getCacheTTL($id)
Allow external reads of TTL array.
getRevisionId()
Get the ID of the revision we are parsing.
see documentation in includes Linker php for Linker::makeImageLink & $time
maybeDoSubpageLink($target, &$text)
Handle link to subpage if necessary.
If you want to remove the page from your watchlist later
replaceLinkHoldersText($text)
Replace "" link placeholders with plain text of links (not HTML-formatted).
wfDebug($text, $dest= 'all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
static createAssocArgs($args)
Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message.Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item.Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page.Return false to stop further processing of the tag $reader:XMLReader object &$pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision.Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag.Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload.Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports.&$fullInterwikiPrefix:Interwiki prefix, may contain colons.&$pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable.Can be used to lazy-load the import sources list.&$importSources:The value of $wgImportSources.Modify as necessary.See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page.$context:IContextSource object &$pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect.&$title:Title object for the current page &$request:WebRequest &$ignoreRedirect:boolean to skip redirect check &$target:Title/string of redirect target &$article:Article object 'InternalParseBeforeLinks':during Parser's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InternalParseBeforeSanitize':during Parser's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings.Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not.Return true without providing an interwiki to continue interwiki search.$prefix:interwiki prefix we are looking for.&$iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user's email has been invalidated successfully.$user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification.Callee may modify $url and $query, URL will be constructed as $url.$query &$url:URL to index.php &$query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) &$article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() &$ip:IP being check &$result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from &$allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn't match your organization.$addr:The e-mail address entered by the user &$result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user &$result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we're looking for a messages file for &$file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED!Use $magicWords in a file listed in $wgExtensionMessagesFiles instead.Use this to define synonyms of magic words depending of the language &$magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces.Do not use this hook to add namespaces.Use CanonicalNamespaces for that.&$namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED!Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead.Use to define aliases of special pages names depending of the language &$specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names.&$names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page's language links.This is called in various places to allow extensions to define the effective language links for a page.$title:The page's Title.&$links:Associative array mapping language codes to prefixed links of the form"language:title".&$linkFlags:Associative array mapping prefixed links to arrays of flags.Currently unused, but planned to provide support for marking individual language links in the UI, e.g.for featured articles. 'LanguageSelector':Hook to change the language selector available on a page.$out:The output page.$cssClassName:CSS class name of the language selector. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts.Return false to skip default processing and return $ret.See documentation for Linker::link() for details on the expected meanings of parameters.$skin:the Skin object $target:the Title that the link is pointing to &$html:the contents that the< a > tag should have(raw HTML) $result
the value to return A Title object or null for latest to be modified or replaced by the hook handler or if authentication is not possible after cache objects are set for highlighting & $link
static getLocalInstance($ts=false)
Get a timestamp instance in the server local timezone ($wgLocaltimezone)
static getDoubleUnderscoreArray()
Get a MagicWordArray of double-underscore entities.
static splitTrail($trail)
Split a link trail, return the "inside" portion and the remainder of the trail as a two-element array...
getTemplateDom($title)
Get the semi-parsed DOM representation of a template with a given title, and its redirect destination...
static decodeCharReferences($text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string...
openList($char)
These next three functions open, continue, and close the list element appropriate to the prefix chara...
cleanSig($text, $parsing=false)
Clean up signature text.
wfTimestamp($outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
static factory($mode=false, IContextSource $context=null)
Get a new image gallery.
$wgLanguageCode
Site language code.
Custom PHP profiler for parser/DB type section names that xhprof/xdebug can't handle.
Class for asserting that a callback happens when an dummy object leaves scope.
$wgExtraInterlanguageLinkPrefixes
List of additional interwiki prefixes that should be treated as interlanguage links (i...
startExternalParse(Title $title=null, ParserOptions $options, $outputType, $clearState=true)
Set up some variables which are usually set up in parse() so that an external function can call some ...
wfCgiToArray($query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
wfDebugLog($logGroup, $text, $dest= 'all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not...
static capturePath(Title $title, IContextSource $context)
Just like executePath() but will override global variables and execute the page in "inclusion" mode...
addTrackingCategory($msg)
replaceInternalLinks($s)
Process [[ ]] wikilinks.
$wgStylePath
The URL path of the skins directory.
disableCache()
Set a flag in the output object indicating that the content is dynamic and shouldn't be cached...
static normalizeSectionNameWhitespace($section)
Normalizes whitespace in a section name, such as might be returned by Parser::stripSectionName(), for use in the id's that are used for section links.
internalParse($text, $isMain=true, $frame=false)
Helper function for parse() that transforms wiki markup into half-parsed HTML.
static delimiterReplace($startDelim, $endDelim, $replace, $subject, $flags= '')
Perform an operation equivalent to preg_replace() with flags.
__destruct()
Reduce memory usage to reduce the impact of circular references.
wfEscapeWikiText($text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
getRevisionTimestamp()
Get the timestamp associated with the current revision, adjusted for the default server-local timesta...
static stripOuterParagraph($html)
Strip outer.
static singleton()
Get an instance of this class.
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an and does all the work of translating among various forms such as plain database key
static normalizeSubpageLink($contextTitle, $target, &$text)
parseWidthParam($value)
Parsed a width param of imagelink like 300px or 200x300px.
fetchScaryTemplateMaybeFromCache($url)
fetchCurrentRevisionOfTitle($title)
Fetch the current revision of a given title.
stripAltText($caption, $holders)
doAllQuotes($text)
Replace single quotes with HTML markup.
static replaceMarkup($search, $replace, $text)
More or less "markup-safe" str_replace() Ignores any instances of the separator inside <...
static normalizeUrlComponent($component, $unsafe)
const VERSION
Update this version number when the ParserOutput format changes in an incompatible way...
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
setHook($tag, $callback)
Create an HTML-style tag, e.g.
Preprocessor $mPreprocessor
getPreprocessor()
Get a preprocessor object.
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same so they can t rely on Unix and must forbid reads to even standard directories like tmp lest users read each others files We cannot assume that the user has the ability to install or run any programs not written as web accessible PHP scripts Since anything that works on cheap shared hosting will work if you have shell or root access MediaWiki s design is based around catering to the lowest common denominator Although we support higher end setups as the way many things work by default is tailored toward shared hosting These defaults are unconventional from the point of view of normal(non-web) applications--they might conflict with distributors'policies
static getInstance($ts=false)
Get a timestamp instance in GMT.
static singleton()
Get a RepoGroup instance.
replaceVariables($text, $frame=false, $argsOnly=false)
Replace magic variables, templates, and template arguments with the appropriate text.
wfMatchesDomainList($url, $domains)
Check whether a given URL has a domain that occurs in a given set of domains.
getUser()
Get a User object either from $this->mUser, if set, or from the ParserOptions object otherwise...
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
incrementIncludeSize($type, $size)
Increment an include size counter.
getStripList()
Get a list of strippable XML-like elements.
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock()-offset Set to overwrite offset parameter in $wgRequest set to ''to unsetoffset-wrap String Wrap the message in html(usually something like"<
startParse(Title $title=null, ParserOptions $options, $outputType, $clearState=true)
static makeHeadline($level, $attribs, $anchor, $html, $link, $legacyAnchor=false)
Create a headline for content.
static extractTagsAndParams($elements, $text, &$matches, $uniq_prefix=null)
Replaces all occurrences of HTML-style comments and the given tags in the text with a random marker a...
and(b) You must cause any modified files to carry prominent notices stating that You changed the files
doTableStuff($text)
parse the wiki syntax used to render tables
wfDeprecated($function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
getRevisionSize()
Get the size of the revision.
LinkHolderArray $mLinkHolders
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a save
Some information about database access in MediaWiki By Tim January Database layout For information about the MediaWiki database such as a description of the tables and their please see
closeParagraph()
#@+ Used by doBlockLevels()
preSaveTransform($text, Title $title, User $user, ParserOptions $options, $clearState=true)
Transform wiki markup when saving a page by doing "\\r\\n" -> "\\n" conversion, substituting signatur...
getTargetLanguage()
Get the target language for the content being parsed.
namespace and then decline to actually register it file or subcat img or subcat $title
static hasSubpages($index)
Does the namespace allow subpages?
formatHeadings($text, $origText, $isMain=true)
This function accomplishes several tasks: 1) Auto-number headings if that option is enabled 2) Add an...
getConverterLanguage()
Get the language object for language conversion.
static tocUnindent($level)
Finish one or more sublevels on the Table of Contents.
nextItem($char)
TODO: document.
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
static tocLine($anchor, $tocline, $tocnumber, $level, $sectionIndex=false)
parameter level defines if we are on an indentation level
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
getExternalLinkAttribs($url=false)
Get an associative array of additional HTML attributes appropriate for a particular external link...
magicword txt Magic Words are some phrases used in the wikitext They are used for two things
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books $tag
getUserSig(&$user, $nickname=false, $fancySig=null)
Fetch the user's signature text, if any, and normalize to validated, ready-to-insert wikitext...
const HALF_PARSED_VERSION
Update this version number when the output of serialiseHalfParsedText() changes in an incompatible wa...
firstCallInit()
Do various kinds of initialisation on the first call of the parser.
Handles a simple LRU key/value map with a maximum number of entries.
static makeImageLink(Parser $parser, Title $title, $file, $frameParams=[], $handlerParams=[], $time=false, $query="", $widthOption=null)
Given parameters derived from [[Image:Foo|options...]], generate the HTML that that syntax inserts in...
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped broken
armorLinks($text)
Insert a NOPARSE hacky thing into any inline links in a chunk that's going to go through further pars...
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
static splitWhitespace($s)
Return a three-element array: leading whitespace, string contents, trailing whitespace.
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
setOutputType($ot)
Set the output type.
Class for handling an array of magic words.
static & get($id)
Factory: creates an object representing an ID.
static getModuleStyles()
Get CSS modules needed if HTML from the current driver is to be displayed.
enableOOUI()
Set's up the PHP implementation of OOUI for use in this request and instructs OutputPage to enable OO...
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
fetchTemplate($title)
Fetch the unparsed text of a template and register a reference to it.
maybeMakeExternalImage($url)
make an image if it's allowed, either through the global option, through the exception, or through the on-wiki whitelist
areSubpagesAllowed()
Return true if subpage links should be expanded on this page.
static escapeId($id, $options=[])
Given a value, escape it so that it can be used in an id attribute and return it. ...
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object & $output
static getSubstIDs()
Get an array of parser substitution modifier IDs.
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
transformMsg($text, $options, $title=null)
Wrapper for preprocess()
static newFromId($id, $flags=0)
Load a page revision from a given revision ID number.
wfUrlProtocols($includeProtocolRelative=true)
Returns a regular expression of url protocols.
static makeExternalLink($url, $text, $escape=true, $linktype= '', $attribs=[], $title=null)
Make an external link.
__clone()
Allow extensions to clean up when the parser is cloned.
static getExternalLinkRel($url=false, $title=null)
Get the rel attribute for a particular external link.
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
wfSetVar(&$dest, $source, $force=false)
Sets dest to source and returns the original value of dest If source is NULL, it just returns the val...
this hook is for auditing only $req
this hook is for auditing only or null if authentication failed before getting that far $username
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc next in line in page history
array $mLangLinkLanguages
Array with the language name of each language link (i.e.
replaceTransparentTags($text)
Replace transparent tags in $text with the values given by the callbacks.
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres as and are nearing end of but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN type
replaceSection($oldText, $sectionId, $newText)
This function returns $oldtext after the content of the section specified by $section has been replac...
getLinkURL($query= '', $query2=false, $proto=PROTO_RELATIVE)
Get a URL that's the simplest URL that will be valid to link, locally, to the current Title...
doDoubleUnderscore($text)
Strip double-underscore items like NOGALLERY and NOTOC Fills $this->mDoubleUnderscores, returns the modified text.
testPreprocess($text, Title $title, ParserOptions $options)
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global then executing the whole list after the page is displayed We don t do anything smart like collating updates to the same table or such because the list is almost always going to have just one item on if so it s not worth the trouble Since there is a job queue in the jobs table
MagicWordArray $mSubstWords
static normalizeCharReferences($text)
Ensure that any entities and character references are legal for XML and XHTML specifically.
callParserFunction($frame, $function, array $args=[])
Call a parser function and return an array with text and flags.
$wgScriptPath
The path we should point to.
Variant of the Message class.
getFreshParser()
Return this parser if it is not doing anything, otherwise get a fresh parser.
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an and does all the work of translating among various forms such as plain database etc For and for historical it also represents a few features of articles that don t involve their such as access rights See also title txt Article Encapsulates access to the page table of the database The object represents a an and maintains state such as etc Revision Encapsulates individual page revision data and access to the revision text blobs storage system Higher level code should never touch text storage directly
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content $content
const COLON_STATE_TAGSTART
lock()
Lock the current instance of the parser.
const COLON_STATE_COMMENT
static statelessFetchTemplate($title, $parser=false)
Static function to get a template Can be overridden via ParserOptions::setTemplateCallback().
I won t presume to tell you how to I m just describing the methods I chose to use for myself If you do choose to follow these it will probably be easier for you to collaborate with others on the but if you want to contribute without by all means do which work well I also use K &R brace matching style I know that s a religious issue for so if you want to use a style that puts opening braces on the next line
setFunctionHook($id, $callback, $flags=0)
Create a function, e.g.
static setupOOUI($skinName= '', $dir= 'ltr')
Helper function to setup the PHP implementation of OOUI to use in this request.
static makeMediaLinkFile(Title $title, $file, $html= '')
Create a direct link to a given uploaded file.
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template to be included in the link
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
getTitle()
Accessor for the Title object.
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the local content language as $wgContLang
extractSections($text, $sectionId, $mode, $newText= '')
Break wikitext input into sections, and either pull or replace some particular section's text...
getOutput()
Get the ParserOutput object.
$wgExperimentalHtmlIds
Should we allow a broader set of characters in id attributes, per HTML5? If not, use only HTML 4-comp...
static statelessFetchRevision($title, $parser=false)
Wrapper around Revision::newFromTitle to allow passing additional parameters without passing them on ...
doMagicLinks($text)
Replace special strings like "ISBN xxx" and "RFC xxx" with magic external links.
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable or merely the Work and Derivative Works thereof Contribution shall mean any work of including the original version of the Work and any modifications or additions to that Work or Derivative Works that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner For the purposes of this submitted means any form of or written communication sent to the Licensor or its including but not limited to communication on electronic mailing source code control and issue tracking systems that are managed or on behalf the Licensor for the purpose of discussing and improving the but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as Not a Contribution Contributor shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work Grant of Copyright License Subject to the terms and conditions of this each Contributor hereby grants to You a non no royalty irrevocable copyright license to prepare Derivative Works publicly display
getCommon($st1, $st2)
getCommon() returns the length of the longest common substring of both arguments, starting at the beg...
!html< table >< tr >< td > broken</td ></tr ></table >!end!test Table cell attributes
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
static cleanSigInSig($text)
Strip 3, 4 or 5 tildes out of signatures.
setDefaultSort($sort)
Mutator for $mDefaultSort.
fetchFile($title, $options=[])
Fetch a file and its title and register a reference to it.
static fixTagAttributes($text, $element)
Take a tag soup fragment listing an HTML element's attributes and normalize it to well-formed XML...
static tocIndent()
Add another level to the Table of Contents.
static legalChars()
Get a regex character class describing the legal characters in a link.
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling output() to send it all.It could be easily changed to send incrementally if that becomes useful
$wgServer
URL of the server.
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going on
incrementExpensiveFunctionCount()
Increment the expensive function count.
getDisableTitleConversion()
static normalizeLinkUrl($url)
Replace unusual escape codes in a URL with their equivalent characters.
static removeHTMLtags($text, $processCallback=null, $args=[], $extratags=[], $removetags=[])
Cleans up HTML, removes dangerous tags and attributes, and removes HTML comments. ...
insertStripItem($text)
Add an item to the strip state Returns the unique tag which must be inserted into the stripped text T...
testPst($text, Title $title, ParserOptions $options)
static factory($url, $options=null, $caller=__METHOD__)
Generate a new request object.
const TS_UNIX
Unix time - the number of seconds since 1970-01-01 00:00:00 UTC.
if(!$wgRequest->checkUrlExtension()) if(!$wgEnableAPI) $wgTitle
static explode($separator, $subject)
Workalike for explode() with limited memory usage.
parse($text, Title $title, ParserOptions $options, $linestart=true, $clearState=true, $revid=null)
Convert wikitext to HTML Do not call this function recursively.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
static numberingroup($group)
Find the number of users in a given user group.
=Architecture==Two class hierarchies are used to provide the functionality associated with the different content models:*Content interface(and AbstractContent base class) define functionality that acts on the concrete content of a page, and *ContentHandler base class provides functionality specific to a content model, but not acting on concrete content.The most important function of ContentHandler is to act as a factory for the appropriate implementation of Content.These Content objects are to be used by MediaWiki everywhere, instead of passing page content around as text.All manipulation and analysis of page content must be done via the appropriate methods of the Content object.For each content model, a subclass of ContentHandler has to be registered with $wgContentHandlers.The ContentHandler object for a given content model can be obtained using ContentHandler::getForModelID($id).Also Title, WikiPage and Revision now have getContentHandler() methods for convenience.ContentHandler objects are singletons that provide functionality specific to the content type, but not directly acting on the content of some page.ContentHandler::makeEmptyContent() and ContentHandler::unserializeContent() can be used to create a Content object of the appropriate type.However, it is recommended to instead use WikiPage::getContent() resp.Revision::getContent() to get a page's content as a Content object.These two methods should be the ONLY way in which page content is accessed.Another important function of ContentHandler objects is to define custom action handlers for a content model, see ContentHandler::getActionOverrides().This is similar to what WikiPage::getActionOverrides() was already doing.==Serialization==With the ContentHandler facility, page content no longer has to be text based.Objects implementing the Content interface are used to represent and handle the content internally.For storage and data exchange, each content model supports at least one serialization format via ContentHandler::serializeContent($content).The list of supported formats for a given content model can be accessed using ContentHandler::getSupportedFormats().Content serialization formats are identified using MIME type like strings.The following formats are built in:*text/x-wiki-wikitext *text/javascript-for js pages *text/css-for css pages *text/plain-for future use, e.g.with plain text messages.*text/html-for future use, e.g.with plain html messages.*application/vnd.php.serialized-for future use with the api and for extensions *application/json-for future use with the api, and for use by extensions *application/xml-for future use with the api, and for use by extensions In PHP, use the corresponding CONTENT_FORMAT_XXX constant.Note that when using the API to access page content, especially action=edit, action=parse and action=query &prop=revisions, the model and format of the content should always be handled explicitly.Without that information, interpretation of the provided content is not reliable.The same applies to XML dumps generated via maintenance/dumpBackup.php or Special:Export.Also note that the API will provide encapsulated, serialized content-so if the API was called with format=json, and contentformat is also json(or rather, application/json), the page content is represented as a string containing an escaped json structure.Extensions that use JSON to serialize some types of page content may provide specialized API modules that allow access to that content in a more natural form.==Compatibility==The ContentHandler facility is introduced in a way that should allow all existing code to keep functioning at least for pages that contain wikitext or other text based content.However, a number of functions and hooks have been deprecated in favor of new versions that are aware of the page's content model, and will now generate warnings when used.Most importantly, the following functions have been deprecated:*Revisions::getText() is deprecated in favor Revisions::getContent()*WikiPage::getText() is deprecated in favor WikiPage::getContent() Also, the old Article::getContent()(which returns text) is superceded by Article::getContentObject().However, both methods should be avoided since they do not provide clean access to the page's actual content.For instance, they may return a system message for non-existing pages.Use WikiPage::getContent() instead.Code that relies on a textual representation of the page content should eventually be rewritten.However, ContentHandler::getContentText() provides a stop-gap that can be used to get text for a page.Its behavior is controlled by $wgContentHandlerTextFallback it
static getVersion($flags= '', $lang=null)
Return a string of the MediaWiki version with Git revision if available.
braceSubstitution($piece, $frame)
Return the text of a template, after recursively replacing any variables or templates within the temp...
setUser($user)
Set the current user.
makeImage($title, $options, $holders=false)
Parse image options text and use it to make an image.
attributeStripCallback(&$text, $frame=false)
Callback from the Sanitizer for expanding items found in HTML attribute values, so they can be safely...
static cascadingsources($parser, $title= '')
Returns the sources of any cascading protection acting on a specified page.
getCustomDefaultSort()
Accessor for $mDefaultSort Unlike getDefaultSort(), will return false if none is set.
extensionSubstitution($params, $frame)
Return the text to be used for a given extension tag.
static makeExternalImage($url, $alt= '')
Return the code for images which were added via external links, via Parser::maybeMakeExternalImage()...
recursiveTagParseFully($text, $frame=false)
Fully parse wikitext to fully parsed HTML.
setTransparentTagHook($tag, $callback)
As setHook(), but letting the contents be parsed.
static element($element, $attribs=[], $contents= '')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
wfFindFile($title, $options=[])
Find a file.
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk page
unserializeHalfParsedText($data)
Load the parser state given in the $data array, which is assumed to have been generated by serializeH...
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
guessSectionNameFromWikiText($text)
Try to guess the section anchor name based on a wikitext fragment presumably extracted from a heading...
static & makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
$wgServerName
Server name.
internalParseHalfParsed($text, $isMain=true, $linestart=true)
Helper function for parse() that transforms half-parsed HTML into fully parsed HTML.
if the prop value should be in the metadata multi language array format
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing & $attribs
controlled by $wgMainCacheType controlled by $wgParserCacheType controlled by $wgMessageCacheType If you set CACHE_NONE to one of the three control variable
getOptions()
Get the ParserOptions object.
makeKnownLinkHolder($nt, $text= '', $query=[], $trail= '', $prefix= '')
Render a forced-blue link inline; protect against double expansion of URLs if we're in a mode that pr...
getDefaultSort()
Accessor for $mDefaultSort Will use the empty string if none is set.
For a write use something like
makeFreeExternalLink($url, $numPostProto)
Make a free external link, given a user-supplied URL.
const COLON_STATE_CLOSETAG
Allows to change the fields on the form that will be generated $name