Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.22% covered (warning)
86.22%
344 / 399
66.07% covered (warning)
66.07%
74 / 112
CRAP
0.00% covered (danger)
0.00%
0 / 1
ParserOptions
86.43% covered (warning)
86.43%
344 / 398
66.07% covered (warning)
66.07%
74 / 112
253.37
0.00% covered (danger)
0.00%
0 / 1
 getOption
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 lazyLoadOption
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 nullifyLazyOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLazyOptions
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getCacheVaryingOptionsHash
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setOption
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 setOptionLegacy
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getInterwikiMagic
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setInterwikiMagic
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllowExternalImages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllowExternalImagesFrom
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getEnableImageWhitelist
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllowSpecialInclusion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setAllowSpecialInclusion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInterfaceMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setInterfaceMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setIsMessage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTargetLanguage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setTargetLanguage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMaxIncludeSize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMaxIncludeSize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMaxPPNodeCount
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMaxPPNodeCount
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMaxPPExpandDepth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMaxTemplateDepth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMaxTemplateDepth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExpensiveParserFunctionLimit
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setExpensiveParserFunctionLimit
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRemoveComments
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setRemoveComments
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEnableLimitReport
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 enableLimitReport
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCleanSignatures
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setCleanSignatures
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExternalLinkTarget
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setExternalLinkTarget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDisableContentConversion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 disableContentConversion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDisableTitleConversion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 disableTitleConversion
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getThumbSize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setThumbSize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getIsPreview
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 setIsPreview
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 getIsSectionPreview
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setIsSectionPreview
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 getIsPrintable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setIsPrintable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPreSaveTransform
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setPreSaveTransform
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUseParsoid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setUseParsoid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDateFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 initDateFormat
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setDateFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserLangObj
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUserLang
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setUserLang
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getMagicISBNLinks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMagicPMIDLinks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMagicRFCLinks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSuppressTOC
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSuppressTOC
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getSuppressSectionEditLinks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setSuppressSectionEditLinks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCollapsibleSections
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setCollapsibleSections
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllowUnsafeRawHtml
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setAllowUnsafeRawHtml
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWrapOutputClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setWrapOutputClass
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 getCurrentRevisionRecordCallback
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setCurrentRevisionRecordCallback
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTemplateCallback
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setTemplateCallback
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSpeculativeRevId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSpeculativePageId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 initSpeculativeRevId
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 initSpeculativePageId
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setSpeculativeRevIdCallback
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setSpeculativePageIdCallback
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getTimestamp
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 setTimestamp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setRedirectTarget
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRedirectTarget
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addExtraKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserIdentity
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 newFromAnon
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 newFromUser
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFromUserAndLang
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFromContext
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 newCanonical
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 clearStaticCache
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 getDefaults
100.00% covered (success)
100.00%
82 / 82
100.00% covered (success)
100.00%
1 / 1
2
 initialiseFromUser
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 matches
95.83% covered (success)
95.83%
23 / 24
0.00% covered (danger)
0.00%
0 / 1
8
 matchesForCacheKey
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 registerWatcher
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 optionUsed
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 allCacheVaryingOptions
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 optionToString
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
 optionsHash
96.67% covered (success)
96.67%
29 / 30
0.00% covered (danger)
0.00%
0 / 1
7
 isSafeToCache
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
8.02
 setupFakeRevision
