Revised styling of sister-search sidebar.
[lhc/web/wiklou.git] / includes / parser / ParserOptions.php
1 <?php
2 /**
3 * Options for the PHP parser
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Parser
22 */
23 use Wikimedia\ScopedCallback;
24
25 /**
26 * @brief Set options of the Parser
27 *
28 * All member variables are supposed to be private in theory, although in
29 * practice this is not the case.
30 *
31 * @ingroup Parser
32 */
33 class ParserOptions {
34
35 /**
36 * Interlanguage links are removed and returned in an array
37 */
38 private $mInterwikiMagic;
39
40 /**
41 * Allow external images inline?
42 */
43 private $mAllowExternalImages;
44
45 /**
46 * If not, any exception?
47 */
48 private $mAllowExternalImagesFrom;
49
50 /**
51 * If not or it doesn't match, should we check an on-wiki whitelist?
52 */
53 private $mEnableImageWhitelist;
54
55 /**
56 * Date format index
57 */
58 private $mDateFormat = null;
59
60 /**
61 * Create "edit section" links?
62 */
63 private $mEditSection = true;
64
65 /**
66 * Allow inclusion of special pages?
67 */
68 private $mAllowSpecialInclusion;
69
70 /**
71 * Use tidy to cleanup output HTML?
72 */
73 private $mTidy = false;
74
75 /**
76 * Which lang to call for PLURAL and GRAMMAR
77 */
78 private $mInterfaceMessage = false;
79
80 /**
81 * Overrides $mInterfaceMessage with arbitrary language
82 */
83 private $mTargetLanguage = null;
84
85 /**
86 * Maximum size of template expansions, in bytes
87 */
88 private $mMaxIncludeSize;
89
90 /**
91 * Maximum number of nodes touched by PPFrame::expand()
92 */
93 private $mMaxPPNodeCount;
94
95 /**
96 * Maximum number of nodes generated by Preprocessor::preprocessToObj()
97 */
98 private $mMaxGeneratedPPNodeCount;
99
100 /**
101 * Maximum recursion depth in PPFrame::expand()
102 */
103 private $mMaxPPExpandDepth;
104
105 /**
106 * Maximum recursion depth for templates within templates
107 */
108 private $mMaxTemplateDepth;
109
110 /**
111 * Maximum number of calls per parse to expensive parser functions
112 */
113 private $mExpensiveParserFunctionLimit;
114
115 /**
116 * Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
117 */
118 private $mRemoveComments = true;
119
120 /**
121 * @var callable Callback for current revision fetching; first argument to call_user_func().
122 */
123 private $mCurrentRevisionCallback =
124 [ 'Parser', 'statelessFetchRevision' ];
125
126 /**
127 * @var callable Callback for template fetching; first argument to call_user_func().
128 */
129 private $mTemplateCallback =
130 [ 'Parser', 'statelessFetchTemplate' ];
131
132 /**
133 * @var callable|null Callback to generate a guess for {{REVISIONID}}
134 */
135 private $mSpeculativeRevIdCallback;
136
137 /**
138 * Enable limit report in an HTML comment on output
139 */
140 private $mEnableLimitReport = false;
141
142 /**
143 * Timestamp used for {{CURRENTDAY}} etc.
144 */
145 private $mTimestamp;
146
147 /**
148 * Target attribute for external links
149 */
150 private $mExternalLinkTarget;
151
152 /**
153 * Clean up signature texts?
154 * @see Parser::cleanSig
155 */
156 private $mCleanSignatures;
157
158 /**
159 * Transform wiki markup when saving the page?
160 */
161 private $mPreSaveTransform = true;
162
163 /**
164 * Whether content conversion should be disabled
165 */
166 private $mDisableContentConversion;
167
168 /**
169 * Whether title conversion should be disabled
170 */
171 private $mDisableTitleConversion;
172
173 /**
174 * Automatically number headings?
175 */
176 private $mNumberHeadings;
177
178 /**
179 * Thumb size preferred by the user.
180 */
181 private $mThumbSize;
182
183 /**
184 * Maximum article size of an article to be marked as "stub"
185 */
186 private $mStubThreshold;
187
188 /**
189 * Language object of the User language.
190 */
191 private $mUserLang;
192
193 /**
194 * @var User
195 * Stored user object
196 */
197 private $mUser;
198
199 /**
200 * Parsing the page for a "preview" operation?
201 */
202 private $mIsPreview = false;
203
204 /**
205 * Parsing the page for a "preview" operation on a single section?
206 */
207 private $mIsSectionPreview = false;
208
209 /**
210 * Parsing the printable version of the page?
211 */
212 private $mIsPrintable = false;
213
214 /**
215 * Extra key that should be present in the caching key.
216 */
217 private $mExtraKey = '';
218
219 /**
220 * Are magic ISBN links enabled?
221 */
222 private $mMagicISBNLinks = true;
223
224 /**
225 * Are magic PMID links enabled?
226 */
227 private $mMagicPMIDLinks = true;
228
229 /**
230 * Are magic RFC links enabled?
231 */
232 private $mMagicRFCLinks = true;
233
234 /**
235 * Function to be called when an option is accessed.
236 */
237 private $onAccessCallback = null;
238
239 /**
240 * If the page being parsed is a redirect, this should hold the redirect
241 * target.
242 * @var Title|null
243 */
244 private $redirectTarget = null;
245
246 /**
247 * If the wiki is configured to allow raw html ($wgRawHtml = true)
248 * is it allowed in the specific case of parsing this page.
249 *
250 * This is meant to disable unsafe parser tags in cases where
251 * a malicious user may control the input to the parser.
252 *
253 * @note This is expected to be true for normal pages even if the
254 * wiki has $wgRawHtml disabled in general. The setting only
255 * signifies that raw html would be unsafe in the current context
256 * provided that raw html is allowed at all.
257 * @var boolean
258 */
259 private $allowUnsafeRawHtml = true;
260
261 public function getInterwikiMagic() {
262 return $this->mInterwikiMagic;
263 }
264
265 public function getAllowExternalImages() {
266 return $this->mAllowExternalImages;
267 }
268
269 public function getAllowExternalImagesFrom() {
270 return $this->mAllowExternalImagesFrom;
271 }
272
273 public function getEnableImageWhitelist() {
274 return $this->mEnableImageWhitelist;
275 }
276
277 public function getEditSection() {
278 return $this->mEditSection;
279 }
280
281 public function getNumberHeadings() {
282 $this->optionUsed( 'numberheadings' );
283
284 return $this->mNumberHeadings;
285 }
286
287 public function getAllowSpecialInclusion() {
288 return $this->mAllowSpecialInclusion;
289 }
290
291 public function getTidy() {
292 return $this->mTidy;
293 }
294
295 public function getInterfaceMessage() {
296 return $this->mInterfaceMessage;
297 }
298
299 public function getTargetLanguage() {
300 return $this->mTargetLanguage;
301 }
302
303 public function getMaxIncludeSize() {
304 return $this->mMaxIncludeSize;
305 }
306
307 public function getMaxPPNodeCount() {
308 return $this->mMaxPPNodeCount;
309 }
310
311 public function getMaxGeneratedPPNodeCount() {
312 return $this->mMaxGeneratedPPNodeCount;
313 }
314
315 public function getMaxPPExpandDepth() {
316 return $this->mMaxPPExpandDepth;
317 }
318
319 public function getMaxTemplateDepth() {
320 return $this->mMaxTemplateDepth;
321 }
322
323 /* @since 1.20 */
324 public function getExpensiveParserFunctionLimit() {
325 return $this->mExpensiveParserFunctionLimit;
326 }
327
328 public function getRemoveComments() {
329 return $this->mRemoveComments;
330 }
331
332 /* @since 1.24 */
333 public function getCurrentRevisionCallback() {
334 return $this->mCurrentRevisionCallback;
335 }
336
337 public function getTemplateCallback() {
338 return $this->mTemplateCallback;
339 }
340
341 /** @since 1.28 */
342 public function getSpeculativeRevIdCallback() {
343 return $this->mSpeculativeRevIdCallback;
344 }
345
346 public function getEnableLimitReport() {
347 return $this->mEnableLimitReport;
348 }
349
350 public function getCleanSignatures() {
351 return $this->mCleanSignatures;
352 }
353
354 public function getExternalLinkTarget() {
355 return $this->mExternalLinkTarget;
356 }
357
358 public function getDisableContentConversion() {
359 return $this->mDisableContentConversion;
360 }
361
362 public function getDisableTitleConversion() {
363 return $this->mDisableTitleConversion;
364 }
365
366 public function getThumbSize() {
367 $this->optionUsed( 'thumbsize' );
368
369 return $this->mThumbSize;
370 }
371
372 public function getStubThreshold() {
373 $this->optionUsed( 'stubthreshold' );
374
375 return $this->mStubThreshold;
376 }
377
378 public function getIsPreview() {
379 return $this->mIsPreview;
380 }
381
382 public function getIsSectionPreview() {
383 return $this->mIsSectionPreview;
384 }
385
386 public function getIsPrintable() {
387 $this->optionUsed( 'printable' );
388
389 return $this->mIsPrintable;
390 }
391
392 public function getUser() {
393 return $this->mUser;
394 }
395
396 public function getPreSaveTransform() {
397 return $this->mPreSaveTransform;
398 }
399
400 public function getDateFormat() {
401 $this->optionUsed( 'dateformat' );
402 if ( !isset( $this->mDateFormat ) ) {
403 $this->mDateFormat = $this->mUser->getDatePreference();
404 }
405 return $this->mDateFormat;
406 }
407
408 public function getTimestamp() {
409 if ( !isset( $this->mTimestamp ) ) {
410 $this->mTimestamp = wfTimestampNow();
411 }
412 return $this->mTimestamp;
413 }
414
415 /**
416 * Get the user language used by the parser for this page and split the parser cache.
417 *
418 * @warning: Calling this causes the parser cache to be fragmented by user language!
419 * To avoid cache fragmentation, output should not depend on the user language.
420 * Use Parser::getFunctionLang() or Parser::getTargetLanguage() instead!
421 *
422 * @note This function will trigger a cache fragmentation by recording the
423 * 'userlang' option, see optionUsed(). This is done to avoid cache pollution
424 * when the page is rendered based on the language of the user.
425 *
426 * @note When saving, this will return the default language instead of the user's.
427 * {{int: }} uses this which used to produce inconsistent link tables (T16404).
428 *
429 * @return Language
430 * @since 1.19
431 */
432 public function getUserLangObj() {
433 $this->optionUsed( 'userlang' );
434 return $this->mUserLang;
435 }
436
437 /**
438 * Same as getUserLangObj() but returns a string instead.
439 *
440 * @warning: Calling this causes the parser cache to be fragmented by user language!
441 * To avoid cache fragmentation, output should not depend on the user language.
442 * Use Parser::getFunctionLang() or Parser::getTargetLanguage() instead!
443 *
444 * @see getUserLangObj()
445 *
446 * @return string Language code
447 * @since 1.17
448 */
449 public function getUserLang() {
450 return $this->getUserLangObj()->getCode();
451 }
452
453 /**
454 * @since 1.28
455 * @return bool
456 */
457 public function getMagicISBNLinks() {
458 return $this->mMagicISBNLinks;
459 }
460
461 /**
462 * @since 1.28
463 * @return bool
464 */
465 public function getMagicPMIDLinks() {
466 return $this->mMagicPMIDLinks;
467 }
468 /**
469 * @since 1.28
470 * @return bool
471 */
472 public function getMagicRFCLinks() {
473 return $this->mMagicRFCLinks;
474 }
475
476 /**
477 * @since 1.29
478 * @return bool
479 */
480 public function getAllowUnsafeRawHtml() {
481 return $this->allowUnsafeRawHtml;
482 }
483
484 public function setInterwikiMagic( $x ) {
485 return wfSetVar( $this->mInterwikiMagic, $x );
486 }
487
488 public function setAllowExternalImages( $x ) {
489 return wfSetVar( $this->mAllowExternalImages, $x );
490 }
491
492 public function setAllowExternalImagesFrom( $x ) {
493 return wfSetVar( $this->mAllowExternalImagesFrom, $x );
494 }
495
496 public function setEnableImageWhitelist( $x ) {
497 return wfSetVar( $this->mEnableImageWhitelist, $x );
498 }
499
500 public function setDateFormat( $x ) {
501 return wfSetVar( $this->mDateFormat, $x );
502 }
503
504 public function setEditSection( $x ) {
505 return wfSetVar( $this->mEditSection, $x );
506 }
507
508 public function setNumberHeadings( $x ) {
509 return wfSetVar( $this->mNumberHeadings, $x );
510 }
511
512 public function setAllowSpecialInclusion( $x ) {
513 return wfSetVar( $this->mAllowSpecialInclusion, $x );
514 }
515
516 public function setTidy( $x ) {
517 return wfSetVar( $this->mTidy, $x );
518 }
519
520 public function setInterfaceMessage( $x ) {
521 return wfSetVar( $this->mInterfaceMessage, $x );
522 }
523
524 public function setTargetLanguage( $x ) {
525 return wfSetVar( $this->mTargetLanguage, $x, true );
526 }
527
528 public function setMaxIncludeSize( $x ) {
529 return wfSetVar( $this->mMaxIncludeSize, $x );
530 }
531
532 public function setMaxPPNodeCount( $x ) {
533 return wfSetVar( $this->mMaxPPNodeCount, $x );
534 }
535
536 public function setMaxGeneratedPPNodeCount( $x ) {
537 return wfSetVar( $this->mMaxGeneratedPPNodeCount, $x );
538 }
539
540 public function setMaxTemplateDepth( $x ) {
541 return wfSetVar( $this->mMaxTemplateDepth, $x );
542 }
543
544 /* @since 1.20 */
545 public function setExpensiveParserFunctionLimit( $x ) {
546 return wfSetVar( $this->mExpensiveParserFunctionLimit, $x );
547 }
548
549 public function setRemoveComments( $x ) {
550 return wfSetVar( $this->mRemoveComments, $x );
551 }
552
553 /* @since 1.24 */
554 public function setCurrentRevisionCallback( $x ) {
555 return wfSetVar( $this->mCurrentRevisionCallback, $x );
556 }
557
558 /** @since 1.28 */
559 public function setSpeculativeRevIdCallback( $x ) {
560 return wfSetVar( $this->mSpeculativeRevIdCallback, $x );
561 }
562
563 public function setTemplateCallback( $x ) {
564 return wfSetVar( $this->mTemplateCallback, $x );
565 }
566
567 public function enableLimitReport( $x = true ) {
568 return wfSetVar( $this->mEnableLimitReport, $x );
569 }
570
571 public function setTimestamp( $x ) {
572 return wfSetVar( $this->mTimestamp, $x );
573 }
574
575 public function setCleanSignatures( $x ) {
576 return wfSetVar( $this->mCleanSignatures, $x );
577 }
578
579 public function setExternalLinkTarget( $x ) {
580 return wfSetVar( $this->mExternalLinkTarget, $x );
581 }
582
583 public function disableContentConversion( $x = true ) {
584 return wfSetVar( $this->mDisableContentConversion, $x );
585 }
586
587 public function disableTitleConversion( $x = true ) {
588 return wfSetVar( $this->mDisableTitleConversion, $x );
589 }
590
591 public function setUserLang( $x ) {
592 if ( is_string( $x ) ) {
593 $x = Language::factory( $x );
594 }
595
596 return wfSetVar( $this->mUserLang, $x );
597 }
598
599 public function setThumbSize( $x ) {
600 return wfSetVar( $this->mThumbSize, $x );
601 }
602
603 public function setStubThreshold( $x ) {
604 return wfSetVar( $this->mStubThreshold, $x );
605 }
606
607 public function setPreSaveTransform( $x ) {
608 return wfSetVar( $this->mPreSaveTransform, $x );
609 }
610
611 public function setIsPreview( $x ) {
612 return wfSetVar( $this->mIsPreview, $x );
613 }
614
615 public function setIsSectionPreview( $x ) {
616 return wfSetVar( $this->mIsSectionPreview, $x );
617 }
618
619 public function setIsPrintable( $x ) {
620 return wfSetVar( $this->mIsPrintable, $x );
621 }
622
623 /**
624 * @param bool|null Value to set or null to get current value
625 * @return bool Current value for allowUnsafeRawHtml
626 * @since 1.29
627 */
628 public function setAllowUnsafeRawHtml( $x ) {
629 return wfSetVar( $this->allowUnsafeRawHtml, $x );
630 }
631
632 /**
633 * Set the redirect target.
634 *
635 * Note that setting or changing this does not *make* the page a redirect
636 * or change its target, it merely records the information for reference
637 * during the parse.
638 *
639 * @since 1.24
640 * @param Title|null $title
641 */
642 function setRedirectTarget( $title ) {
643 $this->redirectTarget = $title;
644 }
645
646 /**
647 * Get the previously-set redirect target.
648 *
649 * @since 1.24
650 * @return Title|null
651 */
652 function getRedirectTarget() {
653 return $this->redirectTarget;
654 }
655
656 /**
657 * Extra key that should be present in the parser cache key.
658 * @param string $key
659 */
660 public function addExtraKey( $key ) {
661 $this->mExtraKey .= '!' . $key;
662 }
663
664 /**
665 * Constructor
666 * @param User $user
667 * @param Language $lang
668 */
669 public function __construct( $user = null, $lang = null ) {
670 if ( $user === null ) {
671 global $wgUser;
672 if ( $wgUser === null ) {
673 $user = new User;
674 } else {
675 $user = $wgUser;
676 }
677 }
678 if ( $lang === null ) {
679 global $wgLang;
680 if ( !StubObject::isRealObject( $wgLang ) ) {
681 $wgLang->_unstub();
682 }
683 $lang = $wgLang;
684 }
685 $this->initialiseFromUser( $user, $lang );
686 }
687
688 /**
689 * Get a ParserOptions object for an anonymous user
690 * @since 1.27
691 * @return ParserOptions
692 */
693 public static function newFromAnon() {
694 global $wgContLang;
695 return new ParserOptions( new User, $wgContLang );
696 }
697
698 /**
699 * Get a ParserOptions object from a given user.
700 * Language will be taken from $wgLang.
701 *
702 * @param User $user
703 * @return ParserOptions
704 */
705 public static function newFromUser( $user ) {
706 return new ParserOptions( $user );
707 }
708
709 /**
710 * Get a ParserOptions object from a given user and language
711 *
712 * @param User $user
713 * @param Language $lang
714 * @return ParserOptions
715 */
716 public static function newFromUserAndLang( User $user, Language $lang ) {
717 return new ParserOptions( $user, $lang );
718 }
719
720 /**
721 * Get a ParserOptions object from a IContextSource object
722 *
723 * @param IContextSource $context
724 * @return ParserOptions
725 */
726 public static function newFromContext( IContextSource $context ) {
727 return new ParserOptions( $context->getUser(), $context->getLanguage() );
728 }
729
730 /**
731 * Get user options
732 *
733 * @param User $user
734 * @param Language $lang
735 */
736 private function initialiseFromUser( $user, $lang ) {
737 global $wgInterwikiMagic, $wgAllowExternalImages,
738 $wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion,
739 $wgMaxArticleSize, $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth,
740 $wgCleanSignatures, $wgExternalLinkTarget, $wgExpensiveParserFunctionLimit,
741 $wgMaxGeneratedPPNodeCount, $wgDisableLangConversion, $wgDisableTitleConversion,
742 $wgEnableMagicLinks;
743
744 // *UPDATE* ParserOptions::matches() if any of this changes as needed
745 $this->mInterwikiMagic = $wgInterwikiMagic;
746 $this->mAllowExternalImages = $wgAllowExternalImages;
747 $this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
748 $this->mEnableImageWhitelist = $wgEnableImageWhitelist;
749 $this->mAllowSpecialInclusion = $wgAllowSpecialInclusion;
750 $this->mMaxIncludeSize = $wgMaxArticleSize * 1024;
751 $this->mMaxPPNodeCount = $wgMaxPPNodeCount;
752 $this->mMaxGeneratedPPNodeCount = $wgMaxGeneratedPPNodeCount;
753 $this->mMaxPPExpandDepth = $wgMaxPPExpandDepth;
754 $this->mMaxTemplateDepth = $wgMaxTemplateDepth;
755 $this->mExpensiveParserFunctionLimit = $wgExpensiveParserFunctionLimit;
756 $this->mCleanSignatures = $wgCleanSignatures;
757 $this->mExternalLinkTarget = $wgExternalLinkTarget;
758 $this->mDisableContentConversion = $wgDisableLangConversion;
759 $this->mDisableTitleConversion = $wgDisableLangConversion || $wgDisableTitleConversion;
760 $this->mMagicISBNLinks = $wgEnableMagicLinks['ISBN'];
761 $this->mMagicPMIDLinks = $wgEnableMagicLinks['PMID'];
762 $this->mMagicRFCLinks = $wgEnableMagicLinks['RFC'];
763
764 $this->mUser = $user;
765 $this->mNumberHeadings = $user->getOption( 'numberheadings' );
766 $this->mThumbSize = $user->getOption( 'thumbsize' );
767 $this->mStubThreshold = $user->getStubThreshold();
768 $this->mUserLang = $lang;
769 }
770
771 /**
772 * Check if these options match that of another options set
773 *
774 * This ignores report limit settings that only affect HTML comments
775 *
776 * @param ParserOptions $other
777 * @return bool
778 * @since 1.25
779 */
780 public function matches( ParserOptions $other ) {
781 $fields = array_keys( get_class_vars( __CLASS__ ) );
782 $fields = array_diff( $fields, [
783 'mEnableLimitReport', // only effects HTML comments
784 'onAccessCallback', // only used for ParserOutput option tracking
785 ] );
786 foreach ( $fields as $field ) {
787 if ( !is_object( $this->$field ) && $this->$field !== $other->$field ) {
788 return false;
789 }
790 }
791 // Check the object and lazy-loaded options
792 return (
793 $this->mUserLang->equals( $other->mUserLang ) &&
794 $this->getDateFormat() === $other->getDateFormat()
795 );
796 }
797
798 /**
799 * Registers a callback for tracking which ParserOptions which are used.
800 * This is a private API with the parser.
801 * @param callable $callback
802 */
803 public function registerWatcher( $callback ) {
804 $this->onAccessCallback = $callback;
805 }
806
807 /**
808 * Called when an option is accessed.
809 * Calls the watcher that was set using registerWatcher().
810 * Typically, the watcher callback is ParserOutput::registerOption().
811 * The information registered that way will be used by ParserCache::save().
812 *
813 * @param string $optionName Name of the option
814 */
815 public function optionUsed( $optionName ) {
816 if ( $this->onAccessCallback ) {
817 call_user_func( $this->onAccessCallback, $optionName );
818 }
819 }
820
821 /**
822 * Returns the full array of options that would have been used by
823 * in 1.16.
824 * Used to get the old parser cache entries when available.
825 * @return array
826 */
827 public static function legacyOptions() {
828 return [
829 'stubthreshold',
830 'numberheadings',
831 'userlang',
832 'thumbsize',
833 'editsection',
834 'printable'
835 ];
836 }
837
838 /**
839 * Generate a hash string with the values set on these ParserOptions
840 * for the keys given in the array.
841 * This will be used as part of the hash key for the parser cache,
842 * so users sharing the options with vary for the same page share
843 * the same cached data safely.
844 *
845 * Extensions which require it should install 'PageRenderingHash' hook,
846 * which will give them a chance to modify this key based on their own
847 * settings.
848 *
849 * @since 1.17
850 * @param array $forOptions
851 * @param Title $title Used to get the content language of the page (since r97636)
852 * @return string Page rendering hash
853 */
854 public function optionsHash( $forOptions, $title = null ) {
855 global $wgRenderHashAppend;
856
857 // FIXME: Once the cache key is reorganized this argument
858 // can be dropped. It was used when the math extension was
859 // part of core.
860 $confstr = '*';
861
862 // Space assigned for the stubthreshold but unused
863 // since it disables the parser cache, its value will always
864 // be 0 when this function is called by parsercache.
865 if ( in_array( 'stubthreshold', $forOptions ) ) {
866 $confstr .= '!' . $this->mStubThreshold;
867 } else {
868 $confstr .= '!*';
869 }
870
871 if ( in_array( 'dateformat', $forOptions ) ) {
872 $confstr .= '!' . $this->getDateFormat();
873 }
874
875 if ( in_array( 'numberheadings', $forOptions ) ) {
876 $confstr .= '!' . ( $this->mNumberHeadings ? '1' : '' );
877 } else {
878 $confstr .= '!*';
879 }
880
881 if ( in_array( 'userlang', $forOptions ) ) {
882 $confstr .= '!' . $this->mUserLang->getCode();
883 } else {
884 $confstr .= '!*';
885 }
886
887 if ( in_array( 'thumbsize', $forOptions ) ) {
888 $confstr .= '!' . $this->mThumbSize;
889 } else {
890 $confstr .= '!*';
891 }
892
893 // add in language specific options, if any
894 // @todo FIXME: This is just a way of retrieving the url/user preferred variant
895 if ( !is_null( $title ) ) {
896 $confstr .= $title->getPageLanguage()->getExtraHashOptions();
897 } else {
898 global $wgContLang;
899 $confstr .= $wgContLang->getExtraHashOptions();
900 }
901
902 $confstr .= $wgRenderHashAppend;
903
904 // @note: as of Feb 2015, core never sets the editsection flag, since it uses
905 // <mw:editsection> tags to inject editsections on the fly. However, extensions
906 // may be using it by calling ParserOption::optionUsed resp. ParserOutput::registerOption
907 // directly. At least Wikibase does at this point in time.
908 if ( !in_array( 'editsection', $forOptions ) ) {
909 $confstr .= '!*';
910 } elseif ( !$this->mEditSection ) {
911 $confstr .= '!edit=0';
912 }
913
914 if ( $this->mIsPrintable && in_array( 'printable', $forOptions ) ) {
915 $confstr .= '!printable=1';
916 }
917
918 if ( $this->mExtraKey != '' ) {
919 $confstr .= $this->mExtraKey;
920 }
921
922 // Give a chance for extensions to modify the hash, if they have
923 // extra options or other effects on the parser cache.
924 Hooks::run( 'PageRenderingHash', [ &$confstr, $this->getUser(), &$forOptions ] );
925
926 // Make it a valid memcached key fragment
927 $confstr = str_replace( ' ', '_', $confstr );
928
929 return $confstr;
930 }
931
932 /**
933 * Sets a hook to force that a page exists, and sets a current revision callback to return
934 * a revision with custom content when the current revision of the page is requested.
935 *
936 * @since 1.25
937 * @param Title $title
938 * @param Content $content
939 * @param User $user The user that the fake revision is attributed to
940 * @return ScopedCallback to unset the hook
941 */
942 public function setupFakeRevision( $title, $content, $user ) {
943 $oldCallback = $this->setCurrentRevisionCallback(
944 function (
945 $titleToCheck, $parser = false ) use ( $title, $content, $user, &$oldCallback
946 ) {
947 if ( $titleToCheck->equals( $title ) ) {
948 return new Revision( [
949 'page' => $title->getArticleID(),
950 'user_text' => $user->getName(),
951 'user' => $user->getId(),
952 'parent_id' => $title->getLatestRevID(),
953 'title' => $title,
954 'content' => $content
955 ] );
956 } else {
957 return call_user_func( $oldCallback, $titleToCheck, $parser );
958 }
959 }
960 );
961
962 global $wgHooks;
963 $wgHooks['TitleExists'][] =
964 function ( $titleToCheck, &$exists ) use ( $title ) {
965 if ( $titleToCheck->equals( $title ) ) {
966 $exists = true;
967 }
968 };
969 end( $wgHooks['TitleExists'] );
970 $key = key( $wgHooks['TitleExists'] );
971 LinkCache::singleton()->clearBadLink( $title->getPrefixedDBkey() );
972 return new ScopedCallback( function () use ( $title, $key ) {
973 global $wgHooks;
974 unset( $wgHooks['TitleExists'][$key] );
975 LinkCache::singleton()->clearLink( $title );
976 } );
977 }
978 }