Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
88.12% |
304 / 345 |
|
67.92% |
72 / 106 |
CRAP | |
0.00% |
0 / 1 |
ParserOptions | |
88.37% |
304 / 344 |
|
67.92% |
72 / 106 |
194.26 | |
0.00% |
0 / 1 |
getOption | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
lazyLoadOption | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
nullifyLazyOption | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getLazyOptions | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
getCacheVaryingOptionsHash | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setOption | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
setOptionLegacy | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
getInterwikiMagic | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setInterwikiMagic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getAllowExternalImages | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAllowExternalImagesFrom | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getEnableImageWhitelist | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAllowSpecialInclusion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setAllowSpecialInclusion | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getInterfaceMessage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setInterfaceMessage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTargetLanguage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setTargetLanguage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMaxIncludeSize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setMaxIncludeSize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMaxPPNodeCount | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setMaxPPNodeCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMaxPPExpandDepth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMaxTemplateDepth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setMaxTemplateDepth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getExpensiveParserFunctionLimit | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setExpensiveParserFunctionLimit | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRemoveComments | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setRemoveComments | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getEnableLimitReport | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
enableLimitReport | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCleanSignatures | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setCleanSignatures | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getExternalLinkTarget | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setExternalLinkTarget | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDisableContentConversion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
disableContentConversion | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDisableTitleConversion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
disableTitleConversion | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getThumbSize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setThumbSize | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIsPreview | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setIsPreview | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIsSectionPreview | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setIsSectionPreview | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIsPrintable | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setIsPrintable | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPreSaveTransform | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setPreSaveTransform | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUseParsoid | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setUseParsoid | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDateFormat | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
initDateFormat | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setDateFormat | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUserLangObj | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUserLang | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setUserLang | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getMagicISBNLinks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMagicPMIDLinks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMagicRFCLinks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSuppressTOC | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setSuppressTOC | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getSuppressSectionEditLinks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setSuppressSectionEditLinks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getCollapsibleSections | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setCollapsibleSections | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAllowUnsafeRawHtml | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setAllowUnsafeRawHtml | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getWrapOutputClass | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setWrapOutputClass | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
getCurrentRevisionRecordCallback | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setCurrentRevisionRecordCallback | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTemplateCallback | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setTemplateCallback | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSpeculativeRevId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSpeculativePageId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
initSpeculativeRevId | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
initSpeculativePageId | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setSpeculativeRevIdCallback | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setSpeculativePageIdCallback | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getTimestamp | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setTimestamp | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setRedirectTarget | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRedirectTarget | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addExtraKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUserIdentity | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__construct | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
newFromAnon | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
newFromUser | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
newFromUserAndLang | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
newFromContext | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
newCanonical | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
4 | |||
clearStaticCache | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
3.07 | |||
getDefaults | |
100.00% |
71 / 71 |
|
100.00% |
1 / 1 |
2 | |||
initialiseFromUser | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
matches | |
95.83% |
23 / 24 |
|
0.00% |
0 / 1 |
8 | |||
matchesForCacheKey | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
registerWatcher | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
optionUsed | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
allCacheVaryingOptions | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
optionToString | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
6 | |||
optionsHash | |
96.67% |
29 / 30 |
|
0.00% |
0 / 1 |
7 | |||
isSafeToCache | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
5 | |||
setupFakeRevision | |
96.67% |
29 / 30 |
|
0.00% |
0 / 1 |
3 | |||
getRenderReason | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setRenderReason | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
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 | |
24 | namespace MediaWiki\Parser; |
25 | |
26 | use InvalidArgumentException; |
27 | use LogicException; |
28 | use MediaWiki\Content\Content; |
29 | use MediaWiki\Context\IContextSource; |
30 | use MediaWiki\HookContainer\HookRunner; |
31 | use MediaWiki\Language\Language; |
32 | use MediaWiki\MainConfigNames; |
33 | use MediaWiki\MediaWikiServices; |
34 | use MediaWiki\Revision\MutableRevisionRecord; |
35 | use MediaWiki\Revision\SlotRecord; |
36 | use MediaWiki\StubObject\StubObject; |
37 | use MediaWiki\Title\Title; |
38 | use MediaWiki\User\UserIdentity; |
39 | use MediaWiki\User\UserIdentityValue; |
40 | use MediaWiki\Utils\MWTimestamp; |
41 | use ReflectionClass; |
42 | use Wikimedia\IPUtils; |
43 | use Wikimedia\ScopedCallback; |
44 | |
45 | /** |
46 | * @brief Set options of the Parser |
47 | * |
48 | * How to add an option in core: |
49 | * 1. Add it to one of the arrays in ParserOptions::setDefaults() |
50 | * 2. If necessary, add an entry to ParserOptions::$inCacheKey |
51 | * 3. Add a getter and setter in the section for that. |
52 | * |
53 | * How to add an option in an extension: |
54 | * 1. Use the 'ParserOptionsRegister' hook to register it. |
55 | * 2. Where necessary, use $popt->getOption() and $popt->setOption() |
56 | * to access it. |
57 | * |
58 | * @ingroup Parser |
59 | */ |
60 | class ParserOptions { |
61 | |
62 | /** |
63 | * Default values for all options that are relevant for caching. |
64 | * @see self::getDefaults() |
65 | * @var array|null |
66 | */ |
67 | private static $defaults = null; |
68 | |
69 | /** |
70 | * Lazy-loaded options |
71 | * @var callable[]|null |
72 | */ |
73 | private static $lazyOptions = null; |
74 | |
75 | /** |
76 | * Initial lazy-loaded options (before hook) |
77 | * @var callable[] |
78 | */ |
79 | private static $initialLazyOptions = [ |
80 | 'dateformat' => [ __CLASS__, 'initDateFormat' ], |
81 | 'speculativeRevId' => [ __CLASS__, 'initSpeculativeRevId' ], |
82 | 'speculativePageId' => [ __CLASS__, 'initSpeculativePageId' ], |
83 | ]; |
84 | |
85 | /** |
86 | * Specify options that are included in the cache key |
87 | * @var array|null |
88 | */ |
89 | private static $cacheVaryingOptionsHash = null; |
90 | |
91 | /** |
92 | * Initial inCacheKey options (before hook) |
93 | * @var array |
94 | */ |
95 | private static $initialCacheVaryingOptionsHash = [ |
96 | 'dateformat' => true, |
97 | 'thumbsize' => true, |
98 | 'printable' => true, |
99 | 'userlang' => true, |
100 | 'useParsoid' => true, |
101 | 'suppressSectionEditLinks' => true, |
102 | 'collapsibleSections' => true, |
103 | ]; |
104 | |
105 | /** |
106 | * Specify pseudo-options that are actually callbacks. |
107 | * These must be ignored when checking for cacheability. |
108 | * @var array |
109 | */ |
110 | private static $callbacks = [ |
111 | 'currentRevisionRecordCallback' => true, |
112 | 'templateCallback' => true, |
113 | 'speculativeRevIdCallback' => true, |
114 | 'speculativePageIdCallback' => true, |
115 | ]; |
116 | |
117 | /** |
118 | * Current values for all options that are relevant for caching. |
119 | * @var array |
120 | */ |
121 | private $options; |
122 | |
123 | /** |
124 | * Timestamp used for {{CURRENTDAY}} etc. |
125 | * @var string|null |
126 | * @note Caching based on parse time is handled externally |
127 | */ |
128 | private $mTimestamp; |
129 | |
130 | /** |
131 | * Stored user object |
132 | * @var UserIdentity |
133 | * @todo Track this for caching somehow without fragmenting the cache |
134 | */ |
135 | private $mUser; |
136 | |
137 | /** |
138 | * Function to be called when an option is accessed. |
139 | * @var callable|null |
140 | * @note Used for collecting used options, does not affect caching |
141 | */ |
142 | private $onAccessCallback = null; |
143 | |
144 | /** |
145 | * If the page being parsed is a redirect, this should hold the redirect |
146 | * target. |
147 | * @var Title|null |
148 | * @todo Track this for caching somehow |
149 | */ |
150 | private $redirectTarget = null; |
151 | |
152 | /** |
153 | * Appended to the options hash |
154 | * @var string |
155 | */ |
156 | private $mExtraKey = ''; |
157 | |
158 | /** |
159 | * The reason for rendering the content. |
160 | * @var string |
161 | */ |
162 | private $renderReason = 'unknown'; |
163 | |
164 | /** |
165 | * Fetch an option and track that is was accessed |
166 | * @since 1.30 |
167 | * @param string $name Option name |
168 | * @return mixed |
169 | */ |
170 | public function getOption( $name ) { |
171 | if ( !array_key_exists( $name, $this->options ) ) { |
172 | throw new InvalidArgumentException( "Unknown parser option $name" ); |
173 | } |
174 | |
175 | $this->lazyLoadOption( $name ); |
176 | $this->optionUsed( $name ); |
177 | return $this->options[$name]; |
178 | } |
179 | |
180 | /** |
181 | * @param string $name Lazy load option without tracking usage |
182 | */ |
183 | private function lazyLoadOption( $name ) { |
184 | $lazyOptions = self::getLazyOptions(); |
185 | if ( isset( $lazyOptions[$name] ) && $this->options[$name] === null ) { |
186 | $this->options[$name] = call_user_func( $lazyOptions[$name], $this, $name ); |
187 | } |
188 | } |
189 | |
190 | /** |
191 | * Resets lazy loaded options to null in the provided $options array |
192 | * @param array $options |
193 | * @return array |
194 | */ |
195 | private function nullifyLazyOption( array $options ): array { |
196 | return array_fill_keys( array_keys( self::getLazyOptions() ), null ) + $options; |
197 | } |
198 | |
199 | /** |
200 | * Get lazy-loaded options. |
201 | * |
202 | * This array should be initialised by the constructor. The return type |
203 | * hint is used as an assertion to ensure this has happened and to coerce |
204 | * the type for static analysis. |
205 | * |
206 | * @internal Public for testing only |
207 | * |
208 | * @return array |
209 | */ |
210 | public static function getLazyOptions(): array { |
211 | // Trigger a call to the 'ParserOptionsRegister' hook if it hasn't |
212 | // already been called. |
213 | if ( self::$lazyOptions === null ) { |
214 | self::getDefaults(); |
215 | } |
216 | return self::$lazyOptions; |
217 | } |
218 | |
219 | /** |
220 | * Get cache varying options, with the name of the option in the key, and a |
221 | * boolean in the value which indicates whether the cache is indeed varied. |
222 | * |
223 | * @see self::allCacheVaryingOptions() |
224 | * |
225 | * @return array |
226 | */ |
227 | private static function getCacheVaryingOptionsHash(): array { |
228 | // Trigger a call to the 'ParserOptionsRegister' hook if it hasn't |
229 | // already been called. |
230 | if ( self::$cacheVaryingOptionsHash === null ) { |
231 | self::getDefaults(); |
232 | } |
233 | return self::$cacheVaryingOptionsHash; |
234 | } |
235 | |
236 | /** |
237 | * Set an option, generically |
238 | * @since 1.30 |
239 | * @param string $name Option name |
240 | * @param mixed $value New value. Passing null will set null, unlike many |
241 | * of the existing accessors which ignore null for historical reasons. |
242 | * @return mixed Old value |
243 | */ |
244 | public function setOption( $name, $value ) { |
245 | if ( !array_key_exists( $name, $this->options ) ) { |
246 | throw new InvalidArgumentException( "Unknown parser option $name" ); |
247 | } |
248 | $old = $this->options[$name]; |
249 | $this->options[$name] = $value; |
250 | return $old; |
251 | } |
252 | |
253 | /** |
254 | * Legacy implementation |
255 | * @since 1.30 For implementing legacy setters only. Don't use this in new code. |
256 | * @deprecated since 1.30 |
257 | * @param string $name Option name |
258 | * @param mixed $value New value. Passing null does not set the value. |
259 | * @return mixed Old value |
260 | */ |
261 | protected function setOptionLegacy( $name, $value ) { |
262 | if ( !array_key_exists( $name, $this->options ) ) { |
263 | throw new InvalidArgumentException( "Unknown parser option $name" ); |
264 | } |
265 | return wfSetVar( $this->options[$name], $value ); |
266 | } |
267 | |
268 | /** |
269 | * Whether to extract interlanguage links |
270 | * |
271 | * When true, interlanguage links will be returned by |
272 | * ParserOutput::getLanguageLinks() instead of generating link HTML. |
273 | * |
274 | * @return bool |
275 | */ |
276 | public function getInterwikiMagic() { |
277 | return $this->getOption( 'interwikiMagic' ); |
278 | } |
279 | |
280 | /** |
281 | * Specify whether to extract interlanguage links |
282 | * @param bool|null $x New value (null is no change) |
283 | * @return bool Old value |
284 | */ |
285 | public function setInterwikiMagic( $x ) { |
286 | return $this->setOptionLegacy( 'interwikiMagic', $x ); |
287 | } |
288 | |
289 | /** |
290 | * Allow all external images inline? |
291 | * @return bool |
292 | */ |
293 | public function getAllowExternalImages() { |
294 | return $this->getOption( 'allowExternalImages' ); |
295 | } |
296 | |
297 | /** |
298 | * External images to allow |
299 | * |
300 | * When self::getAllowExternalImages() is false |
301 | * |
302 | * @return string|string[] URLs to allow |
303 | */ |
304 | public function getAllowExternalImagesFrom() { |
305 | return $this->getOption( 'allowExternalImagesFrom' ); |
306 | } |
307 | |
308 | /** |
309 | * Use the on-wiki external image whitelist? |
310 | * @return bool |
311 | */ |
312 | public function getEnableImageWhitelist() { |
313 | return $this->getOption( 'enableImageWhitelist' ); |
314 | } |
315 | |
316 | /** |
317 | * Allow inclusion of special pages? |
318 | * @return bool |
319 | */ |
320 | public function getAllowSpecialInclusion() { |
321 | return $this->getOption( 'allowSpecialInclusion' ); |
322 | } |
323 | |
324 | /** |
325 | * Allow inclusion of special pages? |
326 | * @param bool|null $x New value (null is no change) |
327 | * @return bool Old value |
328 | */ |
329 | public function setAllowSpecialInclusion( $x ) { |
330 | return $this->setOptionLegacy( 'allowSpecialInclusion', $x ); |
331 | } |
332 | |
333 | /** |
334 | * Parsing an interface message? |
335 | * @return bool |
336 | */ |
337 | public function getInterfaceMessage() { |
338 | return $this->getOption( 'interfaceMessage' ); |
339 | } |
340 | |
341 | /** |
342 | * Parsing an interface message? |
343 | * @param bool|null $x New value (null is no change) |
344 | * @return bool Old value |
345 | */ |
346 | public function setInterfaceMessage( $x ) { |
347 | return $this->setOptionLegacy( 'interfaceMessage', $x ); |
348 | } |
349 | |
350 | /** |
351 | * Target language for the parse |
352 | * @return Language|null |
353 | */ |
354 | public function getTargetLanguage() { |
355 | return $this->getOption( 'targetLanguage' ); |
356 | } |
357 | |
358 | /** |
359 | * Target language for the parse |
360 | * @param Language|null $x New value |
361 | * @return Language|null Old value |
362 | */ |
363 | public function setTargetLanguage( $x ) { |
364 | return $this->setOption( 'targetLanguage', $x ); |
365 | } |
366 | |
367 | /** |
368 | * Maximum size of template expansions, in bytes |
369 | * @return int |
370 | */ |
371 | public function getMaxIncludeSize() { |
372 | return $this->getOption( 'maxIncludeSize' ); |
373 | } |
374 | |
375 | /** |
376 | * Maximum size of template expansions, in bytes |
377 | * @param int|null $x New value (null is no change) |
378 | * @return int Old value |
379 | */ |
380 | public function setMaxIncludeSize( $x ) { |
381 | return $this->setOptionLegacy( 'maxIncludeSize', $x ); |
382 | } |
383 | |
384 | /** |
385 | * Maximum number of nodes touched by PPFrame::expand() |
386 | * @return int |
387 | */ |
388 | public function getMaxPPNodeCount() { |
389 | return $this->getOption( 'maxPPNodeCount' ); |
390 | } |
391 | |
392 | /** |
393 | * Maximum number of nodes touched by PPFrame::expand() |
394 | * @param int|null $x New value (null is no change) |
395 | * @return int Old value |
396 | */ |
397 | public function setMaxPPNodeCount( $x ) { |
398 | return $this->setOptionLegacy( 'maxPPNodeCount', $x ); |
399 | } |
400 | |
401 | /** |
402 | * Maximum recursion depth in PPFrame::expand() |
403 | * @return int |
404 | */ |
405 | public function getMaxPPExpandDepth() { |
406 | return $this->getOption( 'maxPPExpandDepth' ); |
407 | } |
408 | |
409 | /** |
410 | * Maximum recursion depth for templates within templates |
411 | * @return int |
412 | * @internal Only used by Parser (T318826) |
413 | */ |
414 | public function getMaxTemplateDepth() { |
415 | return $this->getOption( 'maxTemplateDepth' ); |
416 | } |
417 | |
418 | /** |
419 | * Maximum recursion depth for templates within templates |
420 | * @param int|null $x New value (null is no change) |
421 | * @return int Old value |
422 | * @internal Only used by ParserTestRunner (T318826) |
423 | */ |
424 | public function setMaxTemplateDepth( $x ) { |
425 | return $this->setOptionLegacy( 'maxTemplateDepth', $x ); |
426 | } |
427 | |
428 | /** |
429 | * Maximum number of calls per parse to expensive parser functions |
430 | * @since 1.20 |
431 | * @return int |
432 | */ |
433 | public function getExpensiveParserFunctionLimit() { |
434 | return $this->getOption( 'expensiveParserFunctionLimit' ); |
435 | } |
436 | |
437 | /** |
438 | * Maximum number of calls per parse to expensive parser functions |
439 | * @since 1.20 |
440 | * @param int|null $x New value (null is no change) |
441 | * @return int Old value |
442 | */ |
443 | public function setExpensiveParserFunctionLimit( $x ) { |
444 | return $this->setOptionLegacy( 'expensiveParserFunctionLimit', $x ); |
445 | } |
446 | |
447 | /** |
448 | * Remove HTML comments |
449 | * @warning Only applies to preprocess operations |
450 | * @return bool |
451 | */ |
452 | public function getRemoveComments() { |
453 | return $this->getOption( 'removeComments' ); |
454 | } |
455 | |
456 | /** |
457 | * Remove HTML comments |
458 | * @warning Only applies to preprocess operations |
459 | * @param bool|null $x New value (null is no change) |
460 | * @return bool Old value |
461 | */ |
462 | public function setRemoveComments( $x ) { |
463 | return $this->setOptionLegacy( 'removeComments', $x ); |
464 | } |
465 | |
466 | /** |
467 | * @deprecated since 1.38. This does nothing now, to control limit reporting |
468 | * please provide 'includeDebugInfo' option to ParserOutput::getText. |
469 | * |
470 | * Enable limit report in an HTML comment on output |
471 | * @return bool |
472 | */ |
473 | public function getEnableLimitReport() { |
474 | return false; |
475 | } |
476 | |
477 | /** |
478 | * @deprecated since 1.38. This does nothing now, to control limit reporting |
479 | * please provide 'includeDebugInfo' option to ParserOutput::getText. |
480 | * |
481 | * Enable limit report in an HTML comment on output |
482 | * @param bool|null $x New value (null is no change) |
483 | * @return bool Old value |
484 | */ |
485 | public function enableLimitReport( $x = true ) { |
486 | return false; |
487 | } |
488 | |
489 | /** |
490 | * Clean up signature texts? |
491 | * @see Parser::cleanSig |
492 | * @return bool |
493 | */ |
494 | public function getCleanSignatures() { |
495 | return $this->getOption( 'cleanSignatures' ); |
496 | } |
497 | |
498 | /** |
499 | * Clean up signature texts? |
500 | * @see Parser::cleanSig |
501 | * @param bool|null $x New value (null is no change) |
502 | * @return bool Old value |
503 | */ |
504 | public function setCleanSignatures( $x ) { |
505 | return $this->setOptionLegacy( 'cleanSignatures', $x ); |
506 | } |
507 | |
508 | /** |
509 | * Target attribute for external links |
510 | * @return string|false |
511 | * @internal Only set by installer (T317647) |
512 | */ |
513 | public function getExternalLinkTarget() { |
514 | return $this->getOption( 'externalLinkTarget' ); |
515 | } |
516 | |
517 | /** |
518 | * Target attribute for external links |
519 | * @param string|false|null $x New value (null is no change) |
520 | * @return string Old value |
521 | * @internal Only used by installer (T317647) |
522 | */ |
523 | public function setExternalLinkTarget( $x ) { |
524 | return $this->setOptionLegacy( 'externalLinkTarget', $x ); |
525 | } |
526 | |
527 | /** |
528 | * Whether content conversion should be disabled |
529 | * @return bool |
530 | */ |
531 | public function getDisableContentConversion() { |
532 | return $this->getOption( 'disableContentConversion' ); |
533 | } |
534 | |
535 | /** |
536 | * Whether content conversion should be disabled |
537 | * @param bool|null $x New value (null is no change) |
538 | * @return bool Old value |
539 | */ |
540 | public function disableContentConversion( $x = true ) { |
541 | return $this->setOptionLegacy( 'disableContentConversion', $x ); |
542 | } |
543 | |
544 | /** |
545 | * Whether title conversion should be disabled |
546 | * @return bool |
547 | */ |
548 | public function getDisableTitleConversion() { |
549 | return $this->getOption( 'disableTitleConversion' ); |
550 | } |
551 | |
552 | /** |
553 | * Whether title conversion should be disabled |
554 | * @param bool|null $x New value (null is no change) |
555 | * @return bool Old value |
556 | */ |
557 | public function disableTitleConversion( $x = true ) { |
558 | return $this->setOptionLegacy( 'disableTitleConversion', $x ); |
559 | } |
560 | |
561 | /** |
562 | * Thumb size preferred by the user. |
563 | * @return int |
564 | */ |
565 | public function getThumbSize() { |
566 | return $this->getOption( 'thumbsize' ); |
567 | } |
568 | |
569 | /** |
570 | * Thumb size preferred by the user. |
571 | * @param int|null $x New value (null is no change) |
572 | * @return int Old value |
573 | */ |
574 | public function setThumbSize( $x ) { |
575 | return $this->setOptionLegacy( 'thumbsize', $x ); |
576 | } |
577 | |
578 | /** |
579 | * Parsing the page for a "preview" operation? |
580 | * @return bool |
581 | */ |
582 | public function getIsPreview() { |
583 | return $this->getOption( 'isPreview' ); |
584 | } |
585 | |
586 | /** |
587 | * Parsing the page for a "preview" operation? |
588 | * @param bool|null $x New value (null is no change) |
589 | * @return bool Old value |
590 | */ |
591 | public function setIsPreview( $x ) { |
592 | return $this->setOptionLegacy( 'isPreview', $x ); |
593 | } |
594 | |
595 | /** |
596 | * Parsing the page for a "preview" operation on a single section? |
597 | * @return bool |
598 | */ |
599 | public function getIsSectionPreview() { |
600 | return $this->getOption( 'isSectionPreview' ); |
601 | } |
602 | |
603 | /** |
604 | * Parsing the page for a "preview" operation on a single section? |
605 | * @param bool|null $x New value (null is no change) |
606 | * @return bool Old value |
607 | */ |
608 | public function setIsSectionPreview( $x ) { |
609 | return $this->setOptionLegacy( 'isSectionPreview', $x ); |
610 | } |
611 | |
612 | /** |
613 | * Parsing the printable version of the page? |
614 | * @return bool |
615 | */ |
616 | public function getIsPrintable() { |
617 | return $this->getOption( 'printable' ); |
618 | } |
619 | |
620 | /** |
621 | * Parsing the printable version of the page? |
622 | * @param bool|null $x New value (null is no change) |
623 | * @return bool Old value |
624 | */ |
625 | public function setIsPrintable( $x ) { |
626 | return $this->setOptionLegacy( 'printable', $x ); |
627 | } |
628 | |
629 | /** |
630 | * Transform wiki markup when saving the page? |
631 | * @return bool |
632 | */ |
633 | public function getPreSaveTransform() { |
634 | return $this->getOption( 'preSaveTransform' ); |
635 | } |
636 | |
637 | /** |
638 | * Transform wiki markup when saving the page? |
639 | * @param bool|null $x New value (null is no change) |
640 | * @return bool Old value |
641 | */ |
642 | public function setPreSaveTransform( $x ) { |
643 | return $this->setOptionLegacy( 'preSaveTransform', $x ); |
644 | } |
645 | |
646 | /** |
647 | * Parsoid-format HTML output, or legacy wikitext parser HTML? |
648 | * @see T300191 |
649 | * @unstable |
650 | * @since 1.41 |
651 | * @return bool |
652 | */ |
653 | public function getUseParsoid(): bool { |
654 | return $this->getOption( 'useParsoid' ); |
655 | } |
656 | |
657 | /** |
658 | * Request Parsoid-format HTML output. |
659 | * @see T300191 |
660 | * @unstable |
661 | * @since 1.41 |
662 | */ |
663 | public function setUseParsoid() { |
664 | $this->setOption( 'useParsoid', true ); |
665 | } |
666 | |
667 | /** |
668 | * Date format index |
669 | * @return string |
670 | */ |
671 | public function getDateFormat() { |
672 | return $this->getOption( 'dateformat' ); |
673 | } |
674 | |
675 | /** |
676 | * Lazy initializer for dateFormat |
677 | * @param ParserOptions $popt |
678 | * @return string |
679 | */ |
680 | private static function initDateFormat( ParserOptions $popt ) { |
681 | $userFactory = MediaWikiServices::getInstance()->getUserFactory(); |
682 | return $userFactory->newFromUserIdentity( $popt->getUserIdentity() )->getDatePreference(); |
683 | } |
684 | |
685 | /** |
686 | * Date format index |
687 | * @param string|null $x New value (null is no change) |
688 | * @return string Old value |
689 | */ |
690 | public function setDateFormat( $x ) { |
691 | return $this->setOptionLegacy( 'dateformat', $x ); |
692 | } |
693 | |
694 | /** |
695 | * Get the user language used by the parser for this page and split the parser cache. |
696 | * |
697 | * @warning Calling this causes the parser cache to be fragmented by user language! |
698 | * To avoid cache fragmentation, output should not depend on the user language. |
699 | * Use Parser::getTargetLanguage() instead! |
700 | * |
701 | * @note This function will trigger a cache fragmentation by recording the |
702 | * 'userlang' option, see optionUsed(). This is done to avoid cache pollution |
703 | * when the page is rendered based on the language of the user. |
704 | * |
705 | * @note When saving, this will return the default language instead of the user's. |
706 | * {{int: }} uses this which used to produce inconsistent link tables (T16404). |
707 | * |
708 | * @return Language |
709 | * @since 1.19 |
710 | */ |
711 | public function getUserLangObj() { |
712 | return $this->getOption( 'userlang' ); |
713 | } |
714 | |
715 | /** |
716 | * Same as getUserLangObj() but returns a string instead. |
717 | * |
718 | * @warning Calling this causes the parser cache to be fragmented by user language! |
719 | * To avoid cache fragmentation, output should not depend on the user language. |
720 | * Use Parser::getTargetLanguage() instead! |
721 | * |
722 | * @see getUserLangObj() |
723 | * |
724 | * @return string Language code |
725 | * @since 1.17 |
726 | */ |
727 | public function getUserLang() { |
728 | return $this->getUserLangObj()->getCode(); |
729 | } |
730 | |
731 | /** |
732 | * Set the user language used by the parser for this page and split the parser cache. |
733 | * @param string|Language $x New value |
734 | * @return Language Old value |
735 | */ |
736 | public function setUserLang( $x ) { |
737 | if ( is_string( $x ) ) { |
738 | $x = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( $x ); |
739 | } |
740 | |
741 | return $this->setOptionLegacy( 'userlang', $x ); |
742 | } |
743 | |
744 | /** |
745 | * Are magic ISBN links enabled? |
746 | * @since 1.28 |
747 | * @return bool |
748 | */ |
749 | public function getMagicISBNLinks() { |
750 | return $this->getOption( 'magicISBNLinks' ); |
751 | } |
752 | |
753 | /** |
754 | * Are magic PMID links enabled? |
755 | * @since 1.28 |
756 | * @return bool |
757 | */ |
758 | public function getMagicPMIDLinks() { |
759 | return $this->getOption( 'magicPMIDLinks' ); |
760 | } |
761 | |
762 | /** |
763 | * Are magic RFC links enabled? |
764 | * @since 1.28 |
765 | * @return bool |
766 | */ |
767 | public function getMagicRFCLinks() { |
768 | return $this->getOption( 'magicRFCLinks' ); |
769 | } |
770 | |
771 | /** |
772 | * Should the table of contents be suppressed? |
773 | * Used when parsing "code" pages (like JavaScript) as wikitext |
774 | * for backlink support and categories, but where we don't want |
775 | * other metadata generated (like the table of contents). |
776 | * @see T307691 |
777 | * @since 1.39 |
778 | * @return bool |
779 | */ |
780 | public function getSuppressTOC() { |
781 | return $this->getOption( 'suppressTOC' ); |
782 | } |
783 | |
784 | /** |
785 | * Suppress generation of the table of contents. |
786 | * Used when parsing "code" pages (like JavaScript) as wikitext |
787 | * for backlink support and categories, but where we don't want |
788 | * other metadata generated (like the table of contents). |
789 | * @see T307691 |
790 | * @since 1.39 |
791 | * @deprecated since 1.42; just clear the metadata in the final |
792 | * parser output |
793 | */ |
794 | public function setSuppressTOC() { |
795 | wfDeprecated( __METHOD__, '1.42' ); |
796 | $this->setOption( 'suppressTOC', true ); |
797 | } |
798 | |
799 | /** |
800 | * Should section edit links be suppressed? |
801 | * Used when parsing wikitext which will be presented in a |
802 | * non-interactive context: previews, UX text, etc. |
803 | * @since 1.42 |
804 | * @return bool |
805 | */ |
806 | public function getSuppressSectionEditLinks() { |
807 | return $this->getOption( 'suppressSectionEditLinks' ); |
808 | } |
809 | |
810 | /** |
811 | * Suppress section edit links in the output. |
812 | * Used when parsing wikitext which will be presented in a |
813 | * non-interactive context: previews, UX text, etc. |
814 | * @since 1.42 |
815 | */ |
816 | public function setSuppressSectionEditLinks() { |
817 | $this->setOption( 'suppressSectionEditLinks', true ); |
818 | } |
819 | |
820 | /** |
821 | * Should section contents be wrapped in <div> to make them |
822 | * collapsible? |
823 | * @since 1.42 |
824 | */ |
825 | public function getCollapsibleSections(): bool { |
826 | return $this->getOption( 'collapsibleSections' ); |
827 | } |
828 | |
829 | /** |
830 | * Wrap section contents in a <div> to allow client-side code |
831 | * to collapse them. |
832 | * @since 1.42 |
833 | */ |
834 | public function setCollapsibleSections(): void { |
835 | $this->setOption( 'collapsibleSections', true ); |
836 | } |
837 | |
838 | /** |
839 | * If the wiki is configured to allow raw html ($wgRawHtml = true) |
840 | * is it allowed in the specific case of parsing this page. |
841 | * |
842 | * This is meant to disable unsafe parser tags in cases where |
843 | * a malicious user may control the input to the parser. |
844 | * |
845 | * @note This is expected to be true for normal pages even if the |
846 | * wiki has $wgRawHtml disabled in general. The setting only |
847 | * signifies that raw html would be unsafe in the current context |
848 | * provided that raw html is allowed at all. |
849 | * @since 1.29 |
850 | * @return bool |
851 | */ |
852 | public function getAllowUnsafeRawHtml() { |
853 | return $this->getOption( 'allowUnsafeRawHtml' ); |
854 | } |
855 | |
856 | /** |
857 | * If the wiki is configured to allow raw html ($wgRawHtml = true) |
858 | * is it allowed in the specific case of parsing this page. |
859 | * @see self::getAllowUnsafeRawHtml() |
860 | * @since 1.29 |
861 | * @param bool|null $x Value to set or null to get current value |
862 | * @return bool Current value for allowUnsafeRawHtml |
863 | */ |
864 | public function setAllowUnsafeRawHtml( $x ) { |
865 | return $this->setOptionLegacy( 'allowUnsafeRawHtml', $x ); |
866 | } |
867 | |
868 | /** |
869 | * Class to use to wrap output from Parser::parse() |
870 | * @since 1.30 |
871 | * @return string|false |
872 | */ |
873 | public function getWrapOutputClass() { |
874 | return $this->getOption( 'wrapclass' ); |
875 | } |
876 | |
877 | /** |
878 | * CSS class to use to wrap output from Parser::parse() |
879 | * @since 1.30 |
880 | * @param string $className Class name to use for wrapping. |
881 | * Passing false to indicate "no wrapping" was deprecated in MediaWiki 1.31. |
882 | * @return string|false Current value |
883 | */ |
884 | public function setWrapOutputClass( $className ) { |
885 | if ( $className === true ) { // DWIM, they probably want the default class name |
886 | $className = 'mw-parser-output'; |
887 | } |
888 | if ( $className === false ) { |
889 | wfDeprecated( __METHOD__ . '( false )', '1.31' ); |
890 | } |
891 | return $this->setOption( 'wrapclass', $className ); |
892 | } |
893 | |
894 | /** |
895 | * Callback for current revision fetching; first argument to call_user_func(). |
896 | * @internal |
897 | * @since 1.35 |
898 | * @return callable |
899 | */ |
900 | public function getCurrentRevisionRecordCallback() { |
901 | return $this->getOption( 'currentRevisionRecordCallback' ); |
902 | } |
903 | |
904 | /** |
905 | * Callback for current revision fetching; first argument to call_user_func(). |
906 | * @internal |
907 | * @since 1.35 |
908 | * @param callable|null $x New value |
909 | * @return callable Old value |
910 | */ |
911 | public function setCurrentRevisionRecordCallback( $x ) { |
912 | return $this->setOption( 'currentRevisionRecordCallback', $x ); |
913 | } |
914 | |
915 | /** |
916 | * Callback for template fetching; first argument to call_user_func(). |
917 | * @return callable |
918 | */ |
919 | public function getTemplateCallback() { |
920 | return $this->getOption( 'templateCallback' ); |
921 | } |
922 | |
923 | /** |
924 | * Callback for template fetching; first argument to call_user_func(). |
925 | * @param callable|null $x New value (null is no change) |
926 | * @return callable Old value |
927 | */ |
928 | public function setTemplateCallback( $x ) { |
929 | return $this->setOptionLegacy( 'templateCallback', $x ); |
930 | } |
931 | |
932 | /** |
933 | * A guess for {{REVISIONID}}, calculated using the callback provided via |
934 | * setSpeculativeRevIdCallback(). For consistency, the value will be calculated upon the |
935 | * first call of this method, and re-used for subsequent calls. |
936 | * |
937 | * If no callback was defined via setSpeculativeRevIdCallback(), this method will return false. |
938 | * |
939 | * @since 1.32 |
940 | * @return int|false |
941 | */ |
942 | public function getSpeculativeRevId() { |
943 | return $this->getOption( 'speculativeRevId' ); |
944 | } |
945 | |
946 | /** |
947 | * A guess for {{PAGEID}}, calculated using the callback provided via |
948 | * setSpeculativeRevPageCallback(). For consistency, the value will be calculated upon the |
949 | * first call of this method, and re-used for subsequent calls. |
950 | * |
951 | * If no callback was defined via setSpeculativePageIdCallback(), this method will return false. |
952 | * |
953 | * @since 1.34 |
954 | * @return int|false |
955 | */ |
956 | public function getSpeculativePageId() { |
957 | return $this->getOption( 'speculativePageId' ); |
958 | } |
959 | |
960 | /** |
961 | * Callback registered with ParserOptions::$lazyOptions, triggered by getSpeculativeRevId(). |
962 | * |
963 | * @param ParserOptions $popt |
964 | * @return int|false |
965 | */ |
966 | private static function initSpeculativeRevId( ParserOptions $popt ) { |
967 | $cb = $popt->getOption( 'speculativeRevIdCallback' ); |
968 | $id = $cb ? $cb() : null; |
969 | |
970 | // returning null would result in this being re-called every access |
971 | return $id ?? false; |
972 | } |
973 | |
974 | /** |
975 | * Callback registered with ParserOptions::$lazyOptions, triggered by getSpeculativePageId(). |
976 | * |
977 | * @param ParserOptions $popt |
978 | * @return int|false |
979 | */ |
980 | private static function initSpeculativePageId( ParserOptions $popt ) { |
981 | $cb = $popt->getOption( 'speculativePageIdCallback' ); |
982 | $id = $cb ? $cb() : null; |
983 | |
984 | // returning null would result in this being re-called every access |
985 | return $id ?? false; |
986 | } |
987 | |
988 | /** |
989 | * Callback to generate a guess for {{REVISIONID}} |
990 | * @param callable|null $x New value |
991 | * @return callable|null Old value |
992 | * @since 1.28 |
993 | */ |
994 | public function setSpeculativeRevIdCallback( $x ) { |
995 | $this->setOption( 'speculativeRevId', null ); // reset |
996 | return $this->setOption( 'speculativeRevIdCallback', $x ); |
997 | } |
998 | |
999 | /** |
1000 | * Callback to generate a guess for {{PAGEID}} |
1001 | * @param callable|null $x New value |
1002 | * @return callable|null Old value |
1003 | * @since 1.34 |
1004 | */ |
1005 | public function setSpeculativePageIdCallback( $x ) { |
1006 | $this->setOption( 'speculativePageId', null ); // reset |
1007 | return $this->setOption( 'speculativePageIdCallback', $x ); |
1008 | } |
1009 | |
1010 | /** |
1011 | * Timestamp used for {{CURRENTDAY}} etc. |
1012 | * @return string TS_MW timestamp |
1013 | */ |
1014 | public function getTimestamp() { |
1015 | if ( !isset( $this->mTimestamp ) ) { |
1016 | $this->mTimestamp = wfTimestampNow(); |
1017 | } |
1018 | return $this->mTimestamp; |
1019 | } |
1020 | |
1021 | /** |
1022 | * Timestamp used for {{CURRENTDAY}} etc. |
1023 | * @param string|null $x New value (null is no change) |
1024 | * @return string Old value |
1025 | */ |
1026 | public function setTimestamp( $x ) { |
1027 | return wfSetVar( $this->mTimestamp, $x ); |
1028 | } |
1029 | |
1030 | /** |
1031 | * Note that setting or changing this does not *make* the page a redirect |
1032 | * or change its target, it merely records the information for reference |
1033 | * during the parse. |
1034 | * |
1035 | * @since 1.24 |
1036 | * @param Title|null $title |
1037 | */ |
1038 | public function setRedirectTarget( $title ) { |
1039 | $this->redirectTarget = $title; |
1040 | } |
1041 | |
1042 | /** |
1043 | * Get the previously-set redirect target. |
1044 | * |
1045 | * @since 1.24 |
1046 | * @return Title|null |
1047 | */ |
1048 | public function getRedirectTarget() { |
1049 | return $this->redirectTarget; |
1050 | } |
1051 | |
1052 | /** |
1053 | * Extra key that should be present in the parser cache key. |
1054 | * @warning Consider registering your additional options with the |
1055 | * ParserOptionsRegister hook instead of using this method. |
1056 | * @param string $key |
1057 | */ |
1058 | public function addExtraKey( $key ) { |
1059 | $this->mExtraKey .= '!' . $key; |
1060 | } |
1061 | |
1062 | /** |
1063 | * Get the identity of the user for whom the parse is made. |
1064 | * @since 1.36 |
1065 | * @return UserIdentity |
1066 | */ |
1067 | public function getUserIdentity(): UserIdentity { |
1068 | return $this->mUser; |
1069 | } |
1070 | |
1071 | /** |
1072 | * @param UserIdentity $user |
1073 | * @param Language|null $lang |
1074 | */ |
1075 | public function __construct( UserIdentity $user, $lang = null ) { |
1076 | if ( $lang === null ) { |
1077 | global $wgLang; |
1078 | StubObject::unstub( $wgLang ); |
1079 | $lang = $wgLang; |
1080 | } |
1081 | $this->initialiseFromUser( $user, $lang ); |
1082 | } |
1083 | |
1084 | /** |
1085 | * Get a ParserOptions object for an anonymous user |
1086 | * @since 1.27 |
1087 | * @return ParserOptions |
1088 | */ |
1089 | public static function newFromAnon() { |
1090 | return new ParserOptions( MediaWikiServices::getInstance()->getUserFactory()->newAnonymous(), |
1091 | MediaWikiServices::getInstance()->getContentLanguage() ); |
1092 | } |
1093 | |
1094 | /** |
1095 | * Get a ParserOptions object from a given user. |
1096 | * Language will be taken from $wgLang. |
1097 | * |
1098 | * @param UserIdentity $user |
1099 | * @return ParserOptions |
1100 | */ |
1101 | public static function newFromUser( $user ) { |
1102 | return new ParserOptions( $user ); |
1103 | } |
1104 | |
1105 | /** |
1106 | * Get a ParserOptions object from a given user and language |
1107 | * |
1108 | * @param UserIdentity $user |
1109 | * @param Language $lang |
1110 | * @return ParserOptions |
1111 | */ |
1112 | public static function newFromUserAndLang( UserIdentity $user, Language $lang ) { |
1113 | return new ParserOptions( $user, $lang ); |
1114 | } |
1115 | |
1116 | /** |
1117 | * Get a ParserOptions object from a IContextSource object |
1118 | * |
1119 | * @param IContextSource $context |
1120 | * @return ParserOptions |
1121 | */ |
1122 | public static function newFromContext( IContextSource $context ) { |
1123 | $contextUser = $context->getUser(); |
1124 | |
1125 | // Use the stashed temporary account name instead of an IP address as the user for the ParserOptions |
1126 | // (if a stashed name is set). This is so that magic words like {{REVISIONUSER}} show the temporary account |
1127 | // name instead of IP address. |
1128 | $tempUserCreator = MediaWikiServices::getInstance()->getTempUserCreator(); |
1129 | if ( $tempUserCreator->isEnabled() && IPUtils::isIPAddress( $contextUser->getName() ) ) { |
1130 | // We do not attempt to acquire a temporary account name if no name is stashed, as this may be called in |
1131 | // contexts (such as the parse API) where the user will not be performing an edit on their next action |
1132 | // and therefore would be increasing the rate limit unnecessarily. |
1133 | $tempName = $tempUserCreator->getStashedName( $context->getRequest()->getSession() ); |
1134 | if ( $tempName !== null ) { |
1135 | $contextUser = UserIdentityValue::newAnonymous( $tempName ); |
1136 | } |
1137 | } |
1138 | |
1139 | return new ParserOptions( $contextUser, $context->getLanguage() ); |
1140 | } |
1141 | |
1142 | /** |
1143 | * Creates a "canonical" ParserOptions object |
1144 | * |
1145 | * For historical reasons, certain options have default values that are |
1146 | * different from the canonical values used for caching. |
1147 | * |
1148 | * @since 1.30 |
1149 | * @since 1.32 Added string and IContextSource as options for the first parameter |
1150 | * @since 1.36 UserIdentity is also allowed |
1151 | * @deprecated since 1.38. Use ::newFromContext, ::newFromAnon or ::newFromUserAndLang instead. |
1152 | * Canonical ParserOptions are now exactly the same as non-canonical. |
1153 | * @param IContextSource|string|UserIdentity $context |
1154 | * - If an IContextSource, the options are initialized based on the source's UserIdentity and Language. |
1155 | * - If the string 'canonical', the options are initialized with an anonymous user and |
1156 | * the content language. |
1157 | * - If a UserIdentity, the options are initialized for that UserIdentity |
1158 | * 'userlang' is taken from the $userLang parameter, defaulting to $wgLang if that is null. |
1159 | * @param Language|StubObject|null $userLang (see above) |
1160 | * @return ParserOptions |
1161 | */ |
1162 | public static function newCanonical( $context, $userLang = null ) { |
1163 | if ( $context instanceof IContextSource ) { |
1164 | $ret = self::newFromContext( $context ); |
1165 | } elseif ( $context === 'canonical' ) { |
1166 | $ret = self::newFromAnon(); |
1167 | } elseif ( $context instanceof UserIdentity ) { |
1168 | $ret = new self( $context, $userLang ); |
1169 | } else { |
1170 | throw new InvalidArgumentException( |
1171 | '$context must be an IContextSource, the string "canonical", or a UserIdentity' |
1172 | ); |
1173 | } |
1174 | return $ret; |
1175 | } |
1176 | |
1177 | /** |
1178 | * Reset static caches |
1179 | * @internal For testing |
1180 | */ |
1181 | public static function clearStaticCache() { |
1182 | if ( !defined( 'MW_PHPUNIT_TEST' ) && !defined( 'MW_PARSER_TEST' ) ) { |
1183 | throw new LogicException( __METHOD__ . ' is just for testing' ); |
1184 | } |
1185 | self::$defaults = null; |
1186 | self::$lazyOptions = null; |
1187 | self::$cacheVaryingOptionsHash = null; |
1188 | } |
1189 | |
1190 | /** |
1191 | * Get default option values |
1192 | * @warning If you change the default for an existing option, all existing |
1193 | * parser cache entries will be invalid. To avoid bugs, you'll need to handle |
1194 | * that somehow (e.g. with the RejectParserCacheValue hook) because |
1195 | * MediaWiki won't do it for you. |
1196 | * @return array |
1197 | */ |
1198 | private static function getDefaults() { |
1199 | $services = MediaWikiServices::getInstance(); |
1200 | $mainConfig = $services->getMainConfig(); |
1201 | $interwikiMagic = $mainConfig->get( MainConfigNames::InterwikiMagic ); |
1202 | $allowExternalImages = $mainConfig->get( MainConfigNames::AllowExternalImages ); |
1203 | $allowExternalImagesFrom = $mainConfig->get( MainConfigNames::AllowExternalImagesFrom ); |
1204 | $enableImageWhitelist = $mainConfig->get( MainConfigNames::EnableImageWhitelist ); |
1205 | $allowSpecialInclusion = $mainConfig->get( MainConfigNames::AllowSpecialInclusion ); |
1206 | $maxArticleSize = $mainConfig->get( MainConfigNames::MaxArticleSize ); |
1207 | $maxPPNodeCount = $mainConfig->get( MainConfigNames::MaxPPNodeCount ); |
1208 | $maxTemplateDepth = $mainConfig->get( MainConfigNames::MaxTemplateDepth ); |
1209 | $maxPPExpandDepth = $mainConfig->get( MainConfigNames::MaxPPExpandDepth ); |
1210 | $cleanSignatures = $mainConfig->get( MainConfigNames::CleanSignatures ); |
1211 | $externalLinkTarget = $mainConfig->get( MainConfigNames::ExternalLinkTarget ); |
1212 | $expensiveParserFunctionLimit = $mainConfig->get( MainConfigNames::ExpensiveParserFunctionLimit ); |
1213 | $enableMagicLinks = $mainConfig->get( MainConfigNames::EnableMagicLinks ); |
1214 | $languageConverterFactory = $services->getLanguageConverterFactory(); |
1215 | $userOptionsLookup = $services->getUserOptionsLookup(); |
1216 | $contentLanguage = $services->getContentLanguage(); |
1217 | |
1218 | if ( self::$defaults === null ) { |
1219 | // *UPDATE* ParserOptions::matches() if any of this changes as needed |
1220 | self::$defaults = [ |
1221 | 'dateformat' => null, |
1222 | 'interfaceMessage' => false, |
1223 | 'targetLanguage' => null, |
1224 | 'removeComments' => true, |
1225 | 'suppressTOC' => false, |
1226 | 'suppressSectionEditLinks' => false, |
1227 | 'collapsibleSections' => false, |
1228 | 'enableLimitReport' => false, |
1229 | 'preSaveTransform' => true, |
1230 | 'isPreview' => false, |
1231 | 'isSectionPreview' => false, |
1232 | 'printable' => false, |
1233 | 'allowUnsafeRawHtml' => true, |
1234 | 'wrapclass' => 'mw-parser-output', |
1235 | 'currentRevisionRecordCallback' => [ Parser::class, 'statelessFetchRevisionRecord' ], |
1236 | 'templateCallback' => [ Parser::class, 'statelessFetchTemplate' ], |
1237 | 'speculativeRevIdCallback' => null, |
1238 | 'speculativeRevId' => null, |
1239 | 'speculativePageIdCallback' => null, |
1240 | 'speculativePageId' => null, |
1241 | 'useParsoid' => false, |
1242 | ]; |
1243 | |
1244 | self::$cacheVaryingOptionsHash = self::$initialCacheVaryingOptionsHash; |
1245 | self::$lazyOptions = self::$initialLazyOptions; |
1246 | |
1247 | ( new HookRunner( $services->getHookContainer() ) )->onParserOptionsRegister( |
1248 | self::$defaults, |
1249 | self::$cacheVaryingOptionsHash, |
1250 | self::$lazyOptions |
1251 | ); |
1252 | |
1253 | ksort( self::$cacheVaryingOptionsHash ); |
1254 | } |
1255 | |
1256 | // Unit tests depend on being able to modify the globals at will |
1257 | return self::$defaults + [ |
1258 | 'interwikiMagic' => $interwikiMagic, |
1259 | 'allowExternalImages' => $allowExternalImages, |
1260 | 'allowExternalImagesFrom' => $allowExternalImagesFrom, |
1261 | 'enableImageWhitelist' => $enableImageWhitelist, |
1262 | 'allowSpecialInclusion' => $allowSpecialInclusion, |
1263 | 'maxIncludeSize' => $maxArticleSize * 1024, |
1264 | 'maxPPNodeCount' => $maxPPNodeCount, |
1265 | 'maxPPExpandDepth' => $maxPPExpandDepth, |
1266 | 'maxTemplateDepth' => $maxTemplateDepth, |
1267 | 'expensiveParserFunctionLimit' => $expensiveParserFunctionLimit, |
1268 | 'externalLinkTarget' => $externalLinkTarget, |
1269 | 'cleanSignatures' => $cleanSignatures, |
1270 | 'disableContentConversion' => $languageConverterFactory->isConversionDisabled(), |
1271 | 'disableTitleConversion' => $languageConverterFactory->isLinkConversionDisabled(), |
1272 | // FIXME: The fallback to false for enableMagicLinks is a band-aid to allow |
1273 | // the phpunit entrypoint patch (I82045c207738d152d5b0006f353637cfaa40bb66) |
1274 | // to be merged. |
1275 | // It is possible that a test somewhere is globally resetting $wgEnableMagicLinks |
1276 | // to null, or that ParserOptions is somehow similarly getting reset in such a way |
1277 | // that $enableMagicLinks ends up as null rather than an array. This workaround |
1278 | // seems harmless, but would be nice to eventually fix the underlying issue. |
1279 | 'magicISBNLinks' => $enableMagicLinks['ISBN'] ?? false, |
1280 | 'magicPMIDLinks' => $enableMagicLinks['PMID'] ?? false, |
1281 | 'magicRFCLinks' => $enableMagicLinks['RFC'] ?? false, |
1282 | 'thumbsize' => $userOptionsLookup->getDefaultOption( 'thumbsize' ), |
1283 | 'userlang' => $contentLanguage, |
1284 | ]; |
1285 | } |
1286 | |
1287 | /** |
1288 | * Get user options |
1289 | * |
1290 | * @param UserIdentity $user |
1291 | * @param Language $lang |
1292 | */ |
1293 | private function initialiseFromUser( UserIdentity $user, Language $lang ) { |
1294 | // Initially lazy loaded option defaults must not be taken into account, |
1295 | // otherwise lazy loading does not work. Setting a default for lazy option |
1296 | // is useful for matching with canonical options. |
1297 | $this->options = $this->nullifyLazyOption( self::getDefaults() ); |
1298 | |
1299 | $this->mUser = $user; |
1300 | $services = MediaWikiServices::getInstance(); |
1301 | $optionsLookup = $services->getUserOptionsLookup(); |
1302 | $this->options['thumbsize'] = $optionsLookup->getOption( $user, 'thumbsize' ); |
1303 | $this->options['userlang'] = $lang; |
1304 | } |
1305 | |
1306 | /** |
1307 | * Check if these options match that of another options set |
1308 | * |
1309 | * This ignores report limit settings that only affect HTML comments |
1310 | * |
1311 | * @param ParserOptions $other |
1312 | * @return bool |
1313 | * @since 1.25 |
1314 | */ |
1315 | public function matches( ParserOptions $other ) { |
1316 | // Compare most options |
1317 | $options = array_keys( $this->options ); |
1318 | $options = array_diff( $options, [ |
1319 | 'enableLimitReport', // only affects HTML comments |
1320 | 'tidy', // Has no effect since 1.35; removed in 1.36 |
1321 | ] ); |
1322 | foreach ( $options as $option ) { |
1323 | // Resolve any lazy options |
1324 | $this->lazyLoadOption( $option ); |
1325 | $other->lazyLoadOption( $option ); |
1326 | |
1327 | $o1 = $this->optionToString( $this->options[$option] ); |
1328 | $o2 = $this->optionToString( $other->options[$option] ); |
1329 | if ( $o1 !== $o2 ) { |
1330 | return false; |
1331 | } |
1332 | } |
1333 | |
1334 | // Compare most other fields |
1335 | foreach ( ( new ReflectionClass( $this ) )->getProperties() as $property ) { |
1336 | $field = $property->getName(); |
1337 | if ( $property->isStatic() ) { |
1338 | continue; |
1339 | } |
1340 | if ( in_array( $field, [ |
1341 | 'options', // Already checked above |
1342 | 'onAccessCallback', // only used for ParserOutput option tracking |
1343 | ] ) ) { |
1344 | continue; |
1345 | } |
1346 | |
1347 | if ( !is_object( $this->$field ) && $this->$field !== $other->$field ) { |
1348 | return false; |
1349 | } |
1350 | } |
1351 | |
1352 | return true; |
1353 | } |
1354 | |
1355 | /** |
1356 | * @param ParserOptions $other |
1357 | * @return bool Whether the cache key relevant options match those of $other |
1358 | * @since 1.33 |
1359 | */ |
1360 | public function matchesForCacheKey( ParserOptions $other ) { |
1361 | foreach ( self::allCacheVaryingOptions() as $option ) { |
1362 | // Populate any lazy options |
1363 | $this->lazyLoadOption( $option ); |
1364 | $other->lazyLoadOption( $option ); |
1365 | |
1366 | $o1 = $this->optionToString( $this->options[$option] ); |
1367 | $o2 = $this->optionToString( $other->options[$option] ); |
1368 | if ( $o1 !== $o2 ) { |
1369 | return false; |
1370 | } |
1371 | } |
1372 | |
1373 | return true; |
1374 | } |
1375 | |
1376 | /** |
1377 | * Registers a callback for tracking which ParserOptions which are used. |
1378 | * |
1379 | * @since 1.16 |
1380 | * @param callable|null $callback |
1381 | */ |
1382 | public function registerWatcher( $callback ) { |
1383 | $this->onAccessCallback = $callback; |
1384 | } |
1385 | |
1386 | /** |
1387 | * Record that an option was internally accessed. |
1388 | * |
1389 | * This calls the watcher set by ParserOptions::registerWatcher(). |
1390 | * Typically, the watcher callback is ParserOutput::recordOption(). |
1391 | * The information registered this way is consumed by ParserCache::save(). |
1392 | * |
1393 | * @param string $optionName Name of the option |
1394 | */ |
1395 | private function optionUsed( $optionName ) { |
1396 | if ( $this->onAccessCallback ) { |
1397 | call_user_func( $this->onAccessCallback, $optionName ); |
1398 | } |
1399 | } |
1400 | |
1401 | /** |
1402 | * Return all option keys that vary the options hash |
1403 | * @since 1.30 |
1404 | * @return string[] |
1405 | */ |
1406 | public static function allCacheVaryingOptions() { |
1407 | return array_keys( array_filter( self::getCacheVaryingOptionsHash() ) ); |
1408 | } |
1409 | |
1410 | /** |
1411 | * Convert an option to a string value |
1412 | * @param mixed $value |
1413 | * @return string |
1414 | */ |
1415 | private function optionToString( $value ) { |
1416 | if ( $value === true ) { |
1417 | return '1'; |
1418 | } elseif ( $value === false ) { |
1419 | return '0'; |
1420 | } elseif ( $value === null ) { |
1421 | return ''; |
1422 | } elseif ( $value instanceof Language ) { |
1423 | return $value->getCode(); |
1424 | } elseif ( is_array( $value ) ) { |
1425 | return '[' . implode( ',', array_map( [ $this, 'optionToString' ], $value ) ) . ']'; |
1426 | } else { |
1427 | return (string)$value; |
1428 | } |
1429 | } |
1430 | |
1431 | /** |
1432 | * Generate a hash string with the values set on these ParserOptions |
1433 | * for the keys given in the array. |
1434 | * This will be used as part of the hash key for the parser cache, |
1435 | * so users sharing the options with vary for the same page share |
1436 | * the same cached data safely. |
1437 | * |
1438 | * @since 1.17 |
1439 | * @param string[] $forOptions |
1440 | * @param Title|null $title Used to get the content language of the page (since r97636) |
1441 | * @return string Page rendering hash |
1442 | */ |
1443 | public function optionsHash( $forOptions, $title = null ) { |
1444 | $renderHashAppend = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::RenderHashAppend ); |
1445 | |
1446 | $inCacheKey = self::allCacheVaryingOptions(); |
1447 | |
1448 | // Resolve any lazy options |
1449 | $lazyOpts = array_intersect( $forOptions, |
1450 | $inCacheKey, array_keys( self::getLazyOptions() ) ); |
1451 | foreach ( $lazyOpts as $k ) { |
1452 | $this->lazyLoadOption( $k ); |
1453 | } |
1454 | |
1455 | $options = $this->options; |
1456 | $defaults = self::getDefaults(); |
1457 | |
1458 | // We only include used options with non-canonical values in the key |
1459 | // so adding a new option doesn't invalidate the entire parser cache. |
1460 | // The drawback to this is that changing the default value of an option |
1461 | // requires manual invalidation of existing cache entries, as mentioned |
1462 | // in the docs on the relevant methods and hooks. |
1463 | $values = []; |
1464 | foreach ( array_intersect( $inCacheKey, $forOptions ) as $option ) { |
1465 | $v = $this->optionToString( $options[$option] ); |
1466 | $d = $this->optionToString( $defaults[$option] ); |
1467 | if ( $v !== $d ) { |
1468 | $values[] = "$option=$v"; |
1469 | } |
1470 | } |
1471 | |
1472 | $confstr = $values ? implode( '!', $values ) : 'canonical'; |
1473 | |
1474 | // add in language specific options, if any |
1475 | // @todo FIXME: This is just a way of retrieving the url/user preferred variant |
1476 | $services = MediaWikiServices::getInstance(); |
1477 | $lang = $title ? $title->getPageLanguage() : $services->getContentLanguage(); |
1478 | $converter = $services->getLanguageConverterFactory()->getLanguageConverter( $lang ); |
1479 | $confstr .= $converter->getExtraHashOptions(); |
1480 | |
1481 | $confstr .= $renderHashAppend; |
1482 | |
1483 | if ( $this->mExtraKey != '' ) { |
1484 | $confstr .= $this->mExtraKey; |
1485 | } |
1486 | |
1487 | $user = $services->getUserFactory()->newFromUserIdentity( $this->getUserIdentity() ); |
1488 | // Give a chance for extensions to modify the hash, if they have |
1489 | // extra options or other effects on the parser cache. |
1490 | ( new HookRunner( $services->getHookContainer() ) )->onPageRenderingHash( |
1491 | $confstr, |
1492 | $user, |
1493 | $forOptions |
1494 | ); |
1495 | |
1496 | // Make it a valid memcached key fragment |
1497 | $confstr = str_replace( ' ', '_', $confstr ); |
1498 | |
1499 | return $confstr; |
1500 | } |
1501 | |
1502 | /** |
1503 | * Test whether these options are safe to cache |
1504 | * @param string[]|null $usedOptions the list of options actually used in the parse. Defaults to all options. |
1505 | * @return bool |
1506 | * @since 1.30 |
1507 | */ |
1508 | public function isSafeToCache( ?array $usedOptions = null ) { |
1509 | $defaults = self::getDefaults(); |
1510 | $inCacheKey = self::getCacheVaryingOptionsHash(); |
1511 | $usedOptions ??= array_keys( $this->options ); |
1512 | foreach ( $usedOptions as $option ) { |
1513 | if ( empty( $inCacheKey[$option] ) && empty( self::$callbacks[$option] ) ) { |
1514 | $v = $this->optionToString( $this->options[$option] ?? null ); |
1515 | $d = $this->optionToString( $defaults[$option] ?? null ); |
1516 | if ( $v !== $d ) { |
1517 | return false; |
1518 | } |
1519 | } |
1520 | } |
1521 | return true; |
1522 | } |
1523 | |
1524 | /** |
1525 | * Sets a hook to force that a page exists, and sets a current revision callback to return |
1526 | * a revision with custom content when the current revision of the page is requested. |
1527 | * |
1528 | * @since 1.25 |
1529 | * @param Title $title |
1530 | * @param Content $content |
1531 | * @param UserIdentity $user The user that the fake revision is attributed to |
1532 | * @return ScopedCallback to unset the hook |
1533 | */ |
1534 | public function setupFakeRevision( $title, $content, $user ) { |
1535 | $oldCallback = $this->setCurrentRevisionRecordCallback( |
1536 | static function ( |
1537 | $titleToCheck, $parser = null ) use ( $title, $content, $user, &$oldCallback |
1538 | ) { |
1539 | if ( $titleToCheck->equals( $title ) ) { |
1540 | $revRecord = new MutableRevisionRecord( $title ); |
1541 | $revRecord->setContent( SlotRecord::MAIN, $content ) |
1542 | ->setUser( $user ) |
1543 | ->setTimestamp( MWTimestamp::now( TS_MW ) ) |
1544 | ->setPageId( $title->getArticleID() ) |
1545 | ->setParentId( $title->getLatestRevID() ); |
1546 | return $revRecord; |
1547 | } else { |
1548 | return call_user_func( $oldCallback, $titleToCheck, $parser ); |
1549 | } |
1550 | } |
1551 | ); |
1552 | |
1553 | $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); |
1554 | $hookScope = $hookContainer->scopedRegister( |
1555 | 'TitleExists', |
1556 | static function ( $titleToCheck, &$exists ) use ( $title ) { |
1557 | if ( $titleToCheck->equals( $title ) ) { |
1558 | $exists = true; |
1559 | } |
1560 | } |
1561 | ); |
1562 | |
1563 | $linkCache = MediaWikiServices::getInstance()->getLinkCache(); |
1564 | $linkCache->clearBadLink( $title->getPrefixedDBkey() ); |
1565 | |
1566 | return new ScopedCallback( function () use ( $title, $hookScope, $linkCache, $oldCallback ) { |
1567 | ScopedCallback::consume( $hookScope ); |
1568 | $linkCache->clearLink( $title ); |
1569 | $this->setCurrentRevisionRecordCallback( $oldCallback ); |
1570 | } ); |
1571 | } |
1572 | |
1573 | /** |
1574 | * Returns reason for rendering the content. This human-readable, intended for logging and debugging only. |
1575 | * Expected values include "edit", "view", "purge", "LinksUpdate", etc. |
1576 | * @return string |
1577 | */ |
1578 | public function getRenderReason(): string { |
1579 | return $this->renderReason; |
1580 | } |
1581 | |
1582 | /** |
1583 | * Sets reason for rendering the content. This human-readable, intended for logging and debugging only. |
1584 | * Expected values include "edit", "view", "purge", "LinksUpdate", etc. |
1585 | * @param string $renderReason |
1586 | */ |
1587 | public function setRenderReason( string $renderReason ): void { |
1588 | $this->renderReason = $renderReason; |
1589 | } |
1590 | } |
1591 | |
1592 | /** @deprecated class alias since 1.43 */ |
1593 | class_alias( ParserOptions::class, 'ParserOptions' ); |
1594 | |
1595 | /** |
1596 | * For really cool vim folding this needs to be at the end: |
1597 | * vim: foldmarker=@{,@} foldmethod=marker |
1598 | */ |