83.33% covered (warning)
83.33%
40 / 48
0.00% covered (danger)
0.00%
0 / 1
8.30
 getRenderReason
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setRenderReason
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPostproc
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 enablePostproc
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 shouldIncludePostproc
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clearPostproc
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Options for the PHP parser
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 * @ingroup Parser
8 */
9
10namespace MediaWiki\Parser;
11
12use InvalidArgumentException;
13use LogicException;
14use MediaWiki\Content\Content;
15use MediaWiki\Context\IContextSource;
16use MediaWiki\HookContainer\HookRunner;
17use MediaWiki\Language\Language;
18use MediaWiki\MainConfigNames;
19use MediaWiki\MediaWikiServices;
20use MediaWiki\Page\PageIdentity;
21use MediaWiki\Page\PageIdentityValue;
22use MediaWiki\Page\PageRecord;
23use MediaWiki\Revision\MutableRevisionRecord;
24use MediaWiki\Revision\SlotRecord;
25use MediaWiki\Skin\Skin;
26use MediaWiki\StubObject\StubObject;
27use MediaWiki\Title\Title;
28use MediaWiki\User\UserIdentity;
29use MediaWiki\User\UserIdentityValue;
30use MediaWiki\Utils\MWTimestamp;
31use ReflectionClass;
32use Wikimedia\IPUtils;
33use Wikimedia\ScopedCallback;
34use Wikimedia\Timestamp\TimestampFormat as TS;
35
36/**
37 * @brief Set options of the Parser
38 *
39 * How to add an option in core:
40 *  1. Add it to one of the arrays in ParserOptions::getDefaults() (see documentation there)
41 *  2. If necessary:
42 *    a. add an entry to ParserOptions::$initialCacheVaryingOptionsHash: this splits the cache on that option
43 *    b. add an entry to ParserOptions::$callbacks: this is used for pseudo-options that shouldn't be considered
44 *      to compute cacheability
45 *    c. add an entry to ParserOptions::$initialLazyOptions: this ensures that the option is only loaded on read.
46 *      The cacheability of these options is also determined by their usage during the parse and their presence
47 *      in the $initialCacheVaryingOptionsHash or $callbacks arrays.
48 *    Setting options that are not from (2a) or (2b) will make the parse uncacheable if they are actually used
49 *    during the parse in the (current default article parse) setup where option usage is recorded to determine
50 *    cacheability (see ::safeToCache() below for details.)
51 *  3. Add a getter and setter in the section for that.
52 *     These should be typically be wrappers around getOption/setOption in order
53 *     to ensure that $this->optionUsed() gets invoked.
54 *
55 * How to add an option in an extension:
56 *  1. Use the 'ParserOptionsRegister' hook to register it.
57 *     This callback will provide $defaults (step 1), $cacheVaryingOptionsHash (2a)
58 *     and $lazyOptions (2c) arrays which serve the same purpose as
59 *     for options defined in core.  It is not possible to add a pseudo-option
60 *     (2b) from an extension at this time.
61 *  2. Where necessary, use $popt->getOption() and $popt->setOption()
62 *     to access it.
63 *
64 * @ingroup Parser
65 */
66class ParserOptions {
67
68    /**
69     * Default values for all options that are relevant for caching.
70     * @see self::getDefaults()
71     * @var array|null
72     */
73    private static $defaults = null;
74
75    /**
76     * Lazy-loaded options
77     * @var callable[]|null
78     */
79    private static $lazyOptions = null;
80
81    /**
82     * Initial lazy-loaded options (before hook)
83     * @var callable[]
84     */
85    private static $initialLazyOptions = [
86        'dateformat' => [ self::class, 'initDateFormat' ],
87        'speculativeRevId' => [ self::class, 'initSpeculativeRevId' ],
88        'speculativePageId' => [ self::class, 'initSpeculativePageId' ],
89    ];
90
91    /**
92     * Specify options that are included in the cache key. This array is initially a copy of
93     * $initialCacheVaryingOptionsHash, and is then modified by the ParserOptionsRegister hook.
94     * If an option in this array has a value that differs from its defaults, said value is
95     * added to the cache key and splits the cache accordingly.
96     * @var array|null
97     */
98    private static $cacheVaryingOptionsHash = null;
99
100    /**
101     * Initial set of $cacheVaryingOptionsHash options (before hook)
102     * @var array
103     */
104    private static $initialCacheVaryingOptionsHash = [
105        'dateformat' => true,
106        'thumbsize' => true,
107        'printable' => true,
108        'userlang' => true,
109        'useParsoid' => true,
110        'suppressSectionEditLinks' => true,
111        'collapsibleSections' => true,
112        'postproc' => true,
113        'skin' => true,
114        'injectTOC' => true
115    ];
116
117    /**
118     * List of "postprocessing options", excluded from most ParserOption
119     * operations by default.
120     * @var list<string>
121     * @internal
122     */
123    public static array $postprocOptions = [
124        'skin',
125        'allowTOC',
126        'injectTOC',
127        'includeDebugInfo',
128        'enableSectionEditLinks',
129        'wrapperDivClass',
130        'deduplicateStyles',
131        'unwrap',
132        'absoluteURLs',
133    ];
134
135    /**
136     * Specify pseudo-options that are actually callbacks.
137     * These must be ignored when checking for cacheability.
138     * @var array
139     */
140    private static $callbacks = [
141        'currentRevisionRecordCallback' => true,
142        'templateCallback' => true,
143        'speculativeRevIdCallback' => true,
144        'speculativePageIdCallback' => true,
145    ];
146
147    /**
148     * Current values for all options that are relevant for caching.
149     * @var array
150     */
151    private $options;
152
153    /**
154     * Timestamp used for {{CURRENTDAY}} etc.
155     * @var string|null
156     * @note Caching based on parse time is handled externally
157     */
158    private $mTimestamp;
159
160    /**
161     * Stored user object
162     * @var UserIdentity
163     * @todo Track this for caching somehow without fragmenting the cache
164     */
165    private $mUser;
166
167    /**
168     * Function to be called when an option is accessed.
169     * @var callable|null
170     * @note Used for collecting used options, does not affect caching
171     */
172    private $onAccessCallback = null;
173
174    /**
175     * If the page being parsed is a redirect, this should hold the redirect
176     * target.
177     * @var Title|null
178     * @todo Track this for caching somehow
179     */
180    private $redirectTarget = null;
181
182    /**
183     * Appended to the options hash
184     * @var string
185     */
186    private $mExtraKey = '';
187
188    /**
189     * The reason for rendering the content.
190     * @var string
191     */
192    private $renderReason = 'unknown';
193
194    /**
195     * Fetch an option and track that is was accessed
196     * @since 1.30
197     * @param string $name Option name
198     * @return mixed
199     */
200    public function getOption( $name ) {
201        if ( !array_key_exists( $name, $this->options ) ) {
202            throw new InvalidArgumentException( "Unknown parser option $name" );
203        }
204
205        $this->lazyLoadOption( $name );
206        $this->optionUsed( $name );
207        return $this->options[$name];
208    }
209
210    /**
211     * @param string $name Lazy load option without tracking usage
212     */
213    private function lazyLoadOption( $name ) {
214        $lazyOptions = self::getLazyOptions();
215        if ( isset( $lazyOptions[$name] ) && $this->options[$name] === null ) {
216            $this->options[$name] = $lazyOptions[$name]( $this, $name );
217        }
218    }
219
220    /**
221     * Resets lazy loaded options to null in the provided $options array
222     * @param array $options
223     * @return array
224     */
225    private function nullifyLazyOption( array $options ): array {
226        return array_fill_keys( array_keys( self::getLazyOptions() ), null ) + $options;
227    }
228
229    /**
230     * Get lazy-loaded options.
231     *
232     * This array should be initialised by the constructor. The return type
233     * hint is used as an assertion to ensure this has happened and to coerce
234     * the type for static analysis.
235     *
236     * @internal Public for testing only
237     *
238     * @return array
239     */
240    public static function getLazyOptions(): array {
241        // Trigger a call to the 'ParserOptionsRegister' hook if it hasn't
242        // already been called.
243        if ( self::$lazyOptions === null ) {
244            self::getDefaults();
245        }
246        return self::$lazyOptions;
247    }
248
249    /**
250     * Get cache varying options, with the name of the option in the key, and a
251     * boolean in the value which indicates whether the cache is indeed varied.
252     *
253     * @see self::allCacheVaryingOptions()
254     *
255     * @return array
256     */
257    private static function getCacheVaryingOptionsHash(): array {
258        // Trigger a call to the 'ParserOptionsRegister' hook if it hasn't
259        // already been called.
260        if ( self::$cacheVaryingOptionsHash === null ) {
261            self::getDefaults();
262        }
263        return self::$cacheVaryingOptionsHash;
264    }
265
266    /**
267     * Set an option, generically
268     * @since 1.30
269     * @param string $name Option name
270     * @param mixed $value New value. Passing null will set null, unlike many
271     *  of the existing accessors which ignore null for historical reasons.
272     * @return mixed Old value
273     */
274    public function setOption( $name, $value ) {
275        if ( !array_key_exists( $name, $this->options ) ) {
276            throw new InvalidArgumentException( "Unknown parser option $name" );
277        }
278        $old = $this->options[$name];
279        $this->options[$name] = $value;
280        return $old;
281    }
282
283    /**
284     * Legacy implementation
285     * @since 1.30 For implementing legacy setters only. Don't use this in new code.
286     * @deprecated since 1.30
287     * @param string $name Option name
288     * @param mixed $value New value. Passing null does not set the value.
289     * @return mixed Old value
290     */
291    protected function setOptionLegacy( $name, $value ) {
292        if ( !array_key_exists( $name, $this->options ) ) {
293            throw new InvalidArgumentException( "Unknown parser option $name" );
294        }
295        return wfSetVar( $this->options[$name], $value );
296    }
297
298    /**
299     * Whether to extract interlanguage links
300     *
301     * When true, interlanguage links will be returned by
302     * ParserOutput::getLanguageLinks() instead of generating link HTML.
303     *
304     * @return bool
305     */
306    public function getInterwikiMagic() {
307        return $this->getOption( 'interwikiMagic' );
308    }
309
310    /**
311     * Specify whether to extract interlanguage links
312     * @param bool|null $x New value (null is no change)
313     * @return bool Old value
314     */
315    public function setInterwikiMagic( $x ) {
316        return $this->setOptionLegacy( 'interwikiMagic', $x );
317    }
318
319    /**
320     * Allow all external images inline?
321     * @return bool
322     */
323    public function getAllowExternalImages() {
324        return $this->getOption( 'allowExternalImages' );
325    }
326
327    /**
328     * External images to allow
329     *
330     * When self::getAllowExternalImages() is false
331     *
332     * @return string|string[] URLs to allow
333     */
334    public function getAllowExternalImagesFrom() {
335        return $this->getOption( 'allowExternalImagesFrom' );
336    }
337
338    /**
339     * Use the on-wiki external image whitelist?
340     * @return bool
341     */
342    public function getEnableImageWhitelist() {
343        return $this->getOption( 'enableImageWhitelist' );
344    }
345
346    /**
347     * Allow inclusion of special pages?
348     * @return bool
349     */
350    public function getAllowSpecialInclusion() {
351        return $this->getOption( 'allowSpecialInclusion' );
352    }
353
354    /**
355     * Allow inclusion of special pages?
356     * @param bool|null $x New value (null is no change)
357     * @return bool Old value
358     */
359    public function setAllowSpecialInclusion( $x ) {
360        return $this->setOptionLegacy( 'allowSpecialInclusion', $x );
361    }
362
363    /**
364     * Parsing an interface message in the user language?
365     *
366     * Note: use isMessage() to determine if we are parsing a message in any language.
367     *
368     * @return bool
369     */
370    public function getInterfaceMessage() {
371        return $this->getOption( 'interfaceMessage' );
372    }
373
374    /**
375     * Parsing an interface message in the user language?
376     * @param bool|null $x New value (null is no change)
377     * @return bool Old value
378     */
379    public function setInterfaceMessage( $x ) {
380        return $this->setOptionLegacy( 'interfaceMessage', $x );
381    }
382
383    /**
384     * Parsing a message?
385     *
386     * @since 1.45
387     * @return bool
388     */
389    public function isMessage() {
390        return $this->getOption( 'isMessage' );
391    }
392
393    /**
394     * Set whether we are parsing a message
395     *
396     * @since 1.45
397     * @param bool $x
398     * @return bool
399     */
400    public function setIsMessage( $x ) {
401        return $this->setOption( 'isMessage', $x );
402    }
403
404    /**
405     * Target language for the parse
406     * @return Language|null
407     */
408    public function getTargetLanguage() {