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 
477  public function getEditSectionTokens() {
478  wfDeprecated( __METHOD__, '1.31' );
479  return true;
480  }
481 
482  public function &getLinks() {
483  return $this->mLinks;
484  }
485 
486  public function &getTemplates() {
487  return $this->mTemplates;
488  }
489 
490  public function &getTemplateIds() {
491  return $this->mTemplateIds;
492  }
493 
494  public function &getImages() {
495  return $this->mImages;
496  }
497 
498  public function &getFileSearchOptions() {
500  }
501 
502  public function &getExternalLinks() {
503  return $this->mExternalLinks;
504  }
505 
506  public function setNoGallery( $value ) {
507  $this->mNoGallery = (bool)$value;
508  }
509  public function getNoGallery() {
510  return $this->mNoGallery;
511  }
512 
513  public function getHeadItems() {
514  return $this->mHeadItems;
515  }
516 
517  public function getModules() {
518  return $this->mModules;
519  }
520 
521  public function getModuleScripts() {
522  wfDeprecated( __METHOD__, '1.33' );
523  return [];
524  }
525 
526  public function getModuleStyles() {
527  return $this->mModuleStyles;
528  }
529 
534  public function getJsConfigVars() {
535  return $this->mJsConfigVars;
536  }
537 
538  public function getOutputHooks() {
539  return (array)$this->mOutputHooks;
540  }
541 
542  public function getWarnings() {
543  return array_keys( $this->mWarnings );
544  }
545 
546  public function getIndexPolicy() {
547  return $this->mIndexPolicy;
548  }
549 
550  public function getTOCHTML() {
551  return $this->mTOCHTML;
552  }
553 
557  public function getTimestamp() {
558  return $this->mTimestamp;
559  }
560 
561  public function getLimitReportData() {
563  }
564 
565  public function getLimitReportJSData() {
567  }
568 
572  public function getTOCEnabled() {
573  wfDeprecated( __METHOD__, '1.31' );
574  return true;
575  }
576 
577  public function getEnableOOUI() {
578  return $this->mEnableOOUI;
579  }
580 
581  public function setText( $text ) {
582  return wfSetVar( $this->mText, $text );
583  }
584 
585  public function setLanguageLinks( $ll ) {
586  return wfSetVar( $this->mLanguageLinks, $ll );
587  }
588 
589  public function setCategoryLinks( $cl ) {
590  return wfSetVar( $this->mCategories, $cl );
591  }
592 
593  public function setTitleText( $t ) {
594  return wfSetVar( $this->mTitleText, $t );
595  }
596 
597  public function setSections( $toc ) {
598  return wfSetVar( $this->mSections, $toc );
599  }
600 
604  public function setEditSectionTokens( $t ) {
605  wfDeprecated( __METHOD__, '1.31' );
606  return true;
607  }
608 
609  public function setIndexPolicy( $policy ) {
610  return wfSetVar( $this->mIndexPolicy, $policy );
611  }
612 
613  public function setTOCHTML( $tochtml ) {
614  return wfSetVar( $this->mTOCHTML, $tochtml );
615  }
616 
617  public function setTimestamp( $timestamp ) {
618  return wfSetVar( $this->mTimestamp, $timestamp );
619  }
620 
624  public function setTOCEnabled( $flag ) {
625  wfDeprecated( __METHOD__, '1.31' );
626  return true;
627  }
628 
629  public function addCategory( $c, $sort ) {
630  $this->mCategories[$c] = $sort;
631  }
632 
638  public function setIndicator( $id, $content ) {
639  $this->mIndicators[$id] = $content;
640  }
641 
649  public function setEnableOOUI( $enable = false ) {
650  $this->mEnableOOUI = $enable;
651  }
652 
653  public function addLanguageLink( $t ) {
654  $this->mLanguageLinks[] = $t;
655  }
656 
657  public function addWarning( $s ) {
658  $this->mWarnings[$s] = 1;
659  }
660 
661  public function addOutputHook( $hook, $data = false ) {
662  $this->mOutputHooks[] = [ $hook, $data ];
663  }
664 
665  public function setNewSection( $value ) {
666  $this->mNewSection = (bool)$value;
667  }
668  public function hideNewSection( $value ) {
669  $this->mHideNewSection = (bool)$value;
670  }
671  public function getHideNewSection() {
672  return (bool)$this->mHideNewSection;
673  }
674  public function getNewSection() {
675  return (bool)$this->mNewSection;
676  }
677 
685  public static function isLinkInternal( $internal, $url ) {
686  return (bool)preg_match( '/^' .
687  # If server is proto relative, check also for http/https links
688  ( substr( $internal, 0, 2 ) === '//' ? '(?:https?:)?' : '' ) .
689  preg_quote( $internal, '/' ) .
690  # check for query/path/anchor or end of link in each case
691  '(?:[\?\/\#]|$)/i',
692  $url
693  );
694  }
695 
696  public function addExternalLink( $url ) {
697  # We don't register links pointing to our own server, unless... :-)
699 
700  # Replace unnecessary URL escape codes with the referenced character
701  # This prevents spammers from hiding links from the filters
702  $url = Parser::normalizeLinkUrl( $url );
703 
704  $registerExternalLink = true;
705  if ( !$wgRegisterInternalExternals ) {
706  $registerExternalLink = !self::isLinkInternal( $wgServer, $url );
707  }
708  if ( $registerExternalLink ) {
709  $this->mExternalLinks[$url] = 1;
710  }
711  }
712 
719  public function addLink( Title $title, $id = null ) {
720  if ( $title->isExternal() ) {
721  // Don't record interwikis in pagelinks
722  $this->addInterwikiLink( $title );
723  return;
724  }
725  $ns = $title->getNamespace();
726  $dbk = $title->getDBkey();
727  if ( $ns == NS_MEDIA ) {
728  // Normalize this pseudo-alias if it makes it down here...
729  $ns = NS_FILE;
730  } elseif ( $ns == NS_SPECIAL ) {
731  // We don't record Special: links currently
732  // It might actually be wise to, but we'd need to do some normalization.
733  return;
734  } elseif ( $dbk === '' ) {
735  // Don't record self links - [[#Foo]]
736  return;
737  }
738  if ( !isset( $this->mLinks[$ns] ) ) {
739  $this->mLinks[$ns] = [];
740  }
741  if ( is_null( $id ) ) {
742  $id = $title->getArticleID();
743  }
744  $this->mLinks[$ns][$dbk] = $id;
745  }
746 
753  public function addImage( $name, $timestamp = null, $sha1 = null ) {
754  $this->mImages[$name] = 1;
755  if ( $timestamp !== null && $sha1 !== null ) {
756  $this->mFileSearchOptions[$name] = [ 'time' => $timestamp, 'sha1' => $sha1 ];
757  }
758  }
759 
766  public function addTemplate( $title, $page_id, $rev_id ) {
767  $ns = $title->getNamespace();
768  $dbk = $title->getDBkey();
769  if ( !isset( $this->mTemplates[$ns] ) ) {
770  $this->mTemplates[$ns] = [];
771  }
772  $this->mTemplates[$ns][$dbk] = $page_id;
773  if ( !isset( $this->mTemplateIds[$ns] ) ) {
774  $this->mTemplateIds[$ns] = [];
775  }
776  $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
777  }
778 
783  public function addInterwikiLink( $title ) {
784  if ( !$title->isExternal() ) {
785  throw new MWException( 'Non-interwiki link passed, internal parser error.' );
786  }
787  $prefix = $title->getInterwiki();
788  if ( !isset( $this->mInterwikiLinks[$prefix] ) ) {
789  $this->mInterwikiLinks[$prefix] = [];
790  }
791  $this->mInterwikiLinks[$prefix][$title->getDBkey()] = 1;
792  }
793 
801  public function addHeadItem( $section, $tag = false ) {
802  if ( $tag !== false ) {
803  $this->mHeadItems[$tag] = $section;
804  } else {
805  $this->mHeadItems[] = $section;
806  }
807  }
808 
812  public function addModules( $modules ) {
813  $this->mModules = array_merge( $this->mModules, (array)$modules );
814  }
815 
819  public function addModuleStyles( $modules ) {
820  $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
821  }
822 
830  public function addJsConfigVars( $keys, $value = null ) {
831  if ( is_array( $keys ) ) {
832  foreach ( $keys as $key => $value ) {
833  $this->mJsConfigVars[$key] = $value;
834  }
835  return;
836  }
837 
838  $this->mJsConfigVars[$keys] = $value;
839  }
840 
846  public function addOutputPageMetadata( OutputPage $out ) {
847  $this->addModules( $out->getModules() );
848  $this->addModuleStyles( $out->getModuleStyles() );
849  $this->addJsConfigVars( $out->getJsConfigVars() );
850 
851  $this->mHeadItems = array_merge( $this->mHeadItems, $out->getHeadItemsArray() );
852  $this->mPreventClickjacking = $this->mPreventClickjacking || $out->getPreventClickjacking();
853  }
854 
871  public function addTrackingCategory( $msg, $title ) {
872  if ( $title->isSpecialPage() ) {
873  wfDebug( __METHOD__ . ": Not adding tracking category $msg to special page!\n" );
874  return false;
875  }
876 
877  // Important to parse with correct title (T33469)
878  $cat = wfMessage( $msg )
879  ->title( $title )
880  ->inContentLanguage()
881  ->text();
882 
883  # Allow tracking categories to be disabled by setting them to "-"
884  if ( $cat === '-' ) {
885  return false;
886  }
887 
888  $containerCategory = Title::makeTitleSafe( NS_CATEGORY, $cat );
889  if ( $containerCategory ) {
890  $this->addCategory( $containerCategory->getDBkey(), $this->getProperty( 'defaultsort' ) ?: '' );
891  return true;
892  } else {
893  wfDebug( __METHOD__ . ": [[MediaWiki:$msg]] is not a valid title!\n" );
894  return false;
895  }
896  }
897 
909  public function setDisplayTitle( $text ) {
910  $this->setTitleText( $text );
911  $this->setProperty( 'displaytitle', $text );
912  }
913 
922  public function getDisplayTitle() {
923  $t = $this->getTitleText();
924  if ( $t === '' ) {
925  return false;
926  }
927  return $t;
928  }
929 
934  public function setFlag( $flag ) {
935  $this->mFlags[$flag] = true;
936  }
937 
938  public function getFlag( $flag ) {
939  return isset( $this->mFlags[$flag] );
940  }
941 
1002  public function setProperty( $name, $value ) {
1003  $this->mProperties[$name] = $value;
1004  }
1005 
1014  public function getProperty( $name ) {
1015  return $this->mProperties[$name] ?? false;
1016  }
1017 
1018  public function unsetProperty( $name ) {
1019  unset( $this->mProperties[$name] );
1020  }
1021 
1022  public function getProperties() {
1023  if ( !isset( $this->mProperties ) ) {
1024  $this->mProperties = [];
1025  }
1026  return $this->mProperties;
1027  }
1028 
1034  public function getUsedOptions() {
1035  if ( !isset( $this->mAccessedOptions ) ) {
1036  return [];
1037  }
1038  return array_keys( $this->mAccessedOptions );
1039  }
1040 
1053  public function recordOption( $option ) {
1054  $this->mAccessedOptions[$option] = true;
1055  }
1056 
1097  public function setExtensionData( $key, $value ) {
1098  if ( $value === null ) {
1099  unset( $this->mExtensionData[$key] );
1100  } else {
1101  $this->mExtensionData[$key] = $value;
1102  }
1103  }
1104 
1116  public function getExtensionData( $key ) {
1117  return $this->mExtensionData[$key] ?? null;
1118  }
1119 
1120  private static function getTimes( $clock = null ) {
1121  $ret = [];
1122  if ( !$clock || $clock === 'wall' ) {
1123  $ret['wall'] = microtime( true );
1124  }
1125  if ( !$clock || $clock === 'cpu' ) {
1126  $ru = wfGetRusage();
1127  if ( $ru ) {
1128  $ret['cpu'] = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
1129  $ret['cpu'] += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
1130  }
1131  }
1132  return $ret;
1133  }
1134 
1139  public function resetParseStartTime() {
1140  $this->mParseStartTime = self::getTimes();
1141  }
1142 
1154  public function getTimeSinceStart( $clock ) {
1155  if ( !isset( $this->mParseStartTime[$clock] ) ) {
1156  return null;
1157  }
1158 
1159  $end = self::getTimes( $clock );
1160  return $end[$clock] - $this->mParseStartTime[$clock];
1161  }
1162 
1182  public function setLimitReportData( $key, $value ) {
1183  $this->mLimitReportData[$key] = $value;
1184 
1185  if ( is_array( $value ) ) {
1186  if ( array_keys( $value ) === [ 0, 1 ]
1187  && is_numeric( $value[0] )
1188  && is_numeric( $value[1] )
1189  ) {
1190  $data = [ 'value' => $value[0], 'limit' => $value[1] ];
1191  } else {
1192  $data = $value;
1193  }
1194  } else {
1195  $data = $value;
1196  }
1197 
1198  if ( strpos( $key, '-' ) ) {
1199  list( $ns, $name ) = explode( '-', $key, 2 );
1200  $this->mLimitReportJSData[$ns][$name] = $data;
1201  } else {
1202  $this->mLimitReportJSData[$key] = $data;
1203  }
1204  }
1205 
1216  public function hasDynamicContent() {
1217  global $wgParserCacheExpireTime;
1218 
1219  return $this->getCacheExpiry() < $wgParserCacheExpireTime;
1220  }
1221 
1229  public function preventClickjacking( $flag = null ) {
1230  return wfSetVar( $this->mPreventClickjacking, $flag );
1231  }
1232 
1239  public function updateRuntimeAdaptiveExpiry( $ttl ) {
1240  $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry );
1241  $this->updateCacheExpiry( $ttl );
1242  }
1243 
1249  public function finalizeAdaptiveCacheExpiry() {
1250  if ( is_infinite( $this->mMaxAdaptiveExpiry ) ) {
1251  return; // not set
1252  }
1253 
1254  $runtime = $this->getTimeSinceStart( 'wall' );
1255  if ( is_float( $runtime ) ) {
1256  $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL )
1257  / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC );
1258  // SLOW_AR_TTL = PARSE_SLOW_SEC * $slope + $point
1259  $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope;
1260 
1261  $adaptiveTTL = min(
1262  max( $slope * $runtime + $point, self::MIN_AR_TTL ),
1263  $this->mMaxAdaptiveExpiry
1264  );
1265  $this->updateCacheExpiry( $adaptiveTTL );
1266  }
1267  }
1268 
1269  public function __sleep() {
1270  return array_diff(
1271  array_keys( get_object_vars( $this ) ),
1272  [ 'mParseStartTime' ]
1273  );
1274  }
1275 
1284  $this->mOutputHooks = self::mergeList( $this->mOutputHooks, $source->getOutputHooks() );
1285  $this->mWarnings = self::mergeMap( $this->mWarnings, $source->mWarnings ); // don't use getter
1286  $this->mTimestamp = $this->useMaxValue( $this->mTimestamp, $source->getTimestamp() );
1287 
1288  if ( $this->mSpeculativeRevId && $source->mSpeculativeRevId
1289  && $this->mSpeculativeRevId !== $source->mSpeculativeRevId
1290  ) {
1291  wfLogWarning(
1292  'Inconsistent speculative revision ID encountered while merging parser output!'
1293  );
1294  }
1295 
1296  $this->mSpeculativeRevId = $this->useMaxValue(
1297  $this->mSpeculativeRevId,
1298  $source->getSpeculativeRevIdUsed()
1299  );
1300  $this->mParseStartTime = $this->useEachMinValue(
1301  $this->mParseStartTime,
1302  $source->mParseStartTime
1303  );
1304 
1305  $this->mFlags = self::mergeMap( $this->mFlags, $source->mFlags );
1306  $this->mAccessedOptions = self::mergeMap( $this->mAccessedOptions, $source->mAccessedOptions );
1307 
1308  // TODO: maintain per-slot limit reports!
1309  if ( empty( $this->mLimitReportData ) ) {
1310  $this->mLimitReportData = $source->mLimitReportData;
1311  }
1312  if ( empty( $this->mLimitReportJSData ) ) {
1313  $this->mLimitReportJSData = $source->mLimitReportJSData;
1314  }
1315  }
1316 
1325  // HTML and HTTP
1326  $this->mHeadItems = self::mergeMixedList( $this->mHeadItems, $source->getHeadItems() );
1327  $this->mModules = self::mergeList( $this->mModules, $source->getModules() );
1328  $this->mModuleStyles = self::mergeList( $this->mModuleStyles, $source->getModuleStyles() );
1329  $this->mJsConfigVars = self::mergeMap( $this->mJsConfigVars, $source->getJsConfigVars() );
1330  $this->mMaxAdaptiveExpiry = min( $this->mMaxAdaptiveExpiry, $source->mMaxAdaptiveExpiry );
1331 
1332  // "noindex" always wins!
1333  if ( $this->mIndexPolicy === 'noindex' || $source->mIndexPolicy === 'noindex' ) {
1334  $this->mIndexPolicy = 'noindex';
1335  } elseif ( $this->mIndexPolicy !== 'index' ) {
1336  $this->mIndexPolicy = $source->mIndexPolicy;
1337  }
1338 
1339  // Skin control
1340  $this->mNewSection = $this->mNewSection || $source->getNewSection();
1341  $this->mHideNewSection = $this->mHideNewSection || $source->getHideNewSection();
1342  $this->mNoGallery = $this->mNoGallery || $source->getNoGallery();
1343  $this->mEnableOOUI = $this->mEnableOOUI || $source->getEnableOOUI();
1344  $this->mPreventClickjacking = $this->mPreventClickjacking || $source->preventClickjacking();
1345 
1346  // TODO: we'll have to be smarter about this!
1347  $this->mSections = array_merge( $this->mSections, $source->getSections() );
1348  $this->mTOCHTML .= $source->mTOCHTML;
1349 
1350  // XXX: we don't want to concatenate title text, so first write wins.
1351  // We should use the first *modified* title text, but we don't have the original to check.
1352  if ( $this->mTitleText === null || $this->mTitleText === '' ) {
1353  $this->mTitleText = $source->mTitleText;
1354  }
1355 
1356  // class names are stored in array keys
1357  $this->mWrapperDivClasses = self::mergeMap(
1358  $this->mWrapperDivClasses,
1359  $source->mWrapperDivClasses
1360  );
1361 
1362  // NOTE: last write wins, same as within one ParserOutput
1363  $this->mIndicators = self::mergeMap( $this->mIndicators, $source->getIndicators() );
1364 
1365  // NOTE: include extension data in "tracking meta data" as well as "html meta data"!
1366  // TODO: add a $mergeStrategy parameter to setExtensionData to allow different
1367  // kinds of extension data to be merged in different ways.
1368  $this->mExtensionData = self::mergeMap(
1369  $this->mExtensionData,
1370  $source->mExtensionData
1371  );
1372  }
1373 
1382  $this->mLanguageLinks = self::mergeList( $this->mLanguageLinks, $source->getLanguageLinks() );
1383  $this->mCategories = self::mergeMap( $this->mCategories, $source->getCategories() );
1384  $this->mLinks = self::merge2D( $this->mLinks, $source->getLinks() );
1385  $this->mTemplates = self::merge2D( $this->mTemplates, $source->getTemplates() );
1386  $this->mTemplateIds = self::merge2D( $this->mTemplateIds, $source->getTemplateIds() );
1387  $this->mImages = self::mergeMap( $this->mImages, $source->getImages() );
1388  $this->mFileSearchOptions = self::mergeMap(
1389  $this->mFileSearchOptions,
1390  $source->getFileSearchOptions()
1391  );
1392  $this->mExternalLinks = self::mergeMap( $this->mExternalLinks, $source->getExternalLinks() );
1393  $this->mInterwikiLinks = self::merge2D(
1394  $this->mInterwikiLinks,
1395  $source->getInterwikiLinks()
1396  );
1397 
1398  // TODO: add a $mergeStrategy parameter to setProperty to allow different
1399  // kinds of properties to be merged in different ways.
1400  $this->mProperties = self::mergeMap( $this->mProperties, $source->getProperties() );
1401 
1402  // NOTE: include extension data in "tracking meta data" as well as "html meta data"!
1403  // TODO: add a $mergeStrategy parameter to setExtensionData to allow different
1404  // kinds of extension data to be merged in different ways.
1405  $this->mExtensionData = self::mergeMap(
1406  $this->mExtensionData,
1407  $source->mExtensionData
1408  );
1409  }
1410 
1411  private static function mergeMixedList( array $a, array $b ) {
1412  return array_unique( array_merge( $a, $b ), SORT_REGULAR );
1413  }
1414 
1415  private static function mergeList( array $a, array $b ) {
1416  return array_values( array_unique( array_merge( $a, $b ), SORT_REGULAR ) );
1417  }
1418 
1419  private static function mergeMap( array $a, array $b ) {
1420  return array_replace( $a, $b );
1421  }
1422 
1423  private static function merge2D( array $a, array $b ) {
1424  $values = [];
1425  $keys = array_merge( array_keys( $a ), array_keys( $b ) );
1426 
1427  foreach ( $keys as $k ) {
1428  if ( empty( $a[$k] ) ) {
1429  $values[$k] = $b[$k];
1430  } elseif ( empty( $b[$k] ) ) {
1431  $values[$k] = $a[$k];
1432  } elseif ( is_array( $a[$k] ) && is_array( $b[$k] ) ) {
1433  $values[$k] = array_replace( $a[$k], $b[$k] );
1434  } else {
1435  $values[$k] = $b[$k];
1436  }
1437  }
1438 
1439  return $values;
1440  }
1441 
1442  private static function useEachMinValue( array $a, array $b ) {
1443  $values = [];
1444  $keys = array_merge( array_keys( $a ), array_keys( $b ) );
1445 
1446  foreach ( $keys as $k ) {
1447  if ( is_array( $a[$k] ?? null ) && is_array( $b[$k] ?? null ) ) {
1448  $values[$k] = self::useEachMinValue( $a[$k], $b[$k] );
1449  } else {
1450  $values[$k] = self::useMinValue( $a[$k] ?? null, $b[$k] ?? null );
1451  }
1452  }
1453 
1454  return $values;
1455  }
1456 
1457  private static function useMinValue( $a, $b ) {
1458  if ( $a === null ) {
1459  return $b;
1460  }
1461 
1462  if ( $b === null ) {
1463  return $a;
1464  }
1465 
1466  return min( $a, $b );
1467  }
1468 
1469  private static function useMaxValue( $a, $b ) {
1470  if ( $a === null ) {
1471  return $b;
1472  }
1473 
1474  if ( $b === null ) {
1475  return $a;
1476  }
1477 
1478  return max( $a, $b );
1479  }
1480 
1481 }
static useEachMinValue(array $a, array $b)
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
getPreventClickjacking()
Get the prevent-clickjacking flag.
addCategory( $c, $sort)
static useMaxValue( $a, $b)
setEditSectionTokens( $t)
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:638
$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:2985
setTOCHTML( $tochtml)
$data
Utility to generate mapping file used in mw.Title (phpCharToUpper.json)
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:138
& 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:53
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:900
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)
Fairly generic flag setter thingy.
getDBkey()
Get the main part with underscores.
Definition: Title.php:1001
addInterwikiLink( $title)
const NS_MEDIA
Definition: Defines.php:52
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:78
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:1442
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:1025
setTOCEnabled( $flag)
const NS_FILE
Definition: Defines.php:70
getModuleStyles( $filter=false, $position=null)
Get the list of style-only modules to load on this page.
Definition: OutputPage.php:567
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:3050
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:139
finalizeAdaptiveCacheExpiry()
Call this when parsing is done to lower the TTL based on low parse times.
unsetProperty( $name)
setLanguageLinks( $ll)
setIndicator( $id, $content)
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
static decodeCharReferences( $text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string...
Definition: Sanitizer.php:1661
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:3050
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:2038
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