Merge "Fixed wrong EnqueueJob comment"
[mediawiki.git] / includes / parser / ParserOptions.php
blob100656d1e29c6731bb4b0f5bfa6d59e1cd48c84f
1 <?php
2 /**
3 * Options for the PHP parser
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.
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.
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
20 * @file
21 * @ingroup Parser
24 /**
25 * @brief Set options of the Parser
27 * All member variables are supposed to be private in theory, although in
28 * practice this is not the case.
30 * @ingroup Parser
32 class ParserOptions {
34 /**
35 * Interlanguage links are removed and returned in an array
37 public $mInterwikiMagic;
39 /**
40 * Allow external images inline?
42 public $mAllowExternalImages;
44 /**
45 * If not, any exception?
47 public $mAllowExternalImagesFrom;
49 /**
50 * If not or it doesn't match, should we check an on-wiki whitelist?
52 public $mEnableImageWhitelist;
54 /**
55 * Date format index
57 public $mDateFormat = null;
59 /**
60 * Create "edit section" links?
62 public $mEditSection = true;
64 /**
65 * Allow inclusion of special pages?
67 public $mAllowSpecialInclusion;
69 /**
70 * Use tidy to cleanup output HTML?
72 public $mTidy = false;
74 /**
75 * Which lang to call for PLURAL and GRAMMAR
77 public $mInterfaceMessage = false;
79 /**
80 * Overrides $mInterfaceMessage with arbitrary language
82 public $mTargetLanguage = null;
84 /**
85 * Maximum size of template expansions, in bytes
87 public $mMaxIncludeSize;
89 /**
90 * Maximum number of nodes touched by PPFrame::expand()
92 public $mMaxPPNodeCount;
94 /**
95 * Maximum number of nodes generated by Preprocessor::preprocessToObj()
97 public $mMaxGeneratedPPNodeCount;
99 /**
100 * Maximum recursion depth in PPFrame::expand()
102 public $mMaxPPExpandDepth;
105 * Maximum recursion depth for templates within templates
107 public $mMaxTemplateDepth;
110 * Maximum number of calls per parse to expensive parser functions
112 public $mExpensiveParserFunctionLimit;
115 * Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
117 public $mRemoveComments = true;
120 * Callback for current revision fetching. Used as first argument to call_user_func().
122 public $mCurrentRevisionCallback =
123 array( 'Parser', 'statelessFetchRevision' );
126 * Callback for template fetching. Used as first argument to call_user_func().
128 public $mTemplateCallback =
129 array( 'Parser', 'statelessFetchTemplate' );
132 * Enable limit report in an HTML comment on output
134 public $mEnableLimitReport = false;
137 * Timestamp used for {{CURRENTDAY}} etc.
139 public $mTimestamp;
142 * Target attribute for external links
144 public $mExternalLinkTarget;
147 * Clean up signature texts?
148 * @see Parser::cleanSig
150 public $mCleanSignatures;
153 * Transform wiki markup when saving the page?
155 public $mPreSaveTransform = true;
158 * Whether content conversion should be disabled
160 public $mDisableContentConversion;
163 * Whether title conversion should be disabled
165 public $mDisableTitleConversion;
168 * Automatically number headings?
170 public $mNumberHeadings;
173 * Thumb size preferred by the user.
175 public $mThumbSize;
178 * Maximum article size of an article to be marked as "stub"
180 private $mStubThreshold;
183 * Language object of the User language.
185 public $mUserLang;
188 * @var User
189 * Stored user object
191 public $mUser;
194 * Parsing the page for a "preview" operation?
196 public $mIsPreview = false;
199 * Parsing the page for a "preview" operation on a single section?
201 public $mIsSectionPreview = false;
204 * Parsing the printable version of the page?
206 public $mIsPrintable = false;
209 * Extra key that should be present in the caching key.
211 public $mExtraKey = '';
214 * Function to be called when an option is accessed.
216 protected $onAccessCallback = null;
219 * If the page being parsed is a redirect, this should hold the redirect
220 * target.
221 * @var Title|null
223 private $redirectTarget = null;
225 public function getInterwikiMagic() {
226 return $this->mInterwikiMagic;
229 public function getAllowExternalImages() {
230 return $this->mAllowExternalImages;
233 public function getAllowExternalImagesFrom() {
234 return $this->mAllowExternalImagesFrom;
237 public function getEnableImageWhitelist() {
238 return $this->mEnableImageWhitelist;
241 public function getEditSection() {
242 return $this->mEditSection;
245 public function getNumberHeadings() {
246 $this->optionUsed( 'numberheadings' );
248 return $this->mNumberHeadings;
251 public function getAllowSpecialInclusion() {
252 return $this->mAllowSpecialInclusion;
255 public function getTidy() {
256 return $this->mTidy;
259 public function getInterfaceMessage() {
260 return $this->mInterfaceMessage;
263 public function getTargetLanguage() {
264 return $this->mTargetLanguage;
267 public function getMaxIncludeSize() {
268 return $this->mMaxIncludeSize;
271 public function getMaxPPNodeCount() {
272 return $this->mMaxPPNodeCount;
275 public function getMaxGeneratedPPNodeCount() {
276 return $this->mMaxGeneratedPPNodeCount;
279 public function getMaxPPExpandDepth() {
280 return $this->mMaxPPExpandDepth;
283 public function getMaxTemplateDepth() {
284 return $this->mMaxTemplateDepth;
287 /* @since 1.20 */
288 public function getExpensiveParserFunctionLimit() {
289 return $this->mExpensiveParserFunctionLimit;
292 public function getRemoveComments() {
293 return $this->mRemoveComments;
296 /* @since 1.24 */
297 public function getCurrentRevisionCallback() {
298 return $this->mCurrentRevisionCallback;
301 public function getTemplateCallback() {
302 return $this->mTemplateCallback;
305 public function getEnableLimitReport() {
306 return $this->mEnableLimitReport;
309 public function getCleanSignatures() {
310 return $this->mCleanSignatures;
313 public function getExternalLinkTarget() {
314 return $this->mExternalLinkTarget;
317 public function getDisableContentConversion() {
318 return $this->mDisableContentConversion;
321 public function getDisableTitleConversion() {
322 return $this->mDisableTitleConversion;
325 public function getThumbSize() {
326 $this->optionUsed( 'thumbsize' );
328 return $this->mThumbSize;
331 public function getStubThreshold() {
332 $this->optionUsed( 'stubthreshold' );
334 return $this->mStubThreshold;
337 public function getIsPreview() {
338 return $this->mIsPreview;
341 public function getIsSectionPreview() {
342 return $this->mIsSectionPreview;
345 public function getIsPrintable() {
346 $this->optionUsed( 'printable' );
348 return $this->mIsPrintable;
351 public function getUser() {
352 return $this->mUser;
355 public function getPreSaveTransform() {
356 return $this->mPreSaveTransform;
359 public function getDateFormat() {
360 $this->optionUsed( 'dateformat' );
361 if ( !isset( $this->mDateFormat ) ) {
362 $this->mDateFormat = $this->mUser->getDatePreference();
364 return $this->mDateFormat;
367 public function getTimestamp() {
368 if ( !isset( $this->mTimestamp ) ) {
369 $this->mTimestamp = wfTimestampNow();
371 return $this->mTimestamp;
375 * Get the user language used by the parser for this page.
377 * You shouldn't use this. Really. $parser->getFunctionLang() is all you need.
379 * To avoid side-effects where the page will be rendered based on the language
380 * of the user who last saved, this function will triger a cache fragmentation.
381 * Usage of this method is discouraged for that reason.
383 * When saving, this will return the default language instead of the user's.
385 * {{int: }} uses this which used to produce inconsistent link tables (bug 14404).
387 * @return Language
388 * @since 1.19
390 public function getUserLangObj() {
391 $this->optionUsed( 'userlang' );
392 return $this->mUserLang;
396 * Same as getUserLangObj() but returns a string instead.
398 * @return string Language code
399 * @since 1.17
401 public function getUserLang() {
402 return $this->getUserLangObj()->getCode();
405 public function setInterwikiMagic( $x ) {
406 return wfSetVar( $this->mInterwikiMagic, $x );
409 public function setAllowExternalImages( $x ) {
410 return wfSetVar( $this->mAllowExternalImages, $x );
413 public function setAllowExternalImagesFrom( $x ) {
414 return wfSetVar( $this->mAllowExternalImagesFrom, $x );
417 public function setEnableImageWhitelist( $x ) {
418 return wfSetVar( $this->mEnableImageWhitelist, $x );
421 public function setDateFormat( $x ) {
422 return wfSetVar( $this->mDateFormat, $x );
425 public function setEditSection( $x ) {
426 return wfSetVar( $this->mEditSection, $x );
429 public function setNumberHeadings( $x ) {
430 return wfSetVar( $this->mNumberHeadings, $x );
433 public function setAllowSpecialInclusion( $x ) {
434 return wfSetVar( $this->mAllowSpecialInclusion, $x );
437 public function setTidy( $x ) {
438 return wfSetVar( $this->mTidy, $x );
441 public function setInterfaceMessage( $x ) {
442 return wfSetVar( $this->mInterfaceMessage, $x );
445 public function setTargetLanguage( $x ) {
446 return wfSetVar( $this->mTargetLanguage, $x, true );
449 public function setMaxIncludeSize( $x ) {
450 return wfSetVar( $this->mMaxIncludeSize, $x );
453 public function setMaxPPNodeCount( $x ) {
454 return wfSetVar( $this->mMaxPPNodeCount, $x );
457 public function setMaxGeneratedPPNodeCount( $x ) {
458 return wfSetVar( $this->mMaxGeneratedPPNodeCount, $x );
461 public function setMaxTemplateDepth( $x ) {
462 return wfSetVar( $this->mMaxTemplateDepth, $x );
465 /* @since 1.20 */
466 public function setExpensiveParserFunctionLimit( $x ) {
467 return wfSetVar( $this->mExpensiveParserFunctionLimit, $x );
470 public function setRemoveComments( $x ) {
471 return wfSetVar( $this->mRemoveComments, $x );
474 /* @since 1.24 */
475 public function setCurrentRevisionCallback( $x ) {
476 return wfSetVar( $this->mCurrentRevisionCallback, $x );
479 public function setTemplateCallback( $x ) {
480 return wfSetVar( $this->mTemplateCallback, $x );
483 public function enableLimitReport( $x = true ) {
484 return wfSetVar( $this->mEnableLimitReport, $x );
487 public function setTimestamp( $x ) {
488 return wfSetVar( $this->mTimestamp, $x );
491 public function setCleanSignatures( $x ) {
492 return wfSetVar( $this->mCleanSignatures, $x );
495 public function setExternalLinkTarget( $x ) {
496 return wfSetVar( $this->mExternalLinkTarget, $x );
499 public function disableContentConversion( $x = true ) {
500 return wfSetVar( $this->mDisableContentConversion, $x );
503 public function disableTitleConversion( $x = true ) {
504 return wfSetVar( $this->mDisableTitleConversion, $x );
507 public function setUserLang( $x ) {
508 if ( is_string( $x ) ) {
509 $x = Language::factory( $x );
512 return wfSetVar( $this->mUserLang, $x );
515 public function setThumbSize( $x ) {
516 return wfSetVar( $this->mThumbSize, $x );
519 public function setStubThreshold( $x ) {
520 return wfSetVar( $this->mStubThreshold, $x );
523 public function setPreSaveTransform( $x ) {
524 return wfSetVar( $this->mPreSaveTransform, $x );
527 public function setIsPreview( $x ) {
528 return wfSetVar( $this->mIsPreview, $x );
531 public function setIsSectionPreview( $x ) {
532 return wfSetVar( $this->mIsSectionPreview, $x );
535 public function setIsPrintable( $x ) {
536 return wfSetVar( $this->mIsPrintable, $x );
540 * Set the redirect target.
542 * Note that setting or changing this does not *make* the page a redirect
543 * or change its target, it merely records the information for reference
544 * during the parse.
546 * @since 1.24
547 * @param Title|null $title
549 function setRedirectTarget( $title ) {
550 $this->redirectTarget = $title;
554 * Get the previously-set redirect target.
556 * @since 1.24
557 * @return Title|null
559 function getRedirectTarget() {
560 return $this->redirectTarget;
564 * Extra key that should be present in the parser cache key.
565 * @param string $key
567 public function addExtraKey( $key ) {
568 $this->mExtraKey .= '!' . $key;
572 * Constructor
573 * @param User $user
574 * @param Language $lang
576 public function __construct( $user = null, $lang = null ) {
577 if ( $user === null ) {
578 global $wgUser;
579 if ( $wgUser === null ) {
580 $user = new User;
581 } else {
582 $user = $wgUser;
585 if ( $lang === null ) {
586 global $wgLang;
587 if ( !StubObject::isRealObject( $wgLang ) ) {
588 $wgLang->_unstub();
590 $lang = $wgLang;
592 $this->initialiseFromUser( $user, $lang );
596 * Get a ParserOptions object from a given user.
597 * Language will be taken from $wgLang.
599 * @param User $user
600 * @return ParserOptions
602 public static function newFromUser( $user ) {
603 return new ParserOptions( $user );
607 * Get a ParserOptions object from a given user and language
609 * @param User $user
610 * @param Language $lang
611 * @return ParserOptions
613 public static function newFromUserAndLang( User $user, Language $lang ) {
614 return new ParserOptions( $user, $lang );
618 * Get a ParserOptions object from a IContextSource object
620 * @param IContextSource $context
621 * @return ParserOptions
623 public static function newFromContext( IContextSource $context ) {
624 return new ParserOptions( $context->getUser(), $context->getLanguage() );
628 * Get user options
630 * @param User $user
631 * @param Language $lang
633 private function initialiseFromUser( $user, $lang ) {
634 global $wgInterwikiMagic, $wgAllowExternalImages,
635 $wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion,
636 $wgMaxArticleSize, $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth,
637 $wgCleanSignatures, $wgExternalLinkTarget, $wgExpensiveParserFunctionLimit,
638 $wgMaxGeneratedPPNodeCount, $wgDisableLangConversion, $wgDisableTitleConversion;
640 // *UPDATE* ParserOptions::matches() if any of this changes as needed
641 $this->mInterwikiMagic = $wgInterwikiMagic;
642 $this->mAllowExternalImages = $wgAllowExternalImages;
643 $this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
644 $this->mEnableImageWhitelist = $wgEnableImageWhitelist;
645 $this->mAllowSpecialInclusion = $wgAllowSpecialInclusion;
646 $this->mMaxIncludeSize = $wgMaxArticleSize * 1024;
647 $this->mMaxPPNodeCount = $wgMaxPPNodeCount;
648 $this->mMaxGeneratedPPNodeCount = $wgMaxGeneratedPPNodeCount;
649 $this->mMaxPPExpandDepth = $wgMaxPPExpandDepth;
650 $this->mMaxTemplateDepth = $wgMaxTemplateDepth;
651 $this->mExpensiveParserFunctionLimit = $wgExpensiveParserFunctionLimit;
652 $this->mCleanSignatures = $wgCleanSignatures;
653 $this->mExternalLinkTarget = $wgExternalLinkTarget;
654 $this->mDisableContentConversion = $wgDisableLangConversion;
655 $this->mDisableTitleConversion = $wgDisableLangConversion || $wgDisableTitleConversion;
657 $this->mUser = $user;
658 $this->mNumberHeadings = $user->getOption( 'numberheadings' );
659 $this->mThumbSize = $user->getOption( 'thumbsize' );
660 $this->mStubThreshold = $user->getStubThreshold();
661 $this->mUserLang = $lang;
666 * Check if these options match that of another options set
668 * This ignores report limit settings that only affect HTML comments
670 * @param ParserOptions $other
671 * @return bool
672 * @since 1.25
674 public function matches( ParserOptions $other ) {
675 $fields = array_keys( get_class_vars( __CLASS__ ) );
676 $fields = array_diff( $fields, array(
677 'mEnableLimitReport', // only effects HTML comments
678 'onAccessCallback', // only used for ParserOutput option tracking
679 ) );
680 foreach ( $fields as $field ) {
681 if ( !is_object( $this->$field ) && $this->$field !== $other->$field ) {
682 return false;
685 // Check the object and lazy-loaded options
686 return (
687 $this->mUserLang->getCode() === $other->mUserLang->getCode() &&
688 $this->getDateFormat() === $other->getDateFormat()
693 * Registers a callback for tracking which ParserOptions which are used.
694 * This is a private API with the parser.
695 * @param callable $callback
697 public function registerWatcher( $callback ) {
698 $this->onAccessCallback = $callback;
702 * Called when an option is accessed.
703 * @param string $optionName Name of the option
705 public function optionUsed( $optionName ) {
706 if ( $this->onAccessCallback ) {
707 call_user_func( $this->onAccessCallback, $optionName );
712 * Returns the full array of options that would have been used by
713 * in 1.16.
714 * Used to get the old parser cache entries when available.
715 * @return array
717 public static function legacyOptions() {
718 return array(
719 'stubthreshold',
720 'numberheadings',
721 'userlang',
722 'thumbsize',
723 'editsection',
724 'printable'
729 * Generate a hash string with the values set on these ParserOptions
730 * for the keys given in the array.
731 * This will be used as part of the hash key for the parser cache,
732 * so users sharing the options with vary for the same page share
733 * the same cached data safely.
735 * Extensions which require it should install 'PageRenderingHash' hook,
736 * which will give them a chance to modify this key based on their own
737 * settings.
739 * @since 1.17
740 * @param array $forOptions
741 * @param Title $title Used to get the content language of the page (since r97636)
742 * @return string Page rendering hash
744 public function optionsHash( $forOptions, $title = null ) {
745 global $wgRenderHashAppend;
747 // FIXME: Once the cache key is reorganized this argument
748 // can be dropped. It was used when the math extension was
749 // part of core.
750 $confstr = '*';
752 // Space assigned for the stubthreshold but unused
753 // since it disables the parser cache, its value will always
754 // be 0 when this function is called by parsercache.
755 if ( in_array( 'stubthreshold', $forOptions ) ) {
756 $confstr .= '!' . $this->mStubThreshold;
757 } else {
758 $confstr .= '!*';
761 if ( in_array( 'dateformat', $forOptions ) ) {
762 $confstr .= '!' . $this->getDateFormat();
765 if ( in_array( 'numberheadings', $forOptions ) ) {
766 $confstr .= '!' . ( $this->mNumberHeadings ? '1' : '' );
767 } else {
768 $confstr .= '!*';
771 if ( in_array( 'userlang', $forOptions ) ) {
772 $confstr .= '!' . $this->mUserLang->getCode();
773 } else {
774 $confstr .= '!*';
777 if ( in_array( 'thumbsize', $forOptions ) ) {
778 $confstr .= '!' . $this->mThumbSize;
779 } else {
780 $confstr .= '!*';
783 // add in language specific options, if any
784 // @todo FIXME: This is just a way of retrieving the url/user preferred variant
785 if ( !is_null( $title ) ) {
786 $confstr .= $title->getPageLanguage()->getExtraHashOptions();
787 } else {
788 global $wgContLang;
789 $confstr .= $wgContLang->getExtraHashOptions();
792 $confstr .= $wgRenderHashAppend;
794 if ( !in_array( 'editsection', $forOptions ) ) {
795 $confstr .= '!*';
796 } elseif ( !$this->mEditSection ) {
797 $confstr .= '!edit=0';
800 if ( $this->mIsPrintable && in_array( 'printable', $forOptions ) ) {
801 $confstr .= '!printable=1';
804 if ( $this->mExtraKey != '' ) {
805 $confstr .= $this->mExtraKey;
808 // Give a chance for extensions to modify the hash, if they have
809 // extra options or other effects on the parser cache.
810 Hooks::run( 'PageRenderingHash', array( &$confstr, $this->getUser(), &$forOptions ) );
812 // Make it a valid memcached key fragment
813 $confstr = str_replace( ' ', '_', $confstr );
815 return $confstr;
819 * Sets a hook to force that a page exists, and sets a current revision callback to return a
820 * revision with custom content when the current revision of the page is requested.
822 * @since 1.25
823 * @param Title $title
824 * @param Content $content
825 * @param User $user The user that the fake revision is attributed to
826 * @return ScopedCallback to unset the hook
828 public function setupFakeRevision( $title, $content, $user ) {
829 $oldCallback = $this->setCurrentRevisionCallback( function ( $titleToCheck, $parser = false ) use ( $title, $content, $user, &$oldCallback ) {
830 if ( $titleToCheck->equals( $title ) ) {
831 return new Revision( array(
832 'page' => $title->getArticleID(),
833 'user_text' => $user->getName(),
834 'user' => $user->getId(),
835 'parent_id' => $title->getLatestRevId(),
836 'title' => $title,
837 'content' => $content
838 ) );
839 } else {
840 return call_user_func( $oldCallback, $titleToCheck, $parser );
842 } );
843 global $wgHooks;
844 $wgHooks['TitleExists'][] =
845 function ( $titleToCheck, &$exists ) use ( $title ) {
846 if ( $titleToCheck->equals( $title ) ) {
847 $exists = true;
850 end( $wgHooks['TitleExists'] );
851 $key = key( $wgHooks['TitleExists'] );
852 LinkCache::singleton()->clearBadLink( $title->getPrefixedDBkey() );
853 return new ScopedCallback( function () use ( $title, $key ) {
854 global $wgHooks;
855 unset( $wgHooks['TitleExists'][$key] );
856 LinkCache::singleton()->clearLink( $title );
857 } );