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