Merge "Add a hook to allow extensions to prevent HTML file caching"
[lhc/web/wiklou.git] / includes / parser / ParserOptions.php
1 <?php
2 /**
3 * Options for the PHP parser
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Parser
22 */
23
24 /**
25 * @brief Set options of the Parser
26 *
27 * All member variables are supposed to be private in theory, although in
28 * practise this is not the case.
29 *
30 * @ingroup Parser
31 */
32 class ParserOptions {
33
34 /**
35 * Interlanguage links are removed and returned in an array
36 */
37 public $mInterwikiMagic;
38
39 /**
40 * Allow external images inline?
41 */
42 public $mAllowExternalImages;
43
44 /**
45 * If not, any exception?
46 */
47 public $mAllowExternalImagesFrom;
48
49 /**
50 * If not or it doesn't match, should we check an on-wiki whitelist?
51 */
52 public $mEnableImageWhitelist;
53
54 /**
55 * Date format index
56 */
57 public $mDateFormat = null;
58
59 /**
60 * Create "edit section" links?
61 */
62 public $mEditSection = true;
63
64 /**
65 * Allow inclusion of special pages?
66 */
67 public $mAllowSpecialInclusion;
68
69 /**
70 * Use tidy to cleanup output HTML?
71 */
72 public $mTidy = false;
73
74 /**
75 * Which lang to call for PLURAL and GRAMMAR
76 */
77 public $mInterfaceMessage = false;
78
79 /**
80 * Overrides $mInterfaceMessage with arbitrary language
81 */
82 public $mTargetLanguage = null;
83
84 /**
85 * Maximum size of template expansions, in bytes
86 */
87 public $mMaxIncludeSize;
88
89 /**
90 * Maximum number of nodes touched by PPFrame::expand()
91 */
92 public $mMaxPPNodeCount;
93
94 /**
95 * Maximum number of nodes generated by Preprocessor::preprocessToObj()
96 */
97 public $mMaxGeneratedPPNodeCount;
98
99 /**
100 * Maximum recursion depth in PPFrame::expand()
101 */
102 public $mMaxPPExpandDepth;
103
104 /**
105 * Maximum recursion depth for templates within templates
106 */
107 public $mMaxTemplateDepth;
108
109 /**
110 * Maximum number of calls per parse to expensive parser functions
111 */
112 public $mExpensiveParserFunctionLimit;
113
114 /**
115 * Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
116 */
117 public $mRemoveComments = true;
118
119 /**
120 * Callback for template fetching. Used as first argument to call_user_func().
121 */
122 public $mTemplateCallback =
123 array( 'Parser', 'statelessFetchTemplate' );
124
125 /**
126 * Enable limit report in an HTML comment on output
127 */
128 public $mEnableLimitReport = false;
129
130 /**
131 * Timestamp used for {{CURRENTDAY}} etc.
132 */
133 public $mTimestamp;
134
135 /**
136 * Target attribute for external links
137 */
138 public $mExternalLinkTarget;
139
140 /**
141 * Clean up signature texts?
142 *
143 * 1) Strip ~~~, ~~~~ and ~~~~~ out of signatures
144 * 2) Substitute all transclusions
145 */
146 public $mCleanSignatures;
147
148 /**
149 * Transform wiki markup when saving the page?
150 */
151 public $mPreSaveTransform = true;
152
153 /**
154 * Whether content conversion should be disabled
155 */
156 public $mDisableContentConversion;
157
158 /**
159 * Whether title conversion should be disabled
160 */
161 public $mDisableTitleConversion;
162
163 /**
164 * Automatically number headings?
165 */
166 public $mNumberHeadings;
167
168 /**
169 * Thumb size preferred by the user.
170 */
171 public $mThumbSize;
172
173 /**
174 * Maximum article size of an article to be marked as "stub"
175 */
176 private $mStubThreshold;
177
178 /**
179 * Language object of the User language.
180 */
181 public $mUserLang;
182
183 /**
184 * @var User
185 * Stored user object
186 */
187 public $mUser;
188
189 /**
190 * Parsing the page for a "preview" operation?
191 */
192 public $mIsPreview = false;
193
194 /**
195 * Parsing the page for a "preview" operation on a single section?
196 */
197 public $mIsSectionPreview = false;
198
199 /**
200 * Parsing the printable version of the page?
201 */
202 public $mIsPrintable = false;
203
204 /**
205 * Extra key that should be present in the caching key.
206 */
207 public $mExtraKey = '';
208
209 /**
210 * Function to be called when an option is accessed.
211 */
212 protected $onAccessCallback = null;
213
214 /**
215 * If the page being parsed is a redirect, this should hold the redirect
216 * target.
217 * @var Title|null
218 */
219 private $redirectTarget = null;
220
221 public function getInterwikiMagic() {
222 return $this->mInterwikiMagic;
223 }
224
225 public function getAllowExternalImages() {
226 return $this->mAllowExternalImages;
227 }
228
229 public function getAllowExternalImagesFrom() {
230 return $this->mAllowExternalImagesFrom;
231 }
232
233 public function getEnableImageWhitelist() {
234 return $this->mEnableImageWhitelist;
235 }
236
237 public function getEditSection() {
238 return $this->mEditSection;
239 }
240
241 public function getNumberHeadings() {
242 $this->optionUsed( 'numberheadings' );
243
244 return $this->mNumberHeadings;
245 }
246
247 public function getAllowSpecialInclusion() {
248 return $this->mAllowSpecialInclusion;
249 }
250
251 public function getTidy() {
252 return $this->mTidy;
253 }
254
255 public function getInterfaceMessage() {
256 return $this->mInterfaceMessage;
257 }
258
259 public function getTargetLanguage() {
260 return $this->mTargetLanguage;
261 }
262
263 public function getMaxIncludeSize() {
264 return $this->mMaxIncludeSize;
265 }
266
267 public function getMaxPPNodeCount() {
268 return $this->mMaxPPNodeCount;
269 }
270
271 public function getMaxGeneratedPPNodeCount() {
272 return $this->mMaxGeneratedPPNodeCount;
273 }
274
275 public function getMaxPPExpandDepth() {
276 return $this->mMaxPPExpandDepth;
277 }
278
279 public function getMaxTemplateDepth() {
280 return $this->mMaxTemplateDepth;
281 }
282
283 /* @since 1.20 */
284 public function getExpensiveParserFunctionLimit() {
285 return $this->mExpensiveParserFunctionLimit;
286 }
287
288 public function getRemoveComments() {
289 return $this->mRemoveComments;
290 }
291
292 public function getTemplateCallback() {
293 return $this->mTemplateCallback;
294 }
295
296 public function getEnableLimitReport() {
297 return $this->mEnableLimitReport;
298 }
299
300 public function getCleanSignatures() {
301 return $this->mCleanSignatures;
302 }
303
304 public function getExternalLinkTarget() {
305 return $this->mExternalLinkTarget;
306 }
307
308 public function getDisableContentConversion() {
309 return $this->mDisableContentConversion;
310 }
311
312 public function getDisableTitleConversion() {
313 return $this->mDisableTitleConversion;
314 }
315
316 public function getThumbSize() {
317 $this->optionUsed( 'thumbsize' );
318
319 return $this->mThumbSize;
320 }
321
322 public function getStubThreshold() {
323 $this->optionUsed( 'stubthreshold' );
324
325 return $this->mStubThreshold;
326 }
327
328 public function getIsPreview() {
329 return $this->mIsPreview;
330 }
331
332 public function getIsSectionPreview() {
333 return $this->mIsSectionPreview;
334 }
335
336 public function getIsPrintable() {
337 $this->optionUsed( 'printable' );
338
339 return $this->mIsPrintable;
340 }
341
342 public function getUser() {
343 return $this->mUser;
344 }
345
346 public function getPreSaveTransform() {
347 return $this->mPreSaveTransform;
348 }
349
350 public function getDateFormat() {
351 $this->optionUsed( 'dateformat' );
352 if ( !isset( $this->mDateFormat ) ) {
353 $this->mDateFormat = $this->mUser->getDatePreference();
354 }
355 return $this->mDateFormat;
356 }
357
358 public function getTimestamp() {
359 if ( !isset( $this->mTimestamp ) ) {
360 $this->mTimestamp = wfTimestampNow();
361 }
362 return $this->mTimestamp;
363 }
364
365 /**
366 * Get the user language used by the parser for this page.
367 *
368 * You shouldn't use this. Really. $parser->getFunctionLang() is all you need.
369 *
370 * To avoid side-effects where the page will be rendered based on the language
371 * of the user who last saved, this function will triger a cache fragmentation.
372 * Usage of this method is discouraged for that reason.
373 *
374 * When saving, this will return the default language instead of the user's.
375 *
376 * {{int: }} uses this which used to produce inconsistent link tables (bug 14404).
377 *
378 * @return Language
379 * @since 1.19
380 */
381 public function getUserLangObj() {
382 $this->optionUsed( 'userlang' );
383 return $this->mUserLang;
384 }
385
386 /**
387 * Same as getUserLangObj() but returns a string instead.
388 *
389 * @return string Language code
390 * @since 1.17
391 */
392 public function getUserLang() {
393 return $this->getUserLangObj()->getCode();
394 }
395
396 public function setInterwikiMagic( $x ) {
397 return wfSetVar( $this->mInterwikiMagic, $x );
398 }
399
400 public function setAllowExternalImages( $x ) {
401 return wfSetVar( $this->mAllowExternalImages, $x );
402 }
403
404 public function setAllowExternalImagesFrom( $x ) {
405 return wfSetVar( $this->mAllowExternalImagesFrom, $x );
406 }
407
408 public function setEnableImageWhitelist( $x ) {
409 return wfSetVar( $this->mEnableImageWhitelist, $x );
410 }
411
412 public function setDateFormat( $x ) {
413 return wfSetVar( $this->mDateFormat, $x );
414 }
415
416 public function setEditSection( $x ) {
417 return wfSetVar( $this->mEditSection, $x );
418 }
419
420 public function setNumberHeadings( $x ) {
421 return wfSetVar( $this->mNumberHeadings, $x );
422 }
423
424 public function setAllowSpecialInclusion( $x ) {
425 return wfSetVar( $this->mAllowSpecialInclusion, $x );
426 }
427
428 public function setTidy( $x ) {
429 return wfSetVar( $this->mTidy, $x );
430 }
431
432 public function setInterfaceMessage( $x ) {
433 return wfSetVar( $this->mInterfaceMessage, $x );
434 }
435
436 public function setTargetLanguage( $x ) {
437 return wfSetVar( $this->mTargetLanguage, $x, true );
438 }
439
440 public function setMaxIncludeSize( $x ) {
441 return wfSetVar( $this->mMaxIncludeSize, $x );
442 }
443
444 public function setMaxPPNodeCount( $x ) {
445 return wfSetVar( $this->mMaxPPNodeCount, $x );
446 }
447
448 public function setMaxGeneratedPPNodeCount( $x ) {
449 return wfSetVar( $this->mMaxGeneratedPPNodeCount, $x );
450 }
451
452 public function setMaxTemplateDepth( $x ) {
453 return wfSetVar( $this->mMaxTemplateDepth, $x );
454 }
455
456 /* @since 1.20 */
457 public function setExpensiveParserFunctionLimit( $x ) {
458 return wfSetVar( $this->mExpensiveParserFunctionLimit, $x );
459 }
460
461 public function setRemoveComments( $x ) {
462 return wfSetVar( $this->mRemoveComments, $x );
463 }
464
465 public function setTemplateCallback( $x ) {
466 return wfSetVar( $this->mTemplateCallback, $x );
467 }
468
469 public function enableLimitReport( $x = true ) {
470 return wfSetVar( $this->mEnableLimitReport, $x );
471 }
472
473 public function setTimestamp( $x ) {
474 return wfSetVar( $this->mTimestamp, $x );
475 }
476
477 public function setCleanSignatures( $x ) {
478 return wfSetVar( $this->mCleanSignatures, $x );
479 }
480
481 public function setExternalLinkTarget( $x ) {
482 return wfSetVar( $this->mExternalLinkTarget, $x );
483 }
484
485 public function disableContentConversion( $x = true ) {
486 return wfSetVar( $this->mDisableContentConversion, $x );
487 }
488
489 public function disableTitleConversion( $x = true ) {
490 return wfSetVar( $this->mDisableTitleConversion, $x );
491 }
492
493 public function setUserLang( $x ) {
494 if ( is_string( $x ) ) {
495 $x = Language::factory( $x );
496 }
497
498 return wfSetVar( $this->mUserLang, $x );
499 }
500
501 public function setThumbSize( $x ) {
502 return wfSetVar( $this->mThumbSize, $x );
503 }
504
505 public function setStubThreshold( $x ) {
506 return wfSetVar( $this->mStubThreshold, $x );
507 }
508
509 public function setPreSaveTransform( $x ) {
510 return wfSetVar( $this->mPreSaveTransform, $x );
511 }
512
513 public function setIsPreview( $x ) {
514 return wfSetVar( $this->mIsPreview, $x );
515 }
516
517 public function setIsSectionPreview( $x ) {
518 return wfSetVar( $this->mIsSectionPreview, $x );
519 }
520
521 public function setIsPrintable( $x ) {
522 return wfSetVar( $this->mIsPrintable, $x );
523 }
524
525 /**
526 * Set the redirect target.
527 *
528 * Note that setting or changing this does not *make* the page a redirect
529 * or change its target, it merely records the information for reference
530 * during the parse.
531 *
532 * @since 1.24
533 * @param Title|null $title
534 */
535 function setRedirectTarget( $title ) {
536 $this->redirectTarget = $title;
537 }
538
539 /**
540 * Get the previously-set redirect target.
541 *
542 * @since 1.24
543 * @return Title|null
544 */
545 function getRedirectTarget() {
546 return $this->redirectTarget;
547 }
548
549 /**
550 * Extra key that should be present in the parser cache key.
551 * @param string $key
552 */
553 public function addExtraKey( $key ) {
554 $this->mExtraKey .= '!' . $key;
555 }
556
557 /**
558 * Constructor
559 * @param User $user
560 * @param Language $lang
561 */
562 public function __construct( $user = null, $lang = null ) {
563 if ( $user === null ) {
564 global $wgUser;
565 if ( $wgUser === null ) {
566 $user = new User;
567 } else {
568 $user = $wgUser;
569 }
570 }
571 if ( $lang === null ) {
572 global $wgLang;
573 if ( !StubObject::isRealObject( $wgLang ) ) {
574 $wgLang->_unstub();
575 }
576 $lang = $wgLang;
577 }
578 $this->initialiseFromUser( $user, $lang );
579 }
580
581 /**
582 * Get a ParserOptions object from a given user.
583 * Language will be taken from $wgLang.
584 *
585 * @param User $user
586 * @return ParserOptions
587 */
588 public static function newFromUser( $user ) {
589 return new ParserOptions( $user );
590 }
591
592 /**
593 * Get a ParserOptions object from a given user and language
594 *
595 * @param User $user
596 * @param Language $lang
597 * @return ParserOptions
598 */
599 public static function newFromUserAndLang( User $user, Language $lang ) {
600 return new ParserOptions( $user, $lang );
601 }
602
603 /**
604 * Get a ParserOptions object from a IContextSource object
605 *
606 * @param IContextSource $context
607 * @return ParserOptions
608 */
609 public static function newFromContext( IContextSource $context ) {
610 return new ParserOptions( $context->getUser(), $context->getLanguage() );
611 }
612
613 /**
614 * Get user options
615 *
616 * @param User $user
617 * @param Language $lang
618 */
619 private function initialiseFromUser( $user, $lang ) {
620 global $wgInterwikiMagic, $wgAllowExternalImages,
621 $wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion,
622 $wgMaxArticleSize, $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth,
623 $wgCleanSignatures, $wgExternalLinkTarget, $wgExpensiveParserFunctionLimit,
624 $wgMaxGeneratedPPNodeCount, $wgDisableLangConversion, $wgDisableTitleConversion;
625
626 wfProfileIn( __METHOD__ );
627
628 $this->mInterwikiMagic = $wgInterwikiMagic;
629 $this->mAllowExternalImages = $wgAllowExternalImages;
630 $this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
631 $this->mEnableImageWhitelist = $wgEnableImageWhitelist;
632 $this->mAllowSpecialInclusion = $wgAllowSpecialInclusion;
633 $this->mMaxIncludeSize = $wgMaxArticleSize * 1024;
634 $this->mMaxPPNodeCount = $wgMaxPPNodeCount;
635 $this->mMaxGeneratedPPNodeCount = $wgMaxGeneratedPPNodeCount;
636 $this->mMaxPPExpandDepth = $wgMaxPPExpandDepth;
637 $this->mMaxTemplateDepth = $wgMaxTemplateDepth;
638 $this->mExpensiveParserFunctionLimit = $wgExpensiveParserFunctionLimit;
639 $this->mCleanSignatures = $wgCleanSignatures;
640 $this->mExternalLinkTarget = $wgExternalLinkTarget;
641 $this->mDisableContentConversion = $wgDisableLangConversion;
642 $this->mDisableTitleConversion = $wgDisableLangConversion || $wgDisableTitleConversion;
643
644 $this->mUser = $user;
645 $this->mNumberHeadings = $user->getOption( 'numberheadings' );
646 $this->mThumbSize = $user->getOption( 'thumbsize' );
647 $this->mStubThreshold = $user->getStubThreshold();
648 $this->mUserLang = $lang;
649
650 wfProfileOut( __METHOD__ );
651 }
652
653 /**
654 * Registers a callback for tracking which ParserOptions which are used.
655 * This is a private API with the parser.
656 * @param callable $callback
657 */
658 public function registerWatcher( $callback ) {
659 $this->onAccessCallback = $callback;
660 }
661
662 /**
663 * Called when an option is accessed.
664 * @param string $optionName Name of the option
665 */
666 public function optionUsed( $optionName ) {
667 if ( $this->onAccessCallback ) {
668 call_user_func( $this->onAccessCallback, $optionName );
669 }
670 }
671
672 /**
673 * Returns the full array of options that would have been used by
674 * in 1.16.
675 * Used to get the old parser cache entries when available.
676 * @return array
677 */
678 public static function legacyOptions() {
679 return array(
680 'stubthreshold',
681 'numberheadings',
682 'userlang',
683 'thumbsize',
684 'editsection',
685 'printable'
686 );
687 }
688
689 /**
690 * Generate a hash string with the values set on these ParserOptions
691 * for the keys given in the array.
692 * This will be used as part of the hash key for the parser cache,
693 * so users sharing the options with vary for the same page share
694 * the same cached data safely.
695 *
696 * Extensions which require it should install 'PageRenderingHash' hook,
697 * which will give them a chance to modify this key based on their own
698 * settings.
699 *
700 * @since 1.17
701 * @param array $forOptions
702 * @param Title $title Used to get the content language of the page (since r97636)
703 * @return string Page rendering hash
704 */
705 public function optionsHash( $forOptions, $title = null ) {
706 global $wgRenderHashAppend;
707
708 // FIXME: Once the cache key is reorganized this argument
709 // can be dropped. It was used when the math extension was
710 // part of core.
711 $confstr = '*';
712
713 // Space assigned for the stubthreshold but unused
714 // since it disables the parser cache, its value will always
715 // be 0 when this function is called by parsercache.
716 if ( in_array( 'stubthreshold', $forOptions ) ) {
717 $confstr .= '!' . $this->mStubThreshold;
718 } else {
719 $confstr .= '!*';
720 }
721
722 if ( in_array( 'dateformat', $forOptions ) ) {
723 $confstr .= '!' . $this->getDateFormat();
724 }
725
726 if ( in_array( 'numberheadings', $forOptions ) ) {
727 $confstr .= '!' . ( $this->mNumberHeadings ? '1' : '' );
728 } else {
729 $confstr .= '!*';
730 }
731
732 if ( in_array( 'userlang', $forOptions ) ) {
733 $confstr .= '!' . $this->mUserLang->getCode();
734 } else {
735 $confstr .= '!*';
736 }
737
738 if ( in_array( 'thumbsize', $forOptions ) ) {
739 $confstr .= '!' . $this->mThumbSize;
740 } else {
741 $confstr .= '!*';
742 }
743
744 // add in language specific options, if any
745 // @todo FIXME: This is just a way of retrieving the url/user preferred variant
746 if ( !is_null( $title ) ) {
747 $confstr .= $title->getPageLanguage()->getExtraHashOptions();
748 } else {
749 global $wgContLang;
750 $confstr .= $wgContLang->getExtraHashOptions();
751 }
752
753 $confstr .= $wgRenderHashAppend;
754
755 if ( !in_array( 'editsection', $forOptions ) ) {
756 $confstr .= '!*';
757 } elseif ( !$this->mEditSection ) {
758 $confstr .= '!edit=0';
759 }
760
761 if ( $this->mIsPrintable && in_array( 'printable', $forOptions ) ) {
762 $confstr .= '!printable=1';
763 }
764
765 if ( $this->mExtraKey != '' ) {
766 $confstr .= $this->mExtraKey;
767 }
768
769 // Give a chance for extensions to modify the hash, if they have
770 // extra options or other effects on the parser cache.
771 wfRunHooks( 'PageRenderingHash', array( &$confstr, $this->getUser(), &$forOptions ) );
772
773 // Make it a valid memcached key fragment
774 $confstr = str_replace( ' ', '_', $confstr );
775
776 return $confstr;
777 }
778 }