MediaWiki  master
ParserOutput.php
Go to the documentation of this file.
1 <?php
2 
25 class ParserOutput extends CacheTime {
32 
36  public $mText = null;
37 
43 
47  public $mCategories;
48 
52  public $mIndicators = [];
53 
57  public $mTitleText;
58 
63  public $mLinks = [];
64 
69  public $mTemplates = [];
70 
75  public $mTemplateIds = [];
76 
80  public $mImages = [];
81 
85  public $mFileSearchOptions = [];
86 
90  public $mExternalLinks = [];
91 
96  public $mInterwikiLinks = [];
97 
101  public $mNewSection = false;
102 
106  public $mHideNewSection = false;
107 
111  public $mNoGallery = false;
112 
116  public $mHeadItems = [];
117 
121  public $mModules = [];
122 
126  public $mModuleStyles = [];
127 
131  public $mJsConfigVars = [];
132 
136  public $mOutputHooks = [];
137 
142  public $mWarnings = [];
143 
147  public $mSections = [];
148 
152  public $mProperties = [];
153 
157  public $mTOCHTML = '';
158 
162  public $mTimestamp;
163 
167  public $mEnableOOUI = false;
168 
172  private $mIndexPolicy = '';
173 
177  private $mAccessedOptions = [];
178 
182  private $mExtensionData = [];
183 
187  private $mLimitReportData = [];
188 
190  private $mLimitReportJSData = [];
191 
195  private $mParseStartTime = [];
196 
200  private $mPreventClickjacking = false;
201 
205  private $mFlags = [];
206 
209 
213  private $mWrapperDivClasses = [];
214 
216  private $mMaxAdaptiveExpiry = INF;
217 
219  '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#s';
220 
221  // finalizeAdaptiveCacheExpiry() uses TTL = MAX( m * PARSE_TIME + b, MIN_AR_TTL)
222  // Current values imply that m=3933.333333 and b=-333.333333
223  // See https://www.nngroup.com/articles/website-response-times/
224  const PARSE_FAST_SEC = 0.100; // perceived "fast" page parse
225  const PARSE_SLOW_SEC = 1.0; // perceived "slow" page parse
226  const FAST_AR_TTL = 60; // adaptive TTL for "fast" pages
227  const SLOW_AR_TTL = 3600; // adaptive TTL for "slow" pages
228  const MIN_AR_TTL = 15; // min adaptive TTL (for sanity, pool counter, and edit stashing)
229 
239  public function __construct( $text = '', $languageLinks = [], $categoryLinks = [],
240  $unused = false, $titletext = ''
241  ) {
242  $this->mText = $text;
243  $this->mLanguageLinks = $languageLinks;
244  $this->mCategories = $categoryLinks;
245  $this->mTitleText = $titletext;
246  }
247 
258  public function hasText() {
259  return ( $this->mText !== null );
260  }
261 
270  public function getRawText() {
271  if ( $this->mText === null ) {
272  throw new LogicException( 'This ParserOutput contains no text!' );
273  }
274 
275  return $this->mText;
276  }
277 
303  public function getText( $options = [] ) {
304  $options += [
305  'allowTOC' => true,
306  'enableSectionEditLinks' => true,
307  'unwrap' => false,
308  'deduplicateStyles' => true,
309  'wrapperDivClass' => $this->getWrapperDivClass(),
310  ];
311  $text = $this->getRawText();
312 
313  Hooks::runWithoutAbort( 'ParserOutputPostCacheTransform', [ $this, &$text, &$options ] );
314 
315  if ( $options['wrapperDivClass'] !== '' && !$options['unwrap'] ) {
316  $text = Html::rawElement( 'div', [ 'class' => $options['wrapperDivClass'] ], $text );
317  }
318 
319  if ( $options['enableSectionEditLinks'] ) {
320  $text = preg_replace_callback(
321  self::EDITSECTION_REGEX,
322  function ( $m ) {
323  $editsectionPage = Title::newFromText( htmlspecialchars_decode( $m[1] ) );
324  $editsectionSection = htmlspecialchars_decode( $m[2] );
325  $editsectionContent = isset( $m[4] ) ? Sanitizer::decodeCharReferences( $m[3] ) : null;
326 
327  if ( !is_object( $editsectionPage ) ) {
328  throw new MWException( "Bad parser output text." );
329  }
330 
332  return $context->getSkin()->doEditSectionLink(
333  $editsectionPage,
334  $editsectionSection,
335  $editsectionContent,
336  $context->getLanguage()
337  );
338  },
339  $text
340  );
341  } else {
342  $text = preg_replace( self::EDITSECTION_REGEX, '', $text );
343  }
344 
345  if ( $options['allowTOC'] ) {
346  $text = str_replace( [ Parser::TOC_START, Parser::TOC_END ], '', $text );
347  } else {
348  $text = preg_replace(
349  '#' . preg_quote( Parser::TOC_START, '#' ) . '.*?' . preg_quote( Parser::TOC_END, '#' ) . '#s',
350  '',
351  $text
352  );
353  }
354 
355  if ( $options['deduplicateStyles'] ) {
356  $seen = [];
357  $text = preg_replace_callback(
358  '#<style\s+([^>]*data-mw-deduplicate\s*=[^>]*)>.*?</style>#s',
359  function ( $m ) use ( &$seen ) {
360  $attr = Sanitizer::decodeTagAttributes( $m[1] );
361  if ( !isset( $attr['data-mw-deduplicate'] ) ) {
362  return $m[0];
363  }
364 
365  $key = $attr['data-mw-deduplicate'];
366  if ( !isset( $seen[$key] ) ) {
367  $seen[$key] = true;
368  return $m[0];
369  }
370 
371  // We were going to use an empty <style> here, but there
372  // was concern that would be too much overhead for browsers.
373  // So let's hope a <link> with a non-standard rel and href isn't
374  // going to be misinterpreted or mangled by any subsequent processing.
375  return Html::element( 'link', [
376  'rel' => 'mw-deduplicated-inline-style',
377  'href' => "mw-data:" . wfUrlencode( $key ),
378  ] );
379  },
380  $text
381  );
382  }
383 
384  // Hydrate slot section header placeholders generated by RevisionRenderer.
385  $text = preg_replace_callback(
386  '#<mw:slotheader>(.*?)</mw:slotheader>#',
387  function ( $m ) {
388  $role = htmlspecialchars_decode( $m[1] );
389  // TODO: map to message, using the interface language. Set lang="xyz" accordingly.
390  $headerText = $role;
391  return $headerText;
392  },
393  $text
394  );
395  return $text;
396  }
397 
403  public function addWrapperDivClass( $class ) {
404  $this->mWrapperDivClasses[$class] = true;
405  }
406 
411  public function clearWrapperDivClass() {
412  $this->mWrapperDivClasses = [];
413  }
414 
422  public function getWrapperDivClass() {
423  return implode( ' ', array_keys( $this->mWrapperDivClasses ) );
424  }
425 
430  public function setSpeculativeRevIdUsed( $id ) {
431  $this->mSpeculativeRevId = $id;
432  }
433 
438  public function getSpeculativeRevIdUsed() {
440  }
441 
442  public function &getLanguageLinks() {
443  return $this->mLanguageLinks;
444  }
445 
446  public function getInterwikiLinks() {
447  return $this->mInterwikiLinks;
448  }
449 
450  public function getCategoryLinks() {
451  return array_keys( $this->mCategories );
452  }
453 
454  public function &getCategories() {
455  return $this->mCategories;
456  }
457 
462  public function getIndicators() {
463  return $this->mIndicators;
464  }
465 
466  public function getTitleText() {
467  return $this->mTitleText;
468  }
469 
470  public function getSections() {
471  return $this->mSections;
472  }
473 
474  public function &getLinks() {
475  return $this->mLinks;
476  }
477 
478  public function &getTemplates() {
479  return $this->mTemplates;
480  }
481 
482  public function &getTemplateIds() {
483  return $this->mTemplateIds;
484  }
485 
486  public function &getImages() {
487  return $this->mImages;
488  }
489 
490  public function &getFileSearchOptions() {
492  }
493 
494  public function &getExternalLinks() {
495  return $this->mExternalLinks;
496  }
497 
498  public function setNoGallery( $value ) {
499  $this->mNoGallery = (bool)$value;
500  }
501 
502  public function getNoGallery() {
503  return $this->mNoGallery;
504  }
505 
506  public function getHeadItems() {
507  return $this->mHeadItems;
508  }
509 
510  public function getModules() {
511  return $this->mModules;
512  }
513 
514  public function getModuleStyles() {
515  return $this->mModuleStyles;
516  }
517 
522  public function getJsConfigVars() {
523  return $this->mJsConfigVars;
524  }
525 
526  public function getOutputHooks() {
527  return (array)$this->mOutputHooks;
528  }
529 
530  public function getWarnings() {
531  return array_keys( $this->mWarnings );
532  }
533 
534  public function getIndexPolicy() {
535  return $this->mIndexPolicy;
536  }
537 
538  public function getTOCHTML() {
539  return $this->mTOCHTML;
540  }
541 
545  public function getTimestamp() {
546  return $this->mTimestamp;
547  }
548 
549  public function getLimitReportData() {
551  }
552 
553  public function getLimitReportJSData() {
555  }
556 
557  public function getEnableOOUI() {
558  return $this->mEnableOOUI;
559  }
560 
561  public function setText( $text ) {
562  return wfSetVar( $this->mText, $text );
563  }
564 
565  public function setLanguageLinks( $ll ) {
566  return wfSetVar( $this->mLanguageLinks, $ll );
567  }
568 
569  public function setCategoryLinks( $cl ) {
570  return wfSetVar( $this->mCategories, $cl );
571  }
572 
573  public function setTitleText( $t ) {
574  return wfSetVar( $this->mTitleText, $t );
575  }
576 
577  public function setSections( $toc ) {
578  return wfSetVar( $this->mSections, $toc );
579  }
580 
581  public function setIndexPolicy( $policy ) {
582  return wfSetVar( $this->mIndexPolicy, $policy );
583  }
584 
585  public function setTOCHTML( $tochtml ) {
586  return wfSetVar( $this->mTOCHTML, $tochtml );
587  }
588 
589  public function setTimestamp( $timestamp ) {
590  return wfSetVar( $this->mTimestamp, $timestamp );
591  }
592 
593  public function addCategory( $c, $sort ) {
594  $this->mCategories[$c] = $sort;
595  }
596 
602  public function setIndicator( $id, $content ) {
603  $this->mIndicators[$id] = $content;
604  }
605 
613  public function setEnableOOUI( $enable = false ) {
614  $this->mEnableOOUI = $enable;
615  }
616 
617  public function addLanguageLink( $t ) {
618  $this->mLanguageLinks[] = $t;
619  }
620 
621  public function addWarning( $s ) {
622  $this->mWarnings[$s] = 1;
623  }
624 
625  public function addOutputHook( $hook, $data = false ) {
626  $this->mOutputHooks[] = [ $hook, $data ];
627  }
628 
629  public function setNewSection( $value ) {
630  $this->mNewSection = (bool)$value;
631  }
632 
633  public function hideNewSection( $value ) {
634  $this->mHideNewSection = (bool)$value;
635  }
636 
637  public function getHideNewSection() {
638  return (bool)$this->mHideNewSection;
639  }
640 
641  public function getNewSection() {
642  return (bool)$this->mNewSection;
643  }
644 
652  public static function isLinkInternal( $internal, $url ) {
653  return (bool)preg_match( '/^' .
654  # If server is proto relative, check also for http/https links
655  ( substr( $internal, 0, 2 ) === '//' ? '(?:https?:)?' : '' ) .
656  preg_quote( $internal, '/' ) .
657  # check for query/path/anchor or end of link in each case
658  '(?:[\?\/\#]|$)/i',
659  $url
660  );
661  }
662 
663  public function addExternalLink( $url ) {
664  # We don't register links pointing to our own server, unless... :-)
666 
667  # Replace unnecessary URL escape codes with the referenced character
668  # This prevents spammers from hiding links from the filters
669  $url = Parser::normalizeLinkUrl( $url );
670 
671  $registerExternalLink = true;
672  if ( !$wgRegisterInternalExternals ) {
673  $registerExternalLink = !self::isLinkInternal( $wgServer, $url );
674  }
675  if ( $registerExternalLink ) {
676  $this->mExternalLinks[$url] = 1;
677  }
678  }
679 
686  public function addLink( Title $title, $id = null ) {
687  if ( $title->isExternal() ) {
688  // Don't record interwikis in pagelinks
689  $this->addInterwikiLink( $title );
690  return;
691  }
692  $ns = $title->getNamespace();
693  $dbk = $title->getDBkey();
694  if ( $ns == NS_MEDIA ) {
695  // Normalize this pseudo-alias if it makes it down here...
696  $ns = NS_FILE;
697  } elseif ( $ns == NS_SPECIAL ) {
698  // We don't record Special: links currently
699  // It might actually be wise to, but we'd need to do some normalization.
700  return;
701  } elseif ( $dbk === '' ) {
702  // Don't record self links - [[#Foo]]
703  return;
704  }
705  if ( !isset( $this->mLinks[$ns] ) ) {
706  $this->mLinks[$ns] = [];
707  }
708  if ( is_null( $id ) ) {
709  $id = $title->getArticleID();
710  }
711  $this->mLinks[$ns][$dbk] = $id;
712  }
713 
720  public function addImage( $name, $timestamp = null, $sha1 = null ) {
721  $this->mImages[$name] = 1;
722  if ( $timestamp !== null && $sha1 !== null ) {
723  $this->mFileSearchOptions[$name] = [ 'time' => $timestamp, 'sha1' => $sha1 ];
724  }
725  }
726 
733  public function addTemplate( $title, $page_id, $rev_id ) {
734  $ns = $title->getNamespace();
735  $dbk = $title->getDBkey();
736  if ( !isset( $this->mTemplates[$ns] ) ) {
737  $this->mTemplates[$ns] = [];
738  }
739  $this->mTemplates[$ns][$dbk] = $page_id;
740  if ( !isset( $this->mTemplateIds[$ns] ) ) {
741  $this->mTemplateIds[$ns] = [];
742  }
743  $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
744  }
745 
750  public function addInterwikiLink( $title ) {
751  if ( !$title->isExternal() ) {
752  throw new MWException( 'Non-interwiki link passed, internal parser error.' );
753  }
754  $prefix = $title->getInterwiki();
755  if ( !isset( $this->mInterwikiLinks[$prefix] ) ) {
756  $this->mInterwikiLinks[$prefix] = [];
757  }
758  $this->mInterwikiLinks[$prefix][$title->getDBkey()] = 1;
759  }
760 
768  public function addHeadItem( $section, $tag = false ) {
769  if ( $tag !== false ) {
770  $this->mHeadItems[$tag] = $section;
771  } else {
772  $this->mHeadItems[] = $section;
773  }
774  }
775 
779  public function addModules( $modules ) {
780  $this->mModules = array_merge( $this->mModules, (array)$modules );
781  }
782 
786  public function addModuleStyles( $modules ) {
787  $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
788  }
789 
797  public function addJsConfigVars( $keys, $value = null ) {
798  if ( is_array( $keys ) ) {
799  foreach ( $keys as $key => $value ) {
800  $this->mJsConfigVars[$key] = $value;
801  }
802  return;
803  }
804 
805  $this->mJsConfigVars[$keys] = $value;
806  }
807 
813  public function addOutputPageMetadata( OutputPage $out ) {
814  $this->addModules( $out->getModules() );
815  $this->addModuleStyles( $out->getModuleStyles() );
816  $this->addJsConfigVars( $out->getJsConfigVars() );
817 
818  $this->mHeadItems = array_merge( $this->mHeadItems, $out->getHeadItemsArray() );
819  $this->mPreventClickjacking = $this->mPreventClickjacking || $out->getPreventClickjacking();
820  }
821 
838  public function addTrackingCategory( $msg, $title ) {
839  if ( $title->isSpecialPage() ) {
840  wfDebug( __METHOD__ . ": Not adding tracking category $msg to special page!\n" );
841  return false;
842  }
843 
844  // Important to parse with correct title (T33469)
845  $cat = wfMessage( $msg )
846  ->title( $title )
847  ->inContentLanguage()
848  ->text();
849 
850  # Allow tracking categories to be disabled by setting them to "-"
851  if ( $cat === '-' ) {
852  return false;
853  }
854 
855  $containerCategory = Title::makeTitleSafe( NS_CATEGORY, $cat );
856  if ( $containerCategory ) {
857  $this->addCategory( $containerCategory->getDBkey(), $this->getProperty( 'defaultsort' ) ?: '' );
858  return true;
859  } else {
860  wfDebug( __METHOD__ . ": [[MediaWiki:$msg]] is not a valid title!\n" );
861  return false;
862  }
863  }
864 
876  public function setDisplayTitle( $text ) {
877  $this->setTitleText( $text );
878  $this->setProperty( 'displaytitle', $text );
879  }
880 
889  public function getDisplayTitle() {
890  $t = $this->getTitleText();
891  if ( $t === '' ) {
892  return false;
893  }
894  return $t;
895  }
896 
902  public function setFlag( $flag ) {
903  $this->mFlags[$flag] = true;
904  }
905 
910  public function getFlag( $flag ) {
911  return isset( $this->mFlags[$flag] );
912  }
913 
918  public function getAllFlags() {
919  return array_keys( $this->mFlags );
920  }
921 
982  public function setProperty( $name, $value ) {
983  $this->mProperties[$name] = $value;
984  }
985 
994  public function getProperty( $name ) {
995  return $this->mProperties[$name] ?? false;
996  }
997 
998  public function unsetProperty( $name ) {
999  unset( $this->mProperties[$name] );
1000  }
1001 
1002  public function getProperties() {
1003  if ( !isset( $this->mProperties ) ) {
1004  $this->mProperties = [];
1005  }
1006  return $this->mProperties;
1007  }
1008 
1014  public function getUsedOptions() {
1015  if ( !isset( $this->mAccessedOptions ) ) {
1016  return [];
1017  }
1018  return array_keys( $this->mAccessedOptions );
1019  }
1020 
1033  public function recordOption( $option ) {
1034  $this->mAccessedOptions[$option] = true;
1035  }
1036 
1077  public function setExtensionData( $key, $value ) {
1078  if ( $value === null ) {
1079  unset( $this->mExtensionData[$key] );
1080  } else {
1081  $this->mExtensionData[$key] = $value;
1082  }
1083  }
1084 
1096  public function getExtensionData( $key ) {
1097  return $this->mExtensionData[$key] ?? null;
1098  }
1099 
1100  private static function getTimes( $clock = null ) {
1101  $ret = [];
1102  if ( !$clock || $clock === 'wall' ) {
1103  $ret['wall'] = microtime( true );
1104  }
1105  if ( !$clock || $clock === 'cpu' ) {
1106  $ru = wfGetRusage();
1107  if ( $ru ) {
1108  $ret['cpu'] = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
1109  $ret['cpu'] += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
1110  }
1111  }
1112  return $ret;
1113  }
1114 
1119  public function resetParseStartTime() {
1120  $this->mParseStartTime = self::getTimes();
1121  }
1122 
1134  public function getTimeSinceStart( $clock ) {
1135  if ( !isset( $this->mParseStartTime[$clock] ) ) {
1136  return null;
1137  }
1138 
1139  $end = self::getTimes( $clock );
1140  return $end[$clock] - $this->mParseStartTime[$clock];
1141  }
1142 
1162  public function setLimitReportData( $key, $value ) {
1163  $this->mLimitReportData[$key] = $value;
1164 
1165  if ( is_array( $value ) ) {
1166  if ( array_keys( $value ) === [ 0, 1 ]
1167  && is_numeric( $value[0] )
1168  && is_numeric( $value[1] )
1169  ) {
1170  $data = [ 'value' => $value[0], 'limit' => $value[1] ];
1171  } else {
1172  $data = $value;
1173  }
1174  } else {
1175  $data = $value;
1176  }
1177 
1178  if ( strpos( $key, '-' ) ) {
1179  list( $ns, $name ) = explode( '-', $key, 2 );
1180  $this->mLimitReportJSData[$ns][$name] = $data;
1181  } else {
1182  $this->mLimitReportJSData[$key] = $data;
1183  }
1184  }
1185 
1196  public function hasDynamicContent() {
1197  global $wgParserCacheExpireTime;
1198 
1199  return $this->getCacheExpiry() < $wgParserCacheExpireTime;
1200  }
1201 
1209  public function preventClickjacking( $flag = null ) {
1210  return wfSetVar( $this->mPreventClickjacking, $flag );
1211  }
1212 
1219  public function updateRuntimeAdaptiveExpiry( $ttl ) {
1220  $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry );
1221  $this->updateCacheExpiry( $ttl );
1222  }
1223 
1229  public function finalizeAdaptiveCacheExpiry() {
1230  if ( is_infinite( $this->mMaxAdaptiveExpiry ) ) {
1231  return; // not set
1232  }
1233 
1234  $runtime = $this->getTimeSinceStart( 'wall' );
1235  if ( is_float( $runtime ) ) {
1236  $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL )
1237  / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC );
1238  // SLOW_AR_TTL = PARSE_SLOW_SEC * $slope + $point
1239  $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope;
1240 
1241  $adaptiveTTL = min(
1242  max( $slope * $runtime + $point, self::MIN_AR_TTL ),
1243  $this->mMaxAdaptiveExpiry
1244  );
1245  $this->updateCacheExpiry( $adaptiveTTL );
1246  }
1247  }
1248 
1249  public function __sleep() {
1250  return array_diff(
1251  array_keys( get_object_vars( $this ) ),
1252  [ 'mParseStartTime' ]
1253  );
1254  }
1255 
1264  $this->mOutputHooks = self::mergeList( $this->mOutputHooks, $source->getOutputHooks() );
1265  $this->mWarnings = self::mergeMap( $this->mWarnings, $source->mWarnings ); // don't use getter
1266  $this->mTimestamp = $this->useMaxValue( $this->mTimestamp, $source->getTimestamp() );
1267 
1268  if ( $this->mSpeculativeRevId && $source->mSpeculativeRevId
1269  && $this->mSpeculativeRevId !== $source->mSpeculativeRevId
1270  ) {
1271  wfLogWarning(
1272  'Inconsistent speculative revision ID encountered while merging parser output!'
1273  );
1274  }
1275 
1276  $this->mSpeculativeRevId = $this->useMaxValue(
1277  $this->mSpeculativeRevId,
1278  $source->getSpeculativeRevIdUsed()
1279  );
1280  $this->mParseStartTime = $this->useEachMinValue(
1281  $this->mParseStartTime,
1282  $source->mParseStartTime
1283  );
1284 
1285  $this->mFlags = self::mergeMap( $this->mFlags, $source->mFlags );
1286  $this->mAccessedOptions = self::mergeMap( $this->mAccessedOptions, $source->mAccessedOptions );
1287 
1288  // TODO: maintain per-slot limit reports!
1289  if ( empty( $this->mLimitReportData ) ) {
1290  $this->mLimitReportData = $source->mLimitReportData;
1291  }
1292  if ( empty( $this->mLimitReportJSData ) ) {
1293  $this->mLimitReportJSData = $source->mLimitReportJSData;
1294  }
1295  }
1296 
1305  // HTML and HTTP
1306  $this->mHeadItems = self::mergeMixedList( $this->mHeadItems, $source->getHeadItems() );
1307  $this->mModules = self::mergeList( $this->mModules, $source->getModules() );
1308  $this->mModuleStyles = self::mergeList( $this->mModuleStyles, $source->getModuleStyles() );
1309  $this->mJsConfigVars = self::mergeMap( $this->mJsConfigVars, $source->getJsConfigVars() );
1310  $this->mMaxAdaptiveExpiry = min( $this->mMaxAdaptiveExpiry, $source->mMaxAdaptiveExpiry );
1311 
1312  // "noindex" always wins!
1313  if ( $this->mIndexPolicy === 'noindex' || $source->mIndexPolicy === 'noindex' ) {
1314  $this->mIndexPolicy = 'noindex';
1315  } elseif ( $this->mIndexPolicy !== 'index' ) {
1316  $this->mIndexPolicy = $source->mIndexPolicy;
1317  }
1318 
1319  // Skin control
1320  $this->mNewSection = $this->mNewSection || $source->getNewSection();
1321  $this->mHideNewSection = $this->mHideNewSection || $source->getHideNewSection();
1322  $this->mNoGallery = $this->mNoGallery || $source->getNoGallery();
1323  $this->mEnableOOUI = $this->mEnableOOUI || $source->getEnableOOUI();
1324  $this->mPreventClickjacking = $this->mPreventClickjacking || $source->preventClickjacking();
1325 
1326  // TODO: we'll have to be smarter about this!
1327  $this->mSections = array_merge( $this->mSections, $source->getSections() );
1328  $this->mTOCHTML .= $source->mTOCHTML;
1329 
1330  // XXX: we don't want to concatenate title text, so first write wins.
1331  // We should use the first *modified* title text, but we don't have the original to check.
1332  if ( $this->mTitleText === null || $this->mTitleText === '' ) {
1333  $this->mTitleText = $source->mTitleText;
1334  }
1335 
1336  // class names are stored in array keys
1337  $this->mWrapperDivClasses = self::mergeMap(
1338  $this->mWrapperDivClasses,
1339  $source->mWrapperDivClasses
1340  );
1341 
1342  // NOTE: last write wins, same as within one ParserOutput
1343  $this->mIndicators = self::mergeMap( $this->mIndicators, $source->getIndicators() );
1344 
1345  // NOTE: include extension data in "tracking meta data" as well as "html meta data"!
1346  // TODO: add a $mergeStrategy parameter to setExtensionData to allow different
1347  // kinds of extension data to be merged in different ways.
1348  $this->mExtensionData = self::mergeMap(
1349  $this->mExtensionData,
1350  $source->mExtensionData
1351  );
1352  }
1353 
1362  $this->mLanguageLinks = self::mergeList( $this->mLanguageLinks, $source->getLanguageLinks() );
1363  $this->mCategories = self::mergeMap( $this->mCategories, $source->getCategories() );
1364  $this->mLinks = self::merge2D( $this->mLinks, $source->getLinks() );
1365  $this->mTemplates = self::merge2D( $this->mTemplates, $source->getTemplates() );
1366  $this->mTemplateIds = self::merge2D( $this->mTemplateIds, $source->getTemplateIds() );
1367  $this->mImages = self::mergeMap( $this->mImages, $source->getImages() );
1368  $this->mFileSearchOptions = self::mergeMap(
1369  $this->mFileSearchOptions,
1370  $source->getFileSearchOptions()
1371  );
1372  $this->mExternalLinks = self::mergeMap( $this->mExternalLinks, $source->getExternalLinks() );
1373  $this->mInterwikiLinks = self::merge2D(
1374  $this->mInterwikiLinks,
1375  $source->getInterwikiLinks()
1376  );
1377 
1378  // TODO: add a $mergeStrategy parameter to setProperty to allow different
1379  // kinds of properties to be merged in different ways.
1380  $this->mProperties = self::mergeMap( $this->mProperties, $source->getProperties() );
1381 
1382  // NOTE: include extension data in "tracking meta data" as well as "html meta data"!
1383  // TODO: add a $mergeStrategy parameter to setExtensionData to allow different
1384  // kinds of extension data to be merged in different ways.
1385  $this->mExtensionData = self::mergeMap(
1386  $this->mExtensionData,
1387  $source->mExtensionData
1388  );
1389  }
1390 
1391  private static function mergeMixedList( array $a, array $b ) {
1392  return array_unique( array_merge( $a, $b ), SORT_REGULAR );
1393  }
1394 
1395  private static function mergeList( array $a, array $b ) {
1396  return array_values( array_unique( array_merge( $a, $b ), SORT_REGULAR ) );
1397  }
1398 
1399  private static function mergeMap( array $a, array $b ) {
1400  return array_replace( $a, $b );
1401  }
1402 
1403  private static function merge2D( array $a, array $b ) {
1404  $values = [];
1405  $keys = array_merge( array_keys( $a ), array_keys( $b ) );
1406 
1407  foreach ( $keys as $k ) {
1408  if ( empty( $a[$k] ) ) {
1409  $values[$k] = $b[$k];
1410  } elseif ( empty( $b[$k] ) ) {
1411  $values[$k] = $a[$k];
1412  } elseif ( is_array( $a[$k] ) && is_array( $b[$k] ) ) {
1413  $values[$k] = array_replace( $a[$k], $b[$k] );
1414  } else {
1415  $values[$k] = $b[$k];
1416  }
1417  }
1418 
1419  return $values;
1420  }
1421 
1422  private static function useEachMinValue( array $a, array $b ) {
1423  $values = [];
1424  $keys = array_merge( array_keys( $a ), array_keys( $b ) );
1425 
1426  foreach ( $keys as $k ) {
1427  if ( is_array( $a[$k] ?? null ) && is_array( $b[$k] ?? null ) ) {
1428  $values[$k] = self::useEachMinValue( $a[$k], $b[$k] );
1429  } else {
1430  $values[$k] = self::useMinValue( $a[$k] ?? null, $b[$k] ?? null );
1431  }
1432  }
1433 
1434  return $values;
1435  }
1436 
1437  private static function useMinValue( $a, $b ) {
1438  if ( $a === null ) {
1439  return $b;
1440  }
1441 
1442  if ( $b === null ) {
1443  return $a;
1444  }
1445 
1446  return min( $a, $b );
1447  }
1448 
1449  private static function useMaxValue( $a, $b ) {
1450  if ( $a === null ) {
1451  return $b;
1452  }
1453 
1454  if ( $b === null ) {
1455  return $a;
1456  }
1457 
1458  return max( $a, $b );
1459  }
1460 
1461 }
static useEachMinValue(array $a, array $b)
getPreventClickjacking()
Get the prevent-clickjacking flag.
addCategory( $c, $sort)
static useMaxValue( $a, $b)
getModules( $filter=false, $position=null, $param='mModules', $type=ResourceLoaderModule::TYPE_COMBINED)
Get the list of modules to include on this page.
Definition: OutputPage.php:533
setProperty( $name, $value)
Set a property to be stored in the page_props database table.
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
Definition: deferred.txt:11
addTrackingCategory( $msg, $title)
Add a tracking category, getting the title from a system message, or print a debug message if the tit...
getHeadItemsArray()
Get an array of head items.
Definition: OutputPage.php:629
$wgParserCacheExpireTime
The expiry time for the parser cache, in seconds.
getArticleID( $flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition: Title.php:3005
setTOCHTML( $tochtml)
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:232
int null $mSpeculativeRevId
Assumed rev ID for {{REVISIONID}} if no revision is set.
hideNewSection( $value)
addModuleStyles( $modules)
const PARSE_FAST_SEC
setText( $text)
__construct( $text='', $languageLinks=[], $categoryLinks=[], $unused=false, $titletext='')
const EDITSECTION_REGEX
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
Definition: hooks.txt:1982
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
preventClickjacking( $flag=null)
Get or set the prevent-clickjacking flag.
wfGetRusage()
Get system resource usage of current request context.
in this case you re responsible for computing and outputting the entire conflict i the difference between revisions and your text headers and sections and Diff initially an empty< div id="toolbar"></div > Hook subscribers can return false to have no toolbar HTML be loaded overridable Default is either copyrightwarning or copyrightwarning2 overridable Default is editpage tos summary such as anonymity and the real check
Definition: hooks.txt:1420
setCategoryLinks( $cl)
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
Definition: globals.txt:10
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
$wgRegisterInternalExternals
By default MediaWiki does not register links pointing to same server in externallinks dataset...
const TOC_START
Definition: Parser.php:139
& getFileSearchOptions()
getWrapperDivClass()
Returns the class (or classes) to be used with the wrapper div for this otuput.
$sort
getProperty( $name)
null for the local wiki Added in
Definition: hooks.txt:1585
$source
$value
const NS_SPECIAL
Definition: Defines.php:49
static isLinkInternal( $internal, $url)
Checks, if a url is pointing to the own server.
setSpeculativeRevIdUsed( $id)
Parser cache specific expiry check.
Definition: CacheTime.php:29
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
setDisplayTitle( $text)
Override the title to be used for display.
addImage( $name, $timestamp=null, $sha1=null)
Register a file dependency for this output.
addExternalLink( $url)
static getTimes( $clock=null)
addWrapperDivClass( $class)
Add a CSS class to use for the wrapping div.
addModules( $modules)
Apache License January http
This document provides an overview of the usage of PageUpdater and that is
Definition: pageupdater.txt:3
getJsConfigVars()
Get the javascript config vars to include on this page.
static useMinValue( $a, $b)
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password 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
Definition: hooks.txt:780
setEnableOOUI( $enable=false)
Enables OOUI, if true, in any OutputPage instance this ParserOutput object is added to...
This document describes the XML format used to represent information about external sites known to a MediaWiki installation This information about external sites is used to allow inter wiki links
in the order they appear.
Definition: sitelist.txt:3
addOutputHook( $hook, $data=false)
getDisplayTitle()
Get the title to be used for display.
hasText()
Returns true if text was passed to the constructor, or set using setText().
$modules
setExtensionData( $key, $value)
Attaches arbitrary data to this ParserObject.
getCacheExpiry()
Returns the number of seconds after which this object should expire.
Definition: CacheTime.php:129
isExternal()
Is this Title interwiki?
Definition: Title.php:901
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness, which urlencode encodes by default.
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead 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 unset offset - wrap String Wrap the message in html(usually something like "&lt
static getMain()
Get the RequestContext object associated with the main request.
recordOption( $option)
Tags a parser option for use in the cache key for this parser output.
mergeTrackingMetaDataFrom(ParserOutput $source)
Merges dependency tracking metadata such as backlinks, images used, and extension data from $source i...
getExtensionData( $key)
Gets extensions data previously attached to this ParserOutput using setExtensionData().
setIndexPolicy( $policy)
setFlag( $flag)
Attach a flag to the output so that it can be checked later to handle special cases.
getDBkey()
Get the main part with underscores.
Definition: Title.php:1002
addInterwikiLink( $title)
const NS_MEDIA
Definition: Defines.php:48
static mergeList(array $a, array $b)
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
getTimeSinceStart( $clock)
Returns the time since resetParseStartTime() was last called.
const NS_CATEGORY
Definition: Defines.php:74
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 & $options
Definition: hooks.txt:1982
scripts txt MediaWiki primary scripts are in the root directory of the software Users should only use these scripts to access the wiki There are also some php that aren t primary scripts but helper files and won t work if they are accessed directly by the web Primary see https
Definition: scripts.txt:21
addOutputPageMetadata(OutputPage $out)
Copy items from the OutputPage object into this one.
static mergeMap(array $a, array $b)
setNoGallery( $value)
static mergeMixedList(array $a, array $b)
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
static runWithoutAbort( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:231
static decodeTagAttributes( $text)
Return an associative array of attribute names and values from a partial tag string.
Definition: Sanitizer.php:1443
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:1026
const NS_FILE
Definition: Defines.php:66
getModuleStyles( $filter=false, $position=null)
Get the list of style-only modules to load on this page.
Definition: OutputPage.php:558
getRawText()
Get the cacheable text with <mw:editsection> markers still in it.
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
Definition: distributors.txt:9
resetParseStartTime()
Resets the parse start timestamps for future calls to getTimeSinceStart()
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template $section
Definition: hooks.txt:3051
const SUPPORTS_UNWRAP_TRANSFORM
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:617
static merge2D(array $a, array $b)
addLanguageLink( $t)
mergeInternalMetaDataFrom(ParserOutput $source)
Merges internal metadata such as flags, accessed options, and profiling info from $source into this P...
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
Definition: injection.txt:35
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...
setLimitReportData( $key, $value)
Sets parser limit report data for a key.
addTemplate( $title, $page_id, $rev_id)
Register a template dependency for this output.
array $mLimitReportJSData
Parser limit report data for JSON.
hasDynamicContent()
Check whether the cache TTL was lowered due to dynamic content.
const SUPPORTS_STATELESS_TRANSFORMS
Feature flags to indicate to extensions that MediaWiki core supports and uses getText() stateless tra...
addHeadItem( $section, $tag=false)
Add some text to the "<head>".
getText( $options=[])
Get the output HTML.
const TOC_END
Definition: Parser.php:140
finalizeAdaptiveCacheExpiry()
Call this when parsing is done to lower the TTL based on low parse times.
unsetProperty( $name)
setLanguageLinks( $ll)
setIndicator( $id, $content)
static decodeCharReferences( $text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string...
Definition: Sanitizer.php:1662
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
Definition: hooks.txt:3051
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2633
clearWrapperDivClass()
Clears the CSS class to use for the wrapping div, effectively disabling the wrapper div until addWrap...
addLink(Title $title, $id=null)
Record a local or interwiki inline link for saving in future link tables.
int $mMaxAdaptiveExpiry
Upper bound of expiry based on parse duration.
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 and they certainly aren t ideal for someone who s installing MediaWiki as MediaWiki does not conform to normal Unix filesystem layout Hopefully we ll offer direct support for standard layouts in the but for now *any change to the location of files is unsupported *Moving things and leaving symlinks will *probably *not break but it is *strongly *advised not to try any more intrusive changes to get MediaWiki to conform more closely to your filesystem hierarchy Any such attempt will almost certainly result in unnecessary bugs The standard recommended location to install relative to the web is it should be possible to enable the appropriate rewrite rules by if you can reconfigure the web server
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
$wgServer
URL of the server.
$mWrapperDivClasses
string CSS classes to use for the wrapping div, stored in the array keys.
const PARSE_SLOW_SEC
static normalizeLinkUrl( $url)
Replace unusual escape codes in a URL with their equivalent characters.
Definition: Parser.php:2095
setSections( $toc)
getFlag( $flag)
setTimestamp( $timestamp)
updateCacheExpiry( $seconds)
Sets the number of seconds after which this object should expire.
Definition: CacheTime.php:112
$content
Definition: pageupdater.txt:72
updateRuntimeAdaptiveExpiry( $ttl)
Lower the runtime adaptive TTL to at most this value.
For a write query
Definition: database.txt:26
setNewSection( $value)
mergeHtmlMetaDataFrom(ParserOutput $source)
Merges HTML metadata such as head items, JS config vars, and HTTP cache control info from $source int...
getUsedOptions()
Returns the options from its ParserOptions which have been taken into account to produce this output...
addJsConfigVars( $keys, $value=null)
Add one or more variables to be set in mw.config in JavaScript.
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:319