215 'speculativePageIdUsed',
217 'revisionTimestampUsed'
239 '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#s';
259 public function __construct( $text =
'', $languageLinks = [], $categoryLinks = [],
260 $unused =
false, $titletext =
''
262 $this->mText = $text;
263 $this->mLanguageLinks = $languageLinks;
264 $this->mCategories = $categoryLinks;
265 $this->mTitleText = $titletext;
279 return ( $this->mText !==
null );
291 if ( $this->mText ===
null ) {
292 throw new LogicException(
'This ParserOutput contains no text!' );
326 'enableSectionEditLinks' =>
true,
328 'deduplicateStyles' =>
true,
333 Hooks::runWithoutAbort(
'ParserOutputPostCacheTransform', [ $this, &$text, &$options ] );
335 if ( $options[
'wrapperDivClass'] !==
'' && !$options[
'unwrap'] ) {
336 $text = Html::rawElement(
'div', [
'class' => $options[
'wrapperDivClass'] ], $text );
339 if ( $options[
'enableSectionEditLinks'] ) {
340 $text = preg_replace_callback(
341 self::EDITSECTION_REGEX,
343 $editsectionPage = Title::newFromText( htmlspecialchars_decode( $m[1] ) );
344 $editsectionSection = htmlspecialchars_decode( $m[2] );
345 $editsectionContent = isset( $m[4] ) ? Sanitizer::decodeCharReferences( $m[3] ) :
null;
347 if ( !is_object( $editsectionPage ) ) {
348 throw new MWException(
"Bad parser output text." );
351 $context = RequestContext::getMain();
352 return $context->getSkin()->doEditSectionLink(
362 $text = preg_replace( self::EDITSECTION_REGEX,
'', $text );
365 if ( $options[
'allowTOC'] ) {
366 $text = str_replace( [ Parser::TOC_START, Parser::TOC_END ],
'', $text );
368 $text = preg_replace(
369 '#' . preg_quote( Parser::TOC_START,
'#' ) .
'.*?' . preg_quote( Parser::TOC_END,
'#' ) .
'#s',
375 if ( $options[
'deduplicateStyles'] ) {
377 $text = preg_replace_callback(
378 '#<style\s+([^>]*data-mw-deduplicate\s*=[^>]*)>.*?</style>#s',
379 function ( $m ) use ( &$seen ) {
380 $attr = Sanitizer::decodeTagAttributes( $m[1] );
381 if ( !isset( $attr[
'data-mw-deduplicate'] ) ) {
385 $key = $attr[
'data-mw-deduplicate'];
386 if ( !isset( $seen[$key] ) ) {
395 return Html::element(
'link', [
396 'rel' =>
'mw-deduplicated-inline-style',
405 $text = preg_replace_callback(
406 '#<mw:slotheader>(.*?)</mw:slotheader>#',
408 $role = htmlspecialchars_decode( $m[1] );
424 $this->mWrapperDivClasses[$class] =
true;
432 $this->mWrapperDivClasses = [];
443 return implode(
' ', array_keys( $this->mWrapperDivClasses ) );
451 $this->mSpeculativeRevId = $id;
459 return $this->mSpeculativeRevId;
467 $this->speculativePageIdUsed = $id;
475 return $this->speculativePageIdUsed;
483 $this->revisionTimestampUsed = $timestamp;
491 return $this->revisionTimestampUsed;
499 if ( $hash ===
null ) {
504 $this->revisionUsedSha1Base36 !==
null &&
505 $this->revisionUsedSha1Base36 !== $hash
507 $this->revisionUsedSha1Base36 =
'';
509 $this->revisionUsedSha1Base36 = $hash;
518 return $this->revisionUsedSha1Base36;
522 return $this->mLanguageLinks;
526 return $this->mInterwikiLinks;
530 return array_keys( $this->mCategories );
534 return $this->mCategories;
542 return $this->mIndicators;
546 return $this->mTitleText;
550 return $this->mSections;
554 return $this->mLinks;
558 return $this->mTemplates;
562 return $this->mTemplateIds;
566 return $this->mImages;
570 return $this->mFileSearchOptions;
574 return $this->mExternalLinks;
578 $this->mNoGallery = (bool)$value;
582 return $this->mNoGallery;
586 return $this->mHeadItems;
590 return $this->mModules;
594 return $this->mModuleStyles;
602 return $this->mJsConfigVars;
606 return (array)$this->mOutputHooks;
610 return array_keys( $this->mWarnings );
614 return $this->mIndexPolicy;
618 return $this->mTOCHTML;
625 return $this->mTimestamp;
629 return $this->mLimitReportData;
633 return $this->mLimitReportJSData;
637 return $this->mEnableOOUI;
641 return wfSetVar( $this->mText, $text );
645 return wfSetVar( $this->mLanguageLinks, $ll );
649 return wfSetVar( $this->mCategories, $cl );
657 return wfSetVar( $this->mSections, $toc );
661 return wfSetVar( $this->mIndexPolicy, $policy );
665 return wfSetVar( $this->mTOCHTML, $tochtml );
669 return wfSetVar( $this->mTimestamp, $timestamp );
673 $this->mCategories[$c] =
$sort;
693 $this->mEnableOOUI = $enable;
697 $this->mLanguageLinks[] =
$t;
701 $this->mWarnings[
$s] = 1;
705 $this->mOutputHooks[] = [ $hook, $data ];
709 $this->mNewSection = (bool)$value;
713 $this->mHideNewSection = (bool)$value;
717 return (
bool)$this->mHideNewSection;
721 return (
bool)$this->mNewSection;
732 return (
bool)preg_match(
'/^' .
733 # If server is proto relative, check also
for http/https links
734 ( substr( $internal, 0, 2 ) ===
'//' ?
'(?:https?:)?' :
'' ) .
735 preg_quote( $internal,
'/' ) .
736 # check
for query/path/anchor or end of link in each
case
743 # We don't register links pointing to our own server, unless... :-)
746 # Replace unnecessary URL escape codes with the referenced character
747 # This prevents spammers from hiding links from the filters
748 $url = Parser::normalizeLinkUrl( $url );
750 $registerExternalLink =
true;
752 $registerExternalLink = !self::isLinkInternal(
$wgServer, $url );
754 if ( $registerExternalLink ) {
755 $this->mExternalLinks[$url] = 1;
766 if (
$title->isExternal() ) {
771 $ns =
$title->getNamespace();
772 $dbk =
$title->getDBkey();
780 } elseif ( $dbk ===
'' ) {
784 if ( !isset( $this->mLinks[$ns] ) ) {
785 $this->mLinks[$ns] = [];
787 if ( is_null( $id ) ) {
788 $id =
$title->getArticleID();
790 $this->mLinks[$ns][$dbk] = $id;
799 public function addImage( $name, $timestamp =
null, $sha1 =
null ) {
800 $this->mImages[$name] = 1;
801 if ( $timestamp !==
null && $sha1 !==
null ) {
802 $this->mFileSearchOptions[$name] = [
'time' => $timestamp,
'sha1' => $sha1 ];
813 $ns =
$title->getNamespace();
814 $dbk =
$title->getDBkey();
815 if ( !isset( $this->mTemplates[$ns] ) ) {
816 $this->mTemplates[$ns] = [];
818 $this->mTemplates[$ns][$dbk] = $page_id;
819 if ( !isset( $this->mTemplateIds[$ns] ) ) {
820 $this->mTemplateIds[$ns] = [];
822 $this->mTemplateIds[$ns][$dbk] = $rev_id;
830 if ( !
$title->isExternal() ) {
831 throw new MWException(
'Non-interwiki link passed, internal parser error.' );
833 $prefix =
$title->getInterwiki();
834 if ( !isset( $this->mInterwikiLinks[$prefix] ) ) {
835 $this->mInterwikiLinks[$prefix] = [];
837 $this->mInterwikiLinks[$prefix][
$title->getDBkey()] = 1;
848 if ( $tag !==
false ) {
849 $this->mHeadItems[$tag] = $section;
851 $this->mHeadItems[] = $section;
859 $this->mModules = array_merge( $this->mModules, (array)
$modules );
866 $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)
$modules );
877 if ( is_array(
$keys ) ) {
878 foreach (
$keys as $key => $value ) {
879 $this->mJsConfigVars[$key] = $value;
884 $this->mJsConfigVars[
$keys] = $value;
918 if (
$title->isSpecialPage() ) {
919 wfDebug( __METHOD__ .
": Not adding tracking category $msg to special page!\n" );
926 ->inContentLanguage()
929 # Allow tracking categories to be disabled by setting them to "-"
930 if ( $cat ===
'-' ) {
934 $containerCategory = Title::makeTitleSafe(
NS_CATEGORY, $cat );
935 if ( $containerCategory ) {
936 $this->
addCategory( $containerCategory->getDBkey(), $this->getProperty(
'defaultsort' ) ?:
'' );
939 wfDebug( __METHOD__ .
": [[MediaWiki:$msg]] is not a valid title!\n" );
982 $this->mFlags[$flag] =
true;
990 return isset( $this->mFlags[$flag] );
998 return array_keys( $this->mFlags );
1062 $this->mProperties[$name] = $value;
1074 return $this->mProperties[$name] ??
false;
1078 unset( $this->mProperties[$name] );
1082 if ( !isset( $this->mProperties ) ) {
1083 $this->mProperties = [];
1085 return $this->mProperties;
1094 if ( !isset( $this->mAccessedOptions ) ) {
1097 return array_keys( $this->mAccessedOptions );
1113 $this->mAccessedOptions[$option] =
true;
1157 if ( $value ===
null ) {
1158 unset( $this->mExtensionData[$key] );
1160 $this->mExtensionData[$key] = $value;
1176 return $this->mExtensionData[$key] ??
null;
1181 if ( !$clock || $clock ===
'wall' ) {
1182 $ret[
'wall'] = microtime(
true );
1184 if ( !$clock || $clock ===
'cpu' ) {
1187 $ret[
'cpu'] = $ru[
'ru_utime.tv_sec'] + $ru[
'ru_utime.tv_usec'] / 1e6;
1188 $ret[
'cpu'] += $ru[
'ru_stime.tv_sec'] + $ru[
'ru_stime.tv_usec'] / 1e6;
1199 $this->mParseStartTime = self::getTimes();
1214 if ( !isset( $this->mParseStartTime[$clock] ) ) {
1218 $end = self::getTimes( $clock );
1219 return $end[$clock] - $this->mParseStartTime[$clock];
1242 $this->mLimitReportData[$key] = $value;
1244 if ( is_array( $value ) ) {
1245 if ( array_keys( $value ) === [ 0, 1 ]
1246 && is_numeric( $value[0] )
1247 && is_numeric( $value[1] )
1249 $data = [
'value' => $value[0],
'limit' => $value[1] ];
1257 if ( strpos( $key,
'-' ) ) {
1258 list( $ns, $name ) = explode(
'-', $key, 2 );
1259 $this->mLimitReportJSData[$ns][$name] = $data;
1261 $this->mLimitReportJSData[$key] = $data;
1289 return wfSetVar( $this->mPreventClickjacking, $flag );
1299 $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry );
1309 if ( is_infinite( $this->mMaxAdaptiveExpiry ) ) {
1314 if ( is_float( $runtime ) ) {
1315 $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL )
1316 / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC );
1318 $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope;
1321 max( $slope * $runtime + $point, self::MIN_AR_TTL ),
1322 $this->mMaxAdaptiveExpiry
1329 return array_filter( array_keys( get_object_vars( $this ) ),
1330 function ( $field ) {
1331 if ( $field ===
'mParseStartTime' ) {
1333 } elseif ( strpos( $field,
"\0" ) !==
false ) {
1352 $this->mOutputHooks = self::mergeList( $this->mOutputHooks,
$source->getOutputHooks() );
1353 $this->mWarnings = self::mergeMap( $this->mWarnings,
$source->mWarnings );
1354 $this->mTimestamp = $this->
useMaxValue( $this->mTimestamp,
$source->getTimestamp() );
1356 foreach ( self::$speculativeFields as $field ) {
1357 if ( $this->$field &&
$source->$field && $this->$field !==
$source->$field ) {
1358 wfLogWarning( __METHOD__ .
": inconsistent '$field' properties!" );
1364 $this->mParseStartTime,
1368 $this->mFlags = self::mergeMap( $this->mFlags,
$source->mFlags );
1369 $this->mAccessedOptions = self::mergeMap( $this->mAccessedOptions,
$source->mAccessedOptions );
1372 if ( empty( $this->mLimitReportData ) ) {
1373 $this->mLimitReportData =
$source->mLimitReportData;
1375 if ( empty( $this->mLimitReportJSData ) ) {
1376 $this->mLimitReportJSData =
$source->mLimitReportJSData;
1389 $this->mHeadItems = self::mergeMixedList( $this->mHeadItems,
$source->getHeadItems() );
1390 $this->mModules = self::mergeList( $this->mModules,
$source->getModules() );
1391 $this->mModuleStyles = self::mergeList( $this->mModuleStyles,
$source->getModuleStyles() );
1392 $this->mJsConfigVars = self::mergeMap( $this->mJsConfigVars,
$source->getJsConfigVars() );
1393 $this->mMaxAdaptiveExpiry = min( $this->mMaxAdaptiveExpiry,
$source->mMaxAdaptiveExpiry );
1396 if ( $this->mIndexPolicy ===
'noindex' ||
$source->mIndexPolicy ===
'noindex' ) {
1397 $this->mIndexPolicy =
'noindex';
1398 } elseif ( $this->mIndexPolicy !==
'index' ) {
1399 $this->mIndexPolicy =
$source->mIndexPolicy;
1403 $this->mNewSection = $this->mNewSection ||
$source->getNewSection();
1404 $this->mHideNewSection = $this->mHideNewSection ||
$source->getHideNewSection();
1405 $this->mNoGallery = $this->mNoGallery ||
$source->getNoGallery();
1406 $this->mEnableOOUI = $this->mEnableOOUI ||
$source->getEnableOOUI();
1407 $this->mPreventClickjacking = $this->mPreventClickjacking ||
$source->preventClickjacking();
1410 $this->mSections = array_merge( $this->mSections,
$source->getSections() );
1411 $this->mTOCHTML .=
$source->mTOCHTML;
1415 if ( $this->mTitleText ===
null || $this->mTitleText ===
'' ) {
1416 $this->mTitleText =
$source->mTitleText;
1420 $this->mWrapperDivClasses = self::mergeMap(
1421 $this->mWrapperDivClasses,
1426 $this->mIndicators = self::mergeMap( $this->mIndicators,
$source->getIndicators() );
1431 $this->mExtensionData = self::mergeMap(
1432 $this->mExtensionData,
1445 $this->mLanguageLinks = self::mergeList( $this->mLanguageLinks,
$source->getLanguageLinks() );
1446 $this->mCategories = self::mergeMap( $this->mCategories,
$source->getCategories() );
1447 $this->mLinks = self::merge2D( $this->mLinks,
$source->getLinks() );
1448 $this->mTemplates = self::merge2D( $this->mTemplates,
$source->getTemplates() );
1449 $this->mTemplateIds = self::merge2D( $this->mTemplateIds,
$source->getTemplateIds() );
1450 $this->mImages = self::mergeMap( $this->mImages,
$source->getImages() );
1451 $this->mFileSearchOptions = self::mergeMap(
1452 $this->mFileSearchOptions,
1453 $source->getFileSearchOptions()
1455 $this->mExternalLinks = self::mergeMap( $this->mExternalLinks,
$source->getExternalLinks() );
1456 $this->mInterwikiLinks = self::merge2D(
1457 $this->mInterwikiLinks,
1463 $this->mProperties = self::mergeMap( $this->mProperties,
$source->getProperties() );
1468 $this->mExtensionData = self::mergeMap(
1469 $this->mExtensionData,
1475 return array_unique( array_merge( $a, $b ), SORT_REGULAR );
1479 return array_values( array_unique( array_merge( $a, $b ), SORT_REGULAR ) );
1482 private static function mergeMap( array $a, array $b ) {
1483 return array_replace( $a, $b );
1486 private static function merge2D( array $a, array $b ) {
1488 $keys = array_merge( array_keys( $a ), array_keys( $b ) );
1490 foreach (
$keys as $k ) {
1491 if ( empty( $a[$k] ) ) {
1492 $values[$k] = $b[$k];
1493 } elseif ( empty( $b[$k] ) ) {
1494 $values[$k] = $a[$k];
1495 } elseif ( is_array( $a[$k] ) && is_array( $b[$k] ) ) {
1496 $values[$k] = array_replace( $a[$k], $b[$k] );
1498 $values[$k] = $b[$k];
1507 $keys = array_merge( array_keys( $a ), array_keys( $b ) );
1509 foreach (
$keys as $k ) {
1510 if ( is_array( $a[$k] ??
null ) && is_array( $b[$k] ??
null ) ) {
1511 $values[$k] = self::useEachMinValue( $a[$k], $b[$k] );
1513 $values[$k] = self::useMinValue( $a[$k] ??
null, $b[$k] ??
null );
1521 if ( $a ===
null ) {
1525 if ( $b ===
null ) {
1529 return min( $a, $b );
1533 if ( $a ===
null ) {
1537 if ( $b ===
null ) {
1541 return max( $a, $b );
$wgRegisterInternalExternals
By default MediaWiki does not register links pointing to same server in externallinks dataset,...
$wgParserCacheExpireTime
The expiry time for the parser cache, in seconds.
$wgServer
URL of the server.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
wfGetRusage()
Get system resource usage of current request context.
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...
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Parser cache specific expiry check.
updateCacheExpiry( $seconds)
Sets the number of seconds after which this object should expire.
getCacheExpiry()
Returns the number of seconds after which this object should expire.
This is one of the Core classes and should be read at least once by any new developers.
getJsConfigVars()
Get the javascript config vars to include on this page.
getHeadItemsArray()
Get an array of head items.
getModules( $filter=false, $position=null, $param='mModules', $type=ResourceLoaderModule::TYPE_COMBINED)
Get the list of modules to include on this page.
getPreventClickjacking()
Get the prevent-clickjacking flag.
getModuleStyles( $filter=false, $position=null)
Get the list of style-only modules to load on this page.
static mergeMap(array $a, array $b)
addOutputPageMetadata(OutputPage $out)
Copy items from the OutputPage object into this one.
static getTimes( $clock=null)
getSpeculativeRevIdUsed()
int null $mSpeculativeRevId
Assumed rev ID for {{REVISIONID}} if no revision is set.
addOutputHook( $hook, $data=false)
const SUPPORTS_UNWRAP_TRANSFORM
mergeHtmlMetaDataFrom(ParserOutput $source)
Merges HTML metadata such as head items, JS config vars, and HTTP cache control info from $source int...
static useEachMinValue(array $a, array $b)
setDisplayTitle( $text)
Override the title to be used for display.
addJsConfigVars( $keys, $value=null)
Add one or more variables to be set in mw.config in JavaScript.
setIndicator( $id, $content)
static useMaxValue( $a, $b)
setRevisionUsedSha1Base36( $hash)
static merge2D(array $a, array $b)
hasText()
Returns true if text was passed to the constructor, or set using setText().
static mergeList(array $a, array $b)
addInterwikiLink( $title)
setRevisionTimestampUsed( $timestamp)
setEnableOOUI( $enable=false)
Enables OOUI, if true, in any OutputPage instance this ParserOutput object is added to.
$mWrapperDivClasses
string CSS classes to use for the wrapping div, stored in the array keys.
getDisplayTitle()
Get the title to be used for display.
addWrapperDivClass( $class)
Add a CSS class to use for the wrapping div.
addTrackingCategory( $msg, $title)
Add a tracking category, getting the title from a system message, or print a debug message if the tit...
finalizeAdaptiveCacheExpiry()
Call this when parsing is done to lower the TTL based on low parse times.
addTemplate( $title, $page_id, $rev_id)
Register a template dependency for this output.
addLink(Title $title, $id=null)
Record a local or interwiki inline link for saving in future link tables.
resetParseStartTime()
Resets the parse start timestamps for future calls to getTimeSinceStart()
static mergeMixedList(array $a, array $b)
preventClickjacking( $flag=null)
Get or set the prevent-clickjacking flag.
mergeTrackingMetaDataFrom(ParserOutput $source)
Merges dependency tracking metadata such as backlinks, images used, and extension data from $source i...
setProperty( $name, $value)
Set a property to be stored in the page_props database table.
getRevisionTimestampUsed()
recordOption( $option)
Tags a parser option for use in the cache key for this parser output.
addHeadItem( $section, $tag=false)
Add some text to the "<head>".
hasDynamicContent()
Check whether the cache TTL was lowered due to dynamic content.
static useMinValue( $a, $b)
getExtensionData( $key)
Gets extensions data previously attached to this ParserOutput using setExtensionData().
clearWrapperDivClass()
Clears the CSS class to use for the wrapping div, effectively disabling the wrapper div until addWrap...
getText( $options=[])
Get the output HTML.
getTimeSinceStart( $clock)
Returns the time since resetParseStartTime() was last called.
int null $revisionTimestampUsed
Assumed rev timestamp for {{REVISIONTIMESTAMP}} if no revision is set.
getRawText()
Get the cacheable text with <mw:editsection> markers still in it.
addImage( $name, $timestamp=null, $sha1=null)
Register a file dependency for this output.
updateRuntimeAdaptiveExpiry( $ttl)
Lower the runtime adaptive TTL to at most this value.
getRevisionUsedSha1Base36()
getSpeculativePageIdUsed()
getUsedOptions()
Returns the options from its ParserOptions which have been taken into account to produce this output.
const SUPPORTS_STATELESS_TRANSFORMS
Feature flags to indicate to extensions that MediaWiki core supports and uses getText() stateless tra...
setLimitReportData( $key, $value)
Sets parser limit report data for a key.
int null $speculativePageIdUsed
Assumed page ID for {{PAGEID}} if no revision is set.
setExtensionData( $key, $value)
Attaches arbitrary data to this ParserObject.
setTimestamp( $timestamp)
setSpeculativeRevIdUsed( $id)
__construct( $text='', $languageLinks=[], $categoryLinks=[], $unused=false, $titletext='')
setSpeculativePageIdUsed( $id)
static isLinkInternal( $internal, $url)
Checks, if a url is pointing to the own server.
array $mLimitReportJSData
Parser limit report data for JSON.
mergeInternalMetaDataFrom(ParserOutput $source)
Merges internal metadata such as flags, accessed options, and profiling info from $source into this P...
addModuleStyles( $modules)
getWrapperDivClass()
Returns the class (or classes) to be used with the wrapper div for this otuput.
setFlag( $flag)
Attach a flag to the output so that it can be checked later to handle special cases.
static string[] $speculativeFields
string null $revisionUsedSha1Base36
SHA-1 base 36 hash of any self-transclusion.
int $mMaxAdaptiveExpiry
Upper bound of expiry based on parse duration.
Represents a title within MediaWiki.