MediaWiki REL1_32
ParserOutput.php
Go to the documentation of this file.
1<?php
2
25class ParserOutput extends CacheTime {
32
36 public $mText = null;
37
43
48
52 public $mIndicators = [];
53
58
63 public $mLinks = [];
64
69 public $mTemplates = [];
70
75 public $mTemplateIds = [];
76
80 public $mImages = [];
81
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 $mModuleScripts = [];
127
131 public $mModuleStyles = [];
132
136 public $mJsConfigVars = [];
137
141 public $mOutputHooks = [];
142
147 public $mWarnings = [];
148
152 public $mSections = [];
153
157 public $mProperties = [];
158
162 public $mTOCHTML = '';
163
168
172 public $mEnableOOUI = false;
173
177 private $mIndexPolicy = '';
178
182 private $mAccessedOptions = [];
183
187 private $mExtensionData = [];
188
192 private $mLimitReportData = [];
193
196
200 private $mParseStartTime = [];
201
205 private $mPreventClickjacking = false;
206
210 private $mFlags = [];
211
214
219
221 private $mMaxAdaptiveExpiry = INF;
222
224 '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#s';
225
226 // finalizeAdaptiveCacheExpiry() uses TTL = MAX( m * PARSE_TIME + b, MIN_AR_TTL)
227 // Current values imply that m=3933.333333 and b=-333.333333
228 // See https://www.nngroup.com/articles/website-response-times/
229 const PARSE_FAST_SEC = 0.100; // perceived "fast" page parse
230 const PARSE_SLOW_SEC = 1.0; // perceived "slow" page parse
231 const FAST_AR_TTL = 60; // adaptive TTL for "fast" pages
232 const SLOW_AR_TTL = 3600; // adaptive TTL for "slow" pages
233 const MIN_AR_TTL = 15; // min adaptive TTL (for sanity, pool counter, and edit stashing)
234
244 public function __construct( $text = '', $languageLinks = [], $categoryLinks = [],
245 $unused = false, $titletext = ''
246 ) {
247 $this->mText = $text;
248 $this->mLanguageLinks = $languageLinks;
249 $this->mCategories = $categoryLinks;
250 $this->mTitleText = $titletext;
251 }
252
263 public function hasText() {
264 return ( $this->mText !== null );
265 }
266
275 public function getRawText() {
276 if ( $this->mText === null ) {
277 throw new LogicException( 'This ParserOutput contains no text!' );
278 }
279
280 return $this->mText;
281 }
282
308 public function getText( $options = [] ) {
309 $options += [
310 'allowTOC' => true,
311 'enableSectionEditLinks' => true,
312 'unwrap' => false,
313 'deduplicateStyles' => true,
314 'wrapperDivClass' => $this->getWrapperDivClass(),
315 ];
316 $text = $this->getRawText();
317
318 Hooks::runWithoutAbort( 'ParserOutputPostCacheTransform', [ $this, &$text, &$options ] );
319
320 if ( $options['wrapperDivClass'] !== '' && !$options['unwrap'] ) {
321 $text = Html::rawElement( 'div', [ 'class' => $options['wrapperDivClass'] ], $text );
322 }
323
324 if ( $options['enableSectionEditLinks'] ) {
325 $text = preg_replace_callback(
326 self::EDITSECTION_REGEX,
327 function ( $m ) {
328 $editsectionPage = Title::newFromText( htmlspecialchars_decode( $m[1] ) );
329 $editsectionSection = htmlspecialchars_decode( $m[2] );
330 $editsectionContent = isset( $m[4] ) ? Sanitizer::decodeCharReferences( $m[3] ) : null;
331
332 if ( !is_object( $editsectionPage ) ) {
333 throw new MWException( "Bad parser output text." );
334 }
335
336 $context = RequestContext::getMain();
337 return $context->getSkin()->doEditSectionLink(
338 $editsectionPage,
339 $editsectionSection,
340 $editsectionContent,
341 $context->getLanguage()
342 );
343 },
344 $text
345 );
346 } else {
347 $text = preg_replace( self::EDITSECTION_REGEX, '', $text );
348 }
349
350 if ( $options['allowTOC'] ) {
351 $text = str_replace( [ Parser::TOC_START, Parser::TOC_END ], '', $text );
352 } else {
353 $text = preg_replace(
354 '#' . preg_quote( Parser::TOC_START, '#' ) . '.*?' . preg_quote( Parser::TOC_END, '#' ) . '#s',
355 '',
356 $text
357 );
358 }
359
360 if ( $options['deduplicateStyles'] ) {
361 $seen = [];
362 $text = preg_replace_callback(
363 '#<style\s+([^>]*data-mw-deduplicate\s*=[^>]*)>.*?</style>#s',
364 function ( $m ) use ( &$seen ) {
365 $attr = Sanitizer::decodeTagAttributes( $m[1] );
366 if ( !isset( $attr['data-mw-deduplicate'] ) ) {
367 return $m[0];
368 }
369
370 $key = $attr['data-mw-deduplicate'];
371 if ( !isset( $seen[$key] ) ) {
372 $seen[$key] = true;
373 return $m[0];
374 }
375
376 // We were going to use an empty <style> here, but there
377 // was concern that would be too much overhead for browsers.
378 // So let's hope a <link> with a non-standard rel and href isn't
379 // going to be misinterpreted or mangled by any subsequent processing.
380 return Html::element( 'link', [
381 'rel' => 'mw-deduplicated-inline-style',
382 'href' => "mw-data:" . wfUrlencode( $key ),
383 ] );
384 },
385 $text
386 );
387 }
388
389 // Hydrate slot section header placeholders generated by RevisionRenderer.
390 $text = preg_replace_callback(
391 '#<mw:slotheader>(.*?)</mw:slotheader>#',
392 function ( $m ) {
393 $role = htmlspecialchars_decode( $m[1] );
394 // TODO: map to message, using the interface language. Set lang="xyz" accordingly.
395 $headerText = $role;
396 return $headerText;
397 },
398 $text
399 );
400 return $text;
401 }
402
408 public function addWrapperDivClass( $class ) {
409 $this->mWrapperDivClasses[$class] = true;
410 }
411
416 public function clearWrapperDivClass() {
417 $this->mWrapperDivClasses = [];
418 }
419
427 public function getWrapperDivClass() {
428 return implode( ' ', array_keys( $this->mWrapperDivClasses ) );
429 }
430
435 public function setSpeculativeRevIdUsed( $id ) {
436 $this->mSpeculativeRevId = $id;
437 }
438
443 public function getSpeculativeRevIdUsed() {
444 return $this->mSpeculativeRevId;
445 }
446
447 public function &getLanguageLinks() {
448 return $this->mLanguageLinks;
449 }
450
451 public function getInterwikiLinks() {
452 return $this->mInterwikiLinks;
453 }
454
455 public function getCategoryLinks() {
456 return array_keys( $this->mCategories );
457 }
458
459 public function &getCategories() {
460 return $this->mCategories;
461 }
462
467 public function getIndicators() {
468 return $this->mIndicators;
469 }
470
471 public function getTitleText() {
472 return $this->mTitleText;
473 }
474
475 public function getSections() {
476 return $this->mSections;
477 }
478
482 public function getEditSectionTokens() {
483 wfDeprecated( __METHOD__, '1.31' );
484 return true;
485 }
486
487 public function &getLinks() {
488 return $this->mLinks;
489 }
490
491 public function &getTemplates() {
492 return $this->mTemplates;
493 }
494
495 public function &getTemplateIds() {
496 return $this->mTemplateIds;
497 }
498
499 public function &getImages() {
500 return $this->mImages;
501 }
502
503 public function &getFileSearchOptions() {
504 return $this->mFileSearchOptions;
505 }
506
507 public function &getExternalLinks() {
508 return $this->mExternalLinks;
509 }
510
511 public function setNoGallery( $value ) {
512 $this->mNoGallery = (bool)$value;
513 }
514 public function getNoGallery() {
515 return $this->mNoGallery;
516 }
517
518 public function getHeadItems() {
519 return $this->mHeadItems;
520 }
521
522 public function getModules() {
523 return $this->mModules;
524 }
525
526 public function getModuleScripts() {
527 return $this->mModuleScripts;
528 }
529
530 public function getModuleStyles() {
531 return $this->mModuleStyles;
532 }
533
538 public function getJsConfigVars() {
539 return $this->mJsConfigVars;
540 }
541
542 public function getOutputHooks() {
543 return (array)$this->mOutputHooks;
544 }
545
546 public function getWarnings() {
547 return array_keys( $this->mWarnings );
548 }
549
550 public function getIndexPolicy() {
551 return $this->mIndexPolicy;
552 }
553
554 public function getTOCHTML() {
555 return $this->mTOCHTML;
556 }
557
561 public function getTimestamp() {
562 return $this->mTimestamp;
563 }
564
565 public function getLimitReportData() {
566 return $this->mLimitReportData;
567 }
568
569 public function getLimitReportJSData() {
570 return $this->mLimitReportJSData;
571 }
572
576 public function getTOCEnabled() {
577 wfDeprecated( __METHOD__, '1.31' );
578 return true;
579 }
580
581 public function getEnableOOUI() {
582 return $this->mEnableOOUI;
583 }
584
585 public function setText( $text ) {
586 return wfSetVar( $this->mText, $text );
587 }
588
589 public function setLanguageLinks( $ll ) {
590 return wfSetVar( $this->mLanguageLinks, $ll );
591 }
592
593 public function setCategoryLinks( $cl ) {
594 return wfSetVar( $this->mCategories, $cl );
595 }
596
597 public function setTitleText( $t ) {
598 return wfSetVar( $this->mTitleText, $t );
599 }
600
601 public function setSections( $toc ) {
602 return wfSetVar( $this->mSections, $toc );
603 }
604
608 public function setEditSectionTokens( $t ) {
609 wfDeprecated( __METHOD__, '1.31' );
610 return true;
611 }
612
613 public function setIndexPolicy( $policy ) {
614 return wfSetVar( $this->mIndexPolicy, $policy );
615 }
616
617 public function setTOCHTML( $tochtml ) {
618 return wfSetVar( $this->mTOCHTML, $tochtml );
619 }
620
621 public function setTimestamp( $timestamp ) {
622 return wfSetVar( $this->mTimestamp, $timestamp );
623 }
624
628 public function setTOCEnabled( $flag ) {
629 wfDeprecated( __METHOD__, '1.31' );
630 return true;
631 }
632
633 public function addCategory( $c, $sort ) {
634 $this->mCategories[$c] = $sort;
635 }
636
642 public function setIndicator( $id, $content ) {
643 $this->mIndicators[$id] = $content;
644 }
645
653 public function setEnableOOUI( $enable = false ) {
654 $this->mEnableOOUI = $enable;
655 }
656
657 public function addLanguageLink( $t ) {
658 $this->mLanguageLinks[] = $t;
659 }
660
661 public function addWarning( $s ) {
662 $this->mWarnings[$s] = 1;
663 }
664
665 public function addOutputHook( $hook, $data = false ) {
666 $this->mOutputHooks[] = [ $hook, $data ];
667 }
668
669 public function setNewSection( $value ) {
670 $this->mNewSection = (bool)$value;
671 }
672 public function hideNewSection( $value ) {
673 $this->mHideNewSection = (bool)$value;
674 }
675 public function getHideNewSection() {
676 return (bool)$this->mHideNewSection;
677 }
678 public function getNewSection() {
679 return (bool)$this->mNewSection;
680 }
681
689 public static function isLinkInternal( $internal, $url ) {
690 return (bool)preg_match( '/^' .
691 # If server is proto relative, check also for http/https links
692 ( substr( $internal, 0, 2 ) === '//' ? '(?:https?:)?' : '' ) .
693 preg_quote( $internal, '/' ) .
694 # check for query/path/anchor or end of link in each case
695 '(?:[\?\/\#]|$)/i',
696 $url
697 );
698 }
699
700 public function addExternalLink( $url ) {
701 # We don't register links pointing to our own server, unless... :-)
703
704 # Replace unnecessary URL escape codes with the referenced character
705 # This prevents spammers from hiding links from the filters
706 $url = Parser::normalizeLinkUrl( $url );
707
708 $registerExternalLink = true;
710 $registerExternalLink = !self::isLinkInternal( $wgServer, $url );
711 }
712 if ( $registerExternalLink ) {
713 $this->mExternalLinks[$url] = 1;
714 }
715 }
716
723 public function addLink( Title $title, $id = null ) {
724 if ( $title->isExternal() ) {
725 // Don't record interwikis in pagelinks
726 $this->addInterwikiLink( $title );
727 return;
728 }
729 $ns = $title->getNamespace();
730 $dbk = $title->getDBkey();
731 if ( $ns == NS_MEDIA ) {
732 // Normalize this pseudo-alias if it makes it down here...
733 $ns = NS_FILE;
734 } elseif ( $ns == NS_SPECIAL ) {
735 // We don't record Special: links currently
736 // It might actually be wise to, but we'd need to do some normalization.
737 return;
738 } elseif ( $dbk === '' ) {
739 // Don't record self links - [[#Foo]]
740 return;
741 }
742 if ( !isset( $this->mLinks[$ns] ) ) {
743 $this->mLinks[$ns] = [];
744 }
745 if ( is_null( $id ) ) {
746 $id = $title->getArticleID();
747 }
748 $this->mLinks[$ns][$dbk] = $id;
749 }
750
757 public function addImage( $name, $timestamp = null, $sha1 = null ) {
758 $this->mImages[$name] = 1;
759 if ( $timestamp !== null && $sha1 !== null ) {
760 $this->mFileSearchOptions[$name] = [ 'time' => $timestamp, 'sha1' => $sha1 ];
761 }
762 }
763
770 public function addTemplate( $title, $page_id, $rev_id ) {
771 $ns = $title->getNamespace();
772 $dbk = $title->getDBkey();
773 if ( !isset( $this->mTemplates[$ns] ) ) {
774 $this->mTemplates[$ns] = [];
775 }
776 $this->mTemplates[$ns][$dbk] = $page_id;
777 if ( !isset( $this->mTemplateIds[$ns] ) ) {
778 $this->mTemplateIds[$ns] = [];
779 }
780 $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
781 }
782
787 public function addInterwikiLink( $title ) {
788 if ( !$title->isExternal() ) {
789 throw new MWException( 'Non-interwiki link passed, internal parser error.' );
790 }
791 $prefix = $title->getInterwiki();
792 if ( !isset( $this->mInterwikiLinks[$prefix] ) ) {
793 $this->mInterwikiLinks[$prefix] = [];
794 }
795 $this->mInterwikiLinks[$prefix][$title->getDBkey()] = 1;
796 }
797
805 public function addHeadItem( $section, $tag = false ) {
806 if ( $tag !== false ) {
807 $this->mHeadItems[$tag] = $section;
808 } else {
809 $this->mHeadItems[] = $section;
810 }
811 }
812
816 public function addModules( $modules ) {
817 $this->mModules = array_merge( $this->mModules, (array)$modules );
818 }
819
824 public function addModuleScripts( $modules ) {
825 $this->mModuleScripts = array_merge( $this->mModuleScripts, (array)$modules );
826 }
827
831 public function addModuleStyles( $modules ) {
832 $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
833 }
834
842 public function addJsConfigVars( $keys, $value = null ) {
843 if ( is_array( $keys ) ) {
844 foreach ( $keys as $key => $value ) {
845 $this->mJsConfigVars[$key] = $value;
846 }
847 return;
848 }
849
850 $this->mJsConfigVars[$keys] = $value;
851 }
852
859 $this->addModules( $out->getModules() );
860 $this->addModuleScripts( $out->getModuleScripts() );
861 $this->addModuleStyles( $out->getModuleStyles() );
862 $this->addJsConfigVars( $out->getJsConfigVars() );
863
864 $this->mHeadItems = array_merge( $this->mHeadItems, $out->getHeadItemsArray() );
865 $this->mPreventClickjacking = $this->mPreventClickjacking || $out->getPreventClickjacking();
866 }
867
884 public function addTrackingCategory( $msg, $title ) {
885 if ( $title->isSpecialPage() ) {
886 wfDebug( __METHOD__ . ": Not adding tracking category $msg to special page!\n" );
887 return false;
888 }
889
890 // Important to parse with correct title (T33469)
891 $cat = wfMessage( $msg )
892 ->title( $title )
893 ->inContentLanguage()
894 ->text();
895
896 # Allow tracking categories to be disabled by setting them to "-"
897 if ( $cat === '-' ) {
898 return false;
899 }
900
901 $containerCategory = Title::makeTitleSafe( NS_CATEGORY, $cat );
902 if ( $containerCategory ) {
903 $this->addCategory( $containerCategory->getDBkey(), $this->getProperty( 'defaultsort' ) ?: '' );
904 return true;
905 } else {
906 wfDebug( __METHOD__ . ": [[MediaWiki:$msg]] is not a valid title!\n" );
907 return false;
908 }
909 }
910
922 public function setDisplayTitle( $text ) {
923 $this->setTitleText( $text );
924 $this->setProperty( 'displaytitle', $text );
925 }
926
935 public function getDisplayTitle() {
936 $t = $this->getTitleText();
937 if ( $t === '' ) {
938 return false;
939 }
940 return $t;
941 }
942
947 public function setFlag( $flag ) {
948 $this->mFlags[$flag] = true;
949 }
950
951 public function getFlag( $flag ) {
952 return isset( $this->mFlags[$flag] );
953 }
954
1015 public function setProperty( $name, $value ) {
1016 $this->mProperties[$name] = $value;
1017 }
1018
1027 public function getProperty( $name ) {
1028 return $this->mProperties[$name] ?? false;
1029 }
1030
1031 public function unsetProperty( $name ) {
1032 unset( $this->mProperties[$name] );
1033 }
1034
1035 public function getProperties() {
1036 if ( !isset( $this->mProperties ) ) {
1037 $this->mProperties = [];
1038 }
1039 return $this->mProperties;
1040 }
1041
1047 public function getUsedOptions() {
1048 if ( !isset( $this->mAccessedOptions ) ) {
1049 return [];
1050 }
1051 return array_keys( $this->mAccessedOptions );
1052 }
1053
1066 public function recordOption( $option ) {
1067 $this->mAccessedOptions[$option] = true;
1068 }
1069
1110 public function setExtensionData( $key, $value ) {
1111 if ( $value === null ) {
1112 unset( $this->mExtensionData[$key] );
1113 } else {
1114 $this->mExtensionData[$key] = $value;
1115 }
1116 }
1117
1129 public function getExtensionData( $key ) {
1130 if ( isset( $this->mExtensionData[$key] ) ) {
1131 return $this->mExtensionData[$key];
1132 }
1133
1134 return null;
1135 }
1136
1137 private static function getTimes( $clock = null ) {
1138 $ret = [];
1139 if ( !$clock || $clock === 'wall' ) {
1140 $ret['wall'] = microtime( true );
1141 }
1142 if ( !$clock || $clock === 'cpu' ) {
1143 $ru = wfGetRusage();
1144 if ( $ru ) {
1145 $ret['cpu'] = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6;
1146 $ret['cpu'] += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6;
1147 }
1148 }
1149 return $ret;
1150 }
1151
1156 public function resetParseStartTime() {
1157 $this->mParseStartTime = self::getTimes();
1158 }
1159
1171 public function getTimeSinceStart( $clock ) {
1172 if ( !isset( $this->mParseStartTime[$clock] ) ) {
1173 return null;
1174 }
1175
1176 $end = self::getTimes( $clock );
1177 return $end[$clock] - $this->mParseStartTime[$clock];
1178 }
1179
1199 public function setLimitReportData( $key, $value ) {
1200 $this->mLimitReportData[$key] = $value;
1201
1202 if ( is_array( $value ) ) {
1203 if ( array_keys( $value ) === [ 0, 1 ]
1204 && is_numeric( $value[0] )
1205 && is_numeric( $value[1] )
1206 ) {
1207 $data = [ 'value' => $value[0], 'limit' => $value[1] ];
1208 } else {
1209 $data = $value;
1210 }
1211 } else {
1212 $data = $value;
1213 }
1214
1215 if ( strpos( $key, '-' ) ) {
1216 list( $ns, $name ) = explode( '-', $key, 2 );
1217 $this->mLimitReportJSData[$ns][$name] = $data;
1218 } else {
1219 $this->mLimitReportJSData[$key] = $data;
1220 }
1221 }
1222
1233 public function hasDynamicContent() {
1235
1236 return $this->getCacheExpiry() < $wgParserCacheExpireTime;
1237 }
1238
1246 public function preventClickjacking( $flag = null ) {
1247 return wfSetVar( $this->mPreventClickjacking, $flag );
1248 }
1249
1256 public function updateRuntimeAdaptiveExpiry( $ttl ) {
1257 $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry );
1258 $this->updateCacheExpiry( $ttl );
1259 }
1260
1267 if ( is_infinite( $this->mMaxAdaptiveExpiry ) ) {
1268 return; // not set
1269 }
1270
1271 $runtime = $this->getTimeSinceStart( 'wall' );
1272 if ( is_float( $runtime ) ) {
1273 $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL )
1274 / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC );
1275 // SLOW_AR_TTL = PARSE_SLOW_SEC * $slope + $point
1276 $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope;
1277
1278 $adaptiveTTL = min(
1279 max( $slope * $runtime + $point, self::MIN_AR_TTL ),
1280 $this->mMaxAdaptiveExpiry
1281 );
1282 $this->updateCacheExpiry( $adaptiveTTL );
1283 }
1284 }
1285
1286 public function __sleep() {
1287 return array_diff(
1288 array_keys( get_object_vars( $this ) ),
1289 [ 'mParseStartTime' ]
1290 );
1291 }
1292
1301 $this->mOutputHooks = self::mergeList( $this->mOutputHooks, $source->getOutputHooks() );
1302 $this->mWarnings = self::mergeMap( $this->mWarnings, $source->mWarnings ); // don't use getter
1303 $this->mTimestamp = $this->useMaxValue( $this->mTimestamp, $source->getTimestamp() );
1304
1305 if ( $this->mSpeculativeRevId && $source->mSpeculativeRevId
1306 && $this->mSpeculativeRevId !== $source->mSpeculativeRevId
1307 ) {
1309 'Inconsistent speculative revision ID encountered while merging parser output!'
1310 );
1311 }
1312
1313 $this->mSpeculativeRevId = $this->useMaxValue(
1314 $this->mSpeculativeRevId,
1315 $source->getSpeculativeRevIdUsed()
1316 );
1317 $this->mParseStartTime = $this->useEachMinValue(
1318 $this->mParseStartTime,
1319 $source->mParseStartTime
1320 );
1321
1322 $this->mFlags = self::mergeMap( $this->mFlags, $source->mFlags );
1323 $this->mAccessedOptions = self::mergeMap( $this->mAccessedOptions, $source->mAccessedOptions );
1324
1325 // TODO: maintain per-slot limit reports!
1326 if ( empty( $this->mLimitReportData ) ) {
1327 $this->mLimitReportData = $source->mLimitReportData;
1328 }
1329 if ( empty( $this->mLimitReportJSData ) ) {
1330 $this->mLimitReportJSData = $source->mLimitReportJSData;
1331 }
1332 }
1333
1342 // HTML and HTTP
1343 $this->mHeadItems = self::mergeMixedList( $this->mHeadItems, $source->getHeadItems() );
1344 $this->mModules = self::mergeList( $this->mModules, $source->getModules() );
1345 $this->mModuleScripts = self::mergeList( $this->mModuleScripts, $source->getModuleScripts() );
1346 $this->mModuleStyles = self::mergeList( $this->mModuleStyles, $source->getModuleStyles() );
1347 $this->mJsConfigVars = self::mergeMap( $this->mJsConfigVars, $source->getJsConfigVars() );
1348 $this->mMaxAdaptiveExpiry = min( $this->mMaxAdaptiveExpiry, $source->mMaxAdaptiveExpiry );
1349
1350 // "noindex" always wins!
1351 if ( $this->mIndexPolicy === 'noindex' || $source->mIndexPolicy === 'noindex' ) {
1352 $this->mIndexPolicy = 'noindex';
1353 } elseif ( $this->mIndexPolicy !== 'index' ) {
1354 $this->mIndexPolicy = $source->mIndexPolicy;
1355 }
1356
1357 // Skin control
1358 $this->mNewSection = $this->mNewSection || $source->getNewSection();
1359 $this->mHideNewSection = $this->mHideNewSection || $source->getHideNewSection();
1360 $this->mNoGallery = $this->mNoGallery || $source->getNoGallery();
1361 $this->mEnableOOUI = $this->mEnableOOUI || $source->getEnableOOUI();
1362 $this->mPreventClickjacking = $this->mPreventClickjacking || $source->preventClickjacking();
1363
1364 // TODO: we'll have to be smarter about this!
1365 $this->mSections = array_merge( $this->mSections, $source->getSections() );
1366 $this->mTOCHTML = $this->mTOCHTML . $source->mTOCHTML;
1367
1368 // XXX: we don't want to concatenate title text, so first write wins.
1369 // We should use the first *modified* title text, but we don't have the original to check.
1370 if ( $this->mTitleText === null || $this->mTitleText === '' ) {
1371 $this->mTitleText = $source->mTitleText;
1372 }
1373
1374 // class names are stored in array keys
1375 $this->mWrapperDivClasses = self::mergeMap(
1376 $this->mWrapperDivClasses,
1377 $source->mWrapperDivClasses
1378 );
1379
1380 // NOTE: last write wins, same as within one ParserOutput
1381 $this->mIndicators = self::mergeMap( $this->mIndicators, $source->getIndicators() );
1382
1383 // NOTE: include extension data in "tracking meta data" as well as "html meta data"!
1384 // TODO: add a $mergeStrategy parameter to setExtensionData to allow different
1385 // kinds of extension data to be merged in different ways.
1386 $this->mExtensionData = self::mergeMap(
1387 $this->mExtensionData,
1388 $source->mExtensionData
1389 );
1390 }
1391
1400 $this->mLanguageLinks = self::mergeList( $this->mLanguageLinks, $source->getLanguageLinks() );
1401 $this->mCategories = self::mergeMap( $this->mCategories, $source->getCategories() );
1402 $this->mLinks = self::merge2D( $this->mLinks, $source->getLinks() );
1403 $this->mTemplates = self::merge2D( $this->mTemplates, $source->getTemplates() );
1404 $this->mTemplateIds = self::merge2D( $this->mTemplateIds, $source->getTemplateIds() );
1405 $this->mImages = self::mergeMap( $this->mImages, $source->getImages() );
1406 $this->mFileSearchOptions = self::mergeMap(
1407 $this->mFileSearchOptions,
1408 $source->getFileSearchOptions()
1409 );
1410 $this->mExternalLinks = self::mergeMap( $this->mExternalLinks, $source->getExternalLinks() );
1411 $this->mInterwikiLinks = self::merge2D(
1412 $this->mInterwikiLinks,
1413 $source->getInterwikiLinks()
1414 );
1415
1416 // TODO: add a $mergeStrategy parameter to setProperty to allow different
1417 // kinds of properties to be merged in different ways.
1418 $this->mProperties = self::mergeMap( $this->mProperties, $source->getProperties() );
1419
1420 // NOTE: include extension data in "tracking meta data" as well as "html meta data"!
1421 // TODO: add a $mergeStrategy parameter to setExtensionData to allow different
1422 // kinds of extension data to be merged in different ways.
1423 $this->mExtensionData = self::mergeMap(
1424 $this->mExtensionData,
1425 $source->mExtensionData
1426 );
1427 }
1428
1429 private static function mergeMixedList( array $a, array $b ) {
1430 return array_unique( array_merge( $a, $b ), SORT_REGULAR );
1431 }
1432
1433 private static function mergeList( array $a, array $b ) {
1434 return array_values( array_unique( array_merge( $a, $b ), SORT_REGULAR ) );
1435 }
1436
1437 private static function mergeMap( array $a, array $b ) {
1438 return array_replace( $a, $b );
1439 }
1440
1441 private static function merge2D( array $a, array $b ) {
1442 $values = [];
1443 $keys = array_merge( array_keys( $a ), array_keys( $b ) );
1444
1445 foreach ( $keys as $k ) {
1446 if ( empty( $a[$k] ) ) {
1447 $values[$k] = $b[$k];
1448 } elseif ( empty( $b[$k] ) ) {
1449 $values[$k] = $a[$k];
1450 } elseif ( is_array( $a[$k] ) && is_array( $b[$k] ) ) {
1451 $values[$k] = array_replace( $a[$k], $b[$k] );
1452 } else {
1453 $values[$k] = $b[$k];
1454 }
1455 }
1456
1457 return $values;
1458 }
1459
1460 private static function useEachMinValue( array $a, array $b ) {
1461 $values = [];
1462 $keys = array_merge( array_keys( $a ), array_keys( $b ) );
1463
1464 foreach ( $keys as $k ) {
1465 if ( is_array( $a[$k] ?? null ) && is_array( $b[$k] ?? null ) ) {
1466 $values[$k] = self::useEachMinValue( $a[$k], $b[$k] );
1467 } else {
1468 $values[$k] = self::useMinValue( $a[$k] ?? null, $b[$k] ?? null );
1469 }
1470 }
1471
1472 return $values;
1473 }
1474
1475 private static function useMinValue( $a, $b ) {
1476 if ( $a === null ) {
1477 return $b;
1478 }
1479
1480 if ( $b === null ) {
1481 return $a;
1482 }
1483
1484 return min( $a, $b );
1485 }
1486
1487 private static function useMaxValue( $a, $b ) {
1488 if ( $a === null ) {
1489 return $b;
1490 }
1491
1492 if ( $b === null ) {
1493 return $a;
1494 }
1495
1496 return max( $a, $b );
1497 }
1498
1499}
Apache License January http
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$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.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Parser cache specific expiry check.
Definition CacheTime.php:29
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.
MediaWiki exception.
This class should be covered by a general architecture document which does not exist as of January 20...
static mergeMap(array $a, array $b)
addOutputPageMetadata(OutputPage $out)
Copy items from the OutputPage object into this one.
getProperty( $name)
unsetProperty( $name)
static getTimes( $clock=null)
hideNewSection( $value)
int null $mSpeculativeRevId
Assumed rev ID for {{REVISIONID}} if no revision is set.
setNewSection( $value)
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)
addExternalLink( $url)
static useMaxValue( $a, $b)
addModuleScripts( $modules)
setLanguageLinks( $ll)
setCategoryLinks( $cl)
static merge2D(array $a, array $b)
setNoGallery( $value)
hasText()
Returns true if text was passed to the constructor, or set using setText().
static mergeList(array $a, array $b)
addInterwikiLink( $title)
setEditSectionTokens( $t)
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.
addModules( $modules)
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.
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.
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.
setTOCHTML( $tochtml)
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.
setExtensionData( $key, $value)
Attaches arbitrary data to this ParserObject.
setTimestamp( $timestamp)
setSpeculativeRevIdUsed( $id)
__construct( $text='', $languageLinks=[], $categoryLinks=[], $unused=false, $titletext='')
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...
setTOCEnabled( $flag)
addModuleStyles( $modules)
getWrapperDivClass()
Returns the class (or classes) to be used with the wrapper div for this otuput.
setFlag( $flag)
Fairly generic flag setter thingy.
const EDITSECTION_REGEX
addCategory( $c, $sort)
setIndexPolicy( $policy)
int $mMaxAdaptiveExpiry
Upper bound of expiry based on parse duration.
Represents a title within MediaWiki.
Definition Title.php:39
For a write query
Definition database.txt:26
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
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
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
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
const NS_FILE
Definition Defines.php:70
const NS_SPECIAL
Definition Defines.php:53
const NS_MEDIA
Definition Defines.php:52
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:2050
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:3108
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:2885
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:994
null for the local wiki Added in
Definition hooks.txt:1627
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:2054
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;div ...>$1&lt;/div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition hooks.txt:894
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
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:1514
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:3107
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:37
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))
$source
$content
This document provides an overview of the usage of PageUpdater and that is
$sort
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:24
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