2007-01-20 12:50:56 +00:00
|
|
|
<?php
|
2010-08-22 14:31:05 +00:00
|
|
|
/**
|
2012-04-30 09:22:16 +00:00
|
|
|
* Options for the PHP parser
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
2010-08-22 14:31:05 +00:00
|
|
|
*
|
|
|
|
|
* @file
|
|
|
|
|
* @ingroup Parser
|
|
|
|
|
*/
|
2018-06-11 06:55:11 +00:00
|
|
|
|
2024-10-03 18:39:06 +00:00
|
|
|
namespace MediaWiki\Parser;
|
|
|
|
|
|
|
|
|
|
use InvalidArgumentException;
|
|
|
|
|
use LogicException;
|
|
|
|
|
use MediaWiki\Content\Content;
|
2024-02-08 14:56:54 +00:00
|
|
|
use MediaWiki\Context\IContextSource;
|
2023-05-06 20:01:10 +00:00
|
|
|
use MediaWiki\HookContainer\HookRunner;
|
2024-09-27 18:13:02 +00:00
|
|
|
use MediaWiki\Language\Language;
|
2022-04-26 15:48:03 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
2018-06-11 06:55:11 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2020-06-10 08:40:05 +00:00
|
|
|
use MediaWiki\Revision\MutableRevisionRecord;
|
|
|
|
|
use MediaWiki\Revision\SlotRecord;
|
2022-10-25 16:58:49 +00:00
|
|
|
use MediaWiki\StubObject\StubObject;
|
2023-03-01 20:33:26 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2021-03-23 14:26:18 +00:00
|
|
|
use MediaWiki\User\UserIdentity;
|
2024-09-04 17:33:26 +00:00
|
|
|
use MediaWiki\User\UserIdentityValue;
|
2023-08-19 03:35:06 +00:00
|
|
|
use MediaWiki\Utils\MWTimestamp;
|
2024-10-03 18:39:06 +00:00
|
|
|
use ReflectionClass;
|
2024-09-04 17:33:26 +00:00
|
|
|
use Wikimedia\IPUtils;
|
2016-10-12 05:36:03 +00:00
|
|
|
use Wikimedia\ScopedCallback;
|
2010-12-11 03:52:35 +00:00
|
|
|
|
2007-01-20 12:50:56 +00:00
|
|
|
/**
|
2014-07-24 17:43:25 +00:00
|
|
|
* @brief Set options of the Parser
|
2012-01-27 22:07:09 +00:00
|
|
|
*
|
2017-05-12 21:38:12 +00:00
|
|
|
* How to add an option in core:
|
|
|
|
|
* 1. Add it to one of the arrays in ParserOptions::setDefaults()
|
|
|
|
|
* 2. If necessary, add an entry to ParserOptions::$inCacheKey
|
|
|
|
|
* 3. Add a getter and setter in the section for that.
|
|
|
|
|
*
|
|
|
|
|
* How to add an option in an extension:
|
|
|
|
|
* 1. Use the 'ParserOptionsRegister' hook to register it.
|
|
|
|
|
* 2. Where necessary, use $popt->getOption() and $popt->setOption()
|
|
|
|
|
* to access it.
|
2012-01-27 22:07:09 +00:00
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup Parser
|
2007-01-20 12:50:56 +00:00
|
|
|
*/
|
2010-01-23 15:24:44 +00:00
|
|
|
class ParserOptions {
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Default values for all options that are relevant for caching.
|
|
|
|
|
* @see self::getDefaults()
|
|
|
|
|
* @var array|null
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
private static $defaults = null;
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Lazy-loaded options
|
2021-03-01 05:20:42 +00:00
|
|
|
* @var callable[]|null
|
|
|
|
|
*/
|
|
|
|
|
private static $lazyOptions = null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initial lazy-loaded options (before hook)
|
2018-12-01 09:02:48 +00:00
|
|
|
* @var callable[]
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2021-03-01 05:20:42 +00:00
|
|
|
private static $initialLazyOptions = [
|
2017-05-12 21:38:12 +00:00
|
|
|
'dateformat' => [ __CLASS__, 'initDateFormat' ],
|
2018-08-07 16:52:40 +00:00
|
|
|
'speculativeRevId' => [ __CLASS__, 'initSpeculativeRevId' ],
|
2019-06-27 04:30:35 +00:00
|
|
|
'speculativePageId' => [ __CLASS__, 'initSpeculativePageId' ],
|
2017-05-12 21:38:12 +00:00
|
|
|
];
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Specify options that are included in the cache key
|
2021-03-01 05:20:42 +00:00
|
|
|
* @var array|null
|
|
|
|
|
*/
|
|
|
|
|
private static $cacheVaryingOptionsHash = null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initial inCacheKey options (before hook)
|
2017-05-12 21:38:12 +00:00
|
|
|
* @var array
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2021-03-01 05:20:42 +00:00
|
|
|
private static $initialCacheVaryingOptionsHash = [
|
2017-05-12 21:38:12 +00:00
|
|
|
'dateformat' => true,
|
|
|
|
|
'thumbsize' => true,
|
|
|
|
|
'printable' => true,
|
|
|
|
|
'userlang' => true,
|
2022-05-27 16:38:32 +00:00
|
|
|
'useParsoid' => true,
|
2024-04-05 17:25:28 +00:00
|
|
|
'suppressSectionEditLinks' => true,
|
2024-04-05 22:46:57 +00:00
|
|
|
'collapsibleSections' => true,
|
2017-05-12 21:38:12 +00:00
|
|
|
];
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2020-09-30 19:25:29 +00:00
|
|
|
/**
|
|
|
|
|
* Specify pseudo-options that are actually callbacks.
|
|
|
|
|
* These must be ignored when checking for cacheability.
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
private static $callbacks = [
|
|
|
|
|
'currentRevisionRecordCallback' => true,
|
|
|
|
|
'templateCallback' => true,
|
|
|
|
|
'speculativeRevIdCallback' => true,
|
|
|
|
|
'speculativePageIdCallback' => true,
|
|
|
|
|
];
|
|
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Current values for all options that are relevant for caching.
|
|
|
|
|
* @var array
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
private $options;
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Timestamp used for {{CURRENTDAY}} etc.
|
|
|
|
|
* @var string|null
|
|
|
|
|
* @note Caching based on parse time is handled externally
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
private $mTimestamp;
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Stored user object
|
2021-03-23 14:26:18 +00:00
|
|
|
* @var UserIdentity
|
2021-11-22 13:35:17 +00:00
|
|
|
* @todo Track this for caching somehow without fragmenting the cache
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
private $mUser;
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Function to be called when an option is accessed.
|
|
|
|
|
* @var callable|null
|
|
|
|
|
* @note Used for collecting used options, does not affect caching
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
private $onAccessCallback = null;
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* If the page being parsed is a redirect, this should hold the redirect
|
|
|
|
|
* target.
|
|
|
|
|
* @var Title|null
|
|
|
|
|
* @todo Track this for caching somehow
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
private $redirectTarget = null;
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2012-01-27 22:07:09 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Appended to the options hash
|
2024-09-07 20:46:08 +00:00
|
|
|
* @var string
|
2012-01-27 22:07:09 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
private $mExtraKey = '';
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2022-11-05 20:49:52 +00:00
|
|
|
/**
|
|
|
|
|
* The reason for rendering the content.
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
|
|
|
|
private $renderReason = 'unknown';
|
|
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2018-10-26 22:42:26 +00:00
|
|
|
* Fetch an option and track that is was accessed
|
2017-05-12 21:38:12 +00:00
|
|
|
* @since 1.30
|
|
|
|
|
* @param string $name Option name
|
|
|
|
|
* @return mixed
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getOption( $name ) {
|
|
|
|
|
if ( !array_key_exists( $name, $this->options ) ) {
|
|
|
|
|
throw new InvalidArgumentException( "Unknown parser option $name" );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2018-10-26 22:42:26 +00:00
|
|
|
$this->lazyLoadOption( $name );
|
2020-12-03 03:38:25 +00:00
|
|
|
$this->optionUsed( $name );
|
2017-05-12 21:38:12 +00:00
|
|
|
return $this->options[$name];
|
|
|
|
|
}
|
2012-09-15 21:51:58 +00:00
|
|
|
|
2018-10-26 22:42:26 +00:00
|
|
|
/**
|
|
|
|
|
* @param string $name Lazy load option without tracking usage
|
|
|
|
|
*/
|
|
|
|
|
private function lazyLoadOption( $name ) {
|
2021-03-01 05:20:42 +00:00
|
|
|
$lazyOptions = self::getLazyOptions();
|
|
|
|
|
if ( isset( $lazyOptions[$name] ) && $this->options[$name] === null ) {
|
|
|
|
|
$this->options[$name] = call_user_func( $lazyOptions[$name], $this, $name );
|
2018-10-26 22:42:26 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-10 15:43:29 +00:00
|
|
|
/**
|
|
|
|
|
* Resets lazy loaded options to null in the provided $options array
|
|
|
|
|
* @param array $options
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
private function nullifyLazyOption( array $options ): array {
|
|
|
|
|
return array_fill_keys( array_keys( self::getLazyOptions() ), null ) + $options;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-01 05:20:42 +00:00
|
|
|
/**
|
|
|
|
|
* Get lazy-loaded options.
|
|
|
|
|
*
|
|
|
|
|
* This array should be initialised by the constructor. The return type
|
|
|
|
|
* hint is used as an assertion to ensure this has happened and to coerce
|
|
|
|
|
* the type for static analysis.
|
|
|
|
|
*
|
|
|
|
|
* @internal Public for testing only
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public static function getLazyOptions(): array {
|
2021-08-10 14:32:03 +00:00
|
|
|
// Trigger a call to the 'ParserOptionsRegister' hook if it hasn't
|
|
|
|
|
// already been called.
|
|
|
|
|
if ( self::$lazyOptions === null ) {
|
|
|
|
|
self::getDefaults();
|
|
|
|
|
}
|
2021-03-01 05:20:42 +00:00
|
|
|
return self::$lazyOptions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get cache varying options, with the name of the option in the key, and a
|
|
|
|
|
* boolean in the value which indicates whether the cache is indeed varied.
|
|
|
|
|
*
|
|
|
|
|
* @see self::allCacheVaryingOptions()
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
private static function getCacheVaryingOptionsHash(): array {
|
|
|
|
|
// Trigger a call to the 'ParserOptionsRegister' hook if it hasn't
|
|
|
|
|
// already been called.
|
|
|
|
|
if ( self::$cacheVaryingOptionsHash === null ) {
|
|
|
|
|
self::getDefaults();
|
|
|
|
|
}
|
|
|
|
|
return self::$cacheVaryingOptionsHash;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Set an option, generically
|
|
|
|
|
* @since 1.30
|
|
|
|
|
* @param string $name Option name
|
|
|
|
|
* @param mixed $value New value. Passing null will set null, unlike many
|
|
|
|
|
* of the existing accessors which ignore null for historical reasons.
|
|
|
|
|
* @return mixed Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setOption( $name, $value ) {
|
|
|
|
|
if ( !array_key_exists( $name, $this->options ) ) {
|
|
|
|
|
throw new InvalidArgumentException( "Unknown parser option $name" );
|
|
|
|
|
}
|
|
|
|
|
$old = $this->options[$name];
|
|
|
|
|
$this->options[$name] = $value;
|
|
|
|
|
return $old;
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Legacy implementation
|
|
|
|
|
* @since 1.30 For implementing legacy setters only. Don't use this in new code.
|
|
|
|
|
* @deprecated since 1.30
|
|
|
|
|
* @param string $name Option name
|
|
|
|
|
* @param mixed $value New value. Passing null does not set the value.
|
|
|
|
|
* @return mixed Old value
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
protected function setOptionLegacy( $name, $value ) {
|
|
|
|
|
if ( !array_key_exists( $name, $this->options ) ) {
|
|
|
|
|
throw new InvalidArgumentException( "Unknown parser option $name" );
|
|
|
|
|
}
|
|
|
|
|
return wfSetVar( $this->options[$name], $value );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Whether to extract interlanguage links
|
|
|
|
|
*
|
|
|
|
|
* When true, interlanguage links will be returned by
|
|
|
|
|
* ParserOutput::getLanguageLinks() instead of generating link HTML.
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getInterwikiMagic() {
|
|
|
|
|
return $this->getOption( 'interwikiMagic' );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Specify whether to extract interlanguage links
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setInterwikiMagic( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'interwikiMagic', $x );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Allow all external images inline?
|
|
|
|
|
* @return bool
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getAllowExternalImages() {
|
|
|
|
|
return $this->getOption( 'allowExternalImages' );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* External images to allow
|
|
|
|
|
*
|
|
|
|
|
* When self::getAllowExternalImages() is false
|
|
|
|
|
*
|
|
|
|
|
* @return string|string[] URLs to allow
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getAllowExternalImagesFrom() {
|
|
|
|
|
return $this->getOption( 'allowExternalImagesFrom' );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Use the on-wiki external image whitelist?
|
|
|
|
|
* @return bool
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getEnableImageWhitelist() {
|
|
|
|
|
return $this->getOption( 'enableImageWhitelist' );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Allow inclusion of special pages?
|
|
|
|
|
* @return bool
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getAllowSpecialInclusion() {
|
|
|
|
|
return $this->getOption( 'allowSpecialInclusion' );
|
|
|
|
|
}
|
2012-06-10 15:35:15 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Allow inclusion of special pages?
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setAllowSpecialInclusion( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'allowSpecialInclusion', $x );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Parsing an interface message?
|
|
|
|
|
* @return bool
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getInterfaceMessage() {
|
|
|
|
|
return $this->getOption( 'interfaceMessage' );
|
|
|
|
|
}
|
Revert r38196, r38204 -- "(bugs 6089, 13079) Show edit section links for transcluded template if, and only if the user can edit it, made Title::getUserPermissionsErrorsInternal() public so that it can be used in Parser and it can pass the User object from ParserOptions. " & co
Cause regression in 19 parser test cases, looks like messing up the tooltips for section edit links.
19 previously failing test(s) now PASSING! :)
* Bug 6563: Edit link generation for section shown by <includeonly> [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Bug 6563: Edit link generation for section suppressed by <includeonly> [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Basic section headings [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Section headings with TOC [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Handling of sections up to level 6 and beyond [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* TOC regression (bug 9764) [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* TOC with wgMaxTocLevel=3 (bug 6204) [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Resolving duplicate section names [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Resolving duplicate section names with differing case (bug 10721) [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Template with sections, __NOTOC__ [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Link inside a section heading [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* TOC regression (bug 12077) [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Fuzz testing: Parser14 [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Fuzz testing: Parser14-table [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Inclusion of !userCanEdit() content [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Out-of-order TOC heading levels [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* -{}- tags within headlines (within html for parserConvert()) [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* Morwen/13: Unclosed link followed by heading [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
* HHP2.2: Heuristics for headings in preprocessor parenthetical structures [Fixed between 29-Jul-2008 22:42:06, 1.14alpha (r38207) and 29-Jul-2008 23:54:51, 1.14alpha (r38207)]
2008-07-29 23:56:30 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Parsing an interface message?
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setInterfaceMessage( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'interfaceMessage', $x );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Target language for the parse
|
|
|
|
|
* @return Language|null
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getTargetLanguage() {
|
|
|
|
|
return $this->getOption( 'targetLanguage' );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Target language for the parse
|
|
|
|
|
* @param Language|null $x New value
|
|
|
|
|
* @return Language|null Old value
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setTargetLanguage( $x ) {
|
|
|
|
|
return $this->setOption( 'targetLanguage', $x );
|
|
|
|
|
}
|
2012-10-10 18:13:40 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Maximum size of template expansions, in bytes
|
|
|
|
|
* @return int
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getMaxIncludeSize() {
|
|
|
|
|
return $this->getOption( 'maxIncludeSize' );
|
|
|
|
|
}
|
2010-12-11 03:52:35 +00:00
|
|
|
|
2014-05-16 00:48:01 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Maximum size of template expansions, in bytes
|
|
|
|
|
* @param int|null $x New value (null is no change)
|
|
|
|
|
* @return int Old value
|
2014-05-16 00:48:01 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setMaxIncludeSize( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'maxIncludeSize', $x );
|
|
|
|
|
}
|
2014-05-16 00:48:01 +00:00
|
|
|
|
2016-09-09 07:28:49 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Maximum number of nodes touched by PPFrame::expand()
|
|
|
|
|
* @return int
|
2016-09-09 07:28:49 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getMaxPPNodeCount() {
|
|
|
|
|
return $this->getOption( 'maxPPNodeCount' );
|
|
|
|
|
}
|
2016-09-09 07:28:49 +00:00
|
|
|
|
|
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Maximum number of nodes touched by PPFrame::expand()
|
|
|
|
|
* @param int|null $x New value (null is no change)
|
|
|
|
|
* @return int Old value
|
2016-09-09 07:28:49 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setMaxPPNodeCount( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'maxPPNodeCount', $x );
|
|
|
|
|
}
|
2016-09-09 07:28:49 +00:00
|
|
|
|
2014-05-02 20:16:51 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Maximum recursion depth in PPFrame::expand()
|
|
|
|
|
* @return int
|
2014-05-02 20:16:51 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getMaxPPExpandDepth() {
|
|
|
|
|
return $this->getOption( 'maxPPExpandDepth' );
|
|
|
|
|
}
|
2014-05-02 20:16:51 +00:00
|
|
|
|
2017-02-06 03:00:39 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Maximum recursion depth for templates within templates
|
|
|
|
|
* @return int
|
2022-09-28 19:22:10 +00:00
|
|
|
* @internal Only used by Parser (T318826)
|
2017-02-06 03:00:39 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getMaxTemplateDepth() {
|
|
|
|
|
return $this->getOption( 'maxTemplateDepth' );
|
|
|
|
|
}
|
2017-02-06 03:00:39 +00:00
|
|
|
|
2017-04-27 16:58:17 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Maximum recursion depth for templates within templates
|
|
|
|
|
* @param int|null $x New value (null is no change)
|
|
|
|
|
* @return int Old value
|
2022-09-28 19:22:10 +00:00
|
|
|
* @internal Only used by ParserTestRunner (T318826)
|
2017-04-27 16:58:17 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setMaxTemplateDepth( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'maxTemplateDepth', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Maximum number of calls per parse to expensive parser functions
|
|
|
|
|
* @since 1.20
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public function getExpensiveParserFunctionLimit() {
|
|
|
|
|
return $this->getOption( 'expensiveParserFunctionLimit' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Maximum number of calls per parse to expensive parser functions
|
|
|
|
|
* @since 1.20
|
|
|
|
|
* @param int|null $x New value (null is no change)
|
|
|
|
|
* @return int Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setExpensiveParserFunctionLimit( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'expensiveParserFunctionLimit', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Remove HTML comments
|
|
|
|
|
* @warning Only applies to preprocess operations
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getRemoveComments() {
|
|
|
|
|
return $this->getOption( 'removeComments' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Remove HTML comments
|
|
|
|
|
* @warning Only applies to preprocess operations
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setRemoveComments( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'removeComments', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
2021-11-09 16:31:27 +00:00
|
|
|
* @deprecated since 1.38. This does nothing now, to control limit reporting
|
|
|
|
|
* please provide 'includeDebugInfo' option to ParserOutput::getText.
|
|
|
|
|
*
|
2017-05-12 21:38:12 +00:00
|
|
|
* Enable limit report in an HTML comment on output
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getEnableLimitReport() {
|
2021-11-09 16:31:27 +00:00
|
|
|
return false;
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
2021-11-09 16:31:27 +00:00
|
|
|
* @deprecated since 1.38. This does nothing now, to control limit reporting
|
|
|
|
|
* please provide 'includeDebugInfo' option to ParserOutput::getText.
|
|
|
|
|
*
|
2017-05-12 21:38:12 +00:00
|
|
|
* Enable limit report in an HTML comment on output
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function enableLimitReport( $x = true ) {
|
2021-11-09 16:31:27 +00:00
|
|
|
return false;
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Clean up signature texts?
|
|
|
|
|
* @see Parser::cleanSig
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getCleanSignatures() {
|
|
|
|
|
return $this->getOption( 'cleanSignatures' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Clean up signature texts?
|
|
|
|
|
* @see Parser::cleanSig
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setCleanSignatures( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'cleanSignatures', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Target attribute for external links
|
2022-03-03 21:33:45 +00:00
|
|
|
* @return string|false
|
2022-09-13 15:08:58 +00:00
|
|
|
* @internal Only set by installer (T317647)
|
2017-05-12 21:38:12 +00:00
|
|
|
*/
|
|
|
|
|
public function getExternalLinkTarget() {
|
|
|
|
|
return $this->getOption( 'externalLinkTarget' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Target attribute for external links
|
2022-03-03 21:33:45 +00:00
|
|
|
* @param string|false|null $x New value (null is no change)
|
2017-05-12 21:38:12 +00:00
|
|
|
* @return string Old value
|
2022-09-13 15:08:58 +00:00
|
|
|
* @internal Only used by installer (T317647)
|
2017-05-12 21:38:12 +00:00
|
|
|
*/
|
|
|
|
|
public function setExternalLinkTarget( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'externalLinkTarget', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Whether content conversion should be disabled
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getDisableContentConversion() {
|
|
|
|
|
return $this->getOption( 'disableContentConversion' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Whether content conversion should be disabled
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function disableContentConversion( $x = true ) {
|
|
|
|
|
return $this->setOptionLegacy( 'disableContentConversion', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Whether title conversion should be disabled
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getDisableTitleConversion() {
|
|
|
|
|
return $this->getOption( 'disableTitleConversion' );
|
2014-09-16 00:07:52 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Whether title conversion should be disabled
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function disableTitleConversion( $x = true ) {
|
|
|
|
|
return $this->setOptionLegacy( 'disableTitleConversion', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Thumb size preferred by the user.
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public function getThumbSize() {
|
|
|
|
|
return $this->getOption( 'thumbsize' );
|
2016-06-19 05:30:21 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Thumb size preferred by the user.
|
|
|
|
|
* @param int|null $x New value (null is no change)
|
|
|
|
|
* @return int Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setThumbSize( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'thumbsize', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Parsing the page for a "preview" operation?
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getIsPreview() {
|
|
|
|
|
return $this->getOption( 'isPreview' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Parsing the page for a "preview" operation?
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setIsPreview( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'isPreview', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Parsing the page for a "preview" operation on a single section?
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getIsSectionPreview() {
|
|
|
|
|
return $this->getOption( 'isSectionPreview' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Parsing the page for a "preview" operation on a single section?
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setIsSectionPreview( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'isSectionPreview', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Parsing the printable version of the page?
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getIsPrintable() {
|
|
|
|
|
return $this->getOption( 'printable' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Parsing the printable version of the page?
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setIsPrintable( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'printable', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Transform wiki markup when saving the page?
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getPreSaveTransform() {
|
|
|
|
|
return $this->getOption( 'preSaveTransform' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Transform wiki markup when saving the page?
|
|
|
|
|
* @param bool|null $x New value (null is no change)
|
|
|
|
|
* @return bool Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setPreSaveTransform( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'preSaveTransform', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2022-05-27 16:38:32 +00:00
|
|
|
/**
|
|
|
|
|
* Parsoid-format HTML output, or legacy wikitext parser HTML?
|
|
|
|
|
* @see T300191
|
|
|
|
|
* @unstable
|
|
|
|
|
* @since 1.41
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getUseParsoid(): bool {
|
|
|
|
|
return $this->getOption( 'useParsoid' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Request Parsoid-format HTML output.
|
|
|
|
|
* @see T300191
|
|
|
|
|
* @unstable
|
|
|
|
|
* @since 1.41
|
|
|
|
|
*/
|
|
|
|
|
public function setUseParsoid() {
|
|
|
|
|
$this->setOption( 'useParsoid', true );
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Date format index
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function getDateFormat() {
|
|
|
|
|
return $this->getOption( 'dateformat' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
2009-07-07 16:13:58 +00:00
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Lazy initializer for dateFormat
|
2018-12-03 14:58:57 +00:00
|
|
|
* @param ParserOptions $popt
|
|
|
|
|
* @return string
|
2017-05-12 21:38:12 +00:00
|
|
|
*/
|
2018-12-03 14:58:57 +00:00
|
|
|
private static function initDateFormat( ParserOptions $popt ) {
|
2021-07-28 14:08:59 +00:00
|
|
|
$userFactory = MediaWikiServices::getInstance()->getUserFactory();
|
|
|
|
|
return $userFactory->newFromUserIdentity( $popt->getUserIdentity() )->getDatePreference();
|
2007-01-20 12:50:56 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Date format index
|
|
|
|
|
* @param string|null $x New value (null is no change)
|
|
|
|
|
* @return string Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setDateFormat( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'dateformat', $x );
|
2008-01-24 04:29:56 +00:00
|
|
|
}
|
|
|
|
|
|
2010-09-29 17:25:17 +00:00
|
|
|
/**
|
2015-08-26 16:17:05 +00:00
|
|
|
* Get the user language used by the parser for this page and split the parser cache.
|
2013-08-01 20:37:41 +00:00
|
|
|
*
|
2018-07-26 16:31:49 +00:00
|
|
|
* @warning Calling this causes the parser cache to be fragmented by user language!
|
2015-08-26 16:17:05 +00:00
|
|
|
* To avoid cache fragmentation, output should not depend on the user language.
|
2022-09-28 19:52:56 +00:00
|
|
|
* Use Parser::getTargetLanguage() instead!
|
2013-08-01 20:37:41 +00:00
|
|
|
*
|
2015-08-26 16:17:05 +00:00
|
|
|
* @note This function will trigger a cache fragmentation by recording the
|
|
|
|
|
* 'userlang' option, see optionUsed(). This is done to avoid cache pollution
|
|
|
|
|
* when the page is rendered based on the language of the user.
|
2013-08-01 20:37:41 +00:00
|
|
|
*
|
2015-08-26 16:17:05 +00:00
|
|
|
* @note When saving, this will return the default language instead of the user's.
|
2017-02-20 22:44:19 +00:00
|
|
|
* {{int: }} uses this which used to produce inconsistent link tables (T16404).
|
2011-10-19 14:16:01 +00:00
|
|
|
*
|
2014-04-21 23:38:39 +00:00
|
|
|
* @return Language
|
2011-10-19 14:16:01 +00:00
|
|
|
* @since 1.19
|
|
|
|
|
*/
|
2014-08-11 20:24:54 +00:00
|
|
|
public function getUserLangObj() {
|
2017-05-12 21:38:12 +00:00
|
|
|
return $this->getOption( 'userlang' );
|
2011-10-19 14:16:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Same as getUserLangObj() but returns a string instead.
|
|
|
|
|
*
|
2018-07-26 16:31:49 +00:00
|
|
|
* @warning Calling this causes the parser cache to be fragmented by user language!
|
2015-08-26 16:17:05 +00:00
|
|
|
* To avoid cache fragmentation, output should not depend on the user language.
|
2022-09-28 19:52:56 +00:00
|
|
|
* Use Parser::getTargetLanguage() instead!
|
2015-08-26 16:17:05 +00:00
|
|
|
*
|
|
|
|
|
* @see getUserLangObj()
|
|
|
|
|
*
|
2014-04-21 23:38:39 +00:00
|
|
|
* @return string Language code
|
2011-06-13 07:47:39 +00:00
|
|
|
* @since 1.17
|
2010-09-29 17:25:17 +00:00
|
|
|
*/
|
2014-08-11 20:24:54 +00:00
|
|
|
public function getUserLang() {
|
2011-10-19 14:16:01 +00:00
|
|
|
return $this->getUserLangObj()->getCode();
|
2010-08-05 18:43:49 +00:00
|
|
|
}
|
|
|
|
|
|
2016-09-09 07:28:49 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Set the user language used by the parser for this page and split the parser cache.
|
|
|
|
|
* @param string|Language $x New value
|
|
|
|
|
* @return Language Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setUserLang( $x ) {
|
|
|
|
|
if ( is_string( $x ) ) {
|
2019-08-26 12:24:37 +00:00
|
|
|
$x = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( $x );
|
2017-05-12 21:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->setOptionLegacy( 'userlang', $x );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Are magic ISBN links enabled?
|
2016-09-09 07:28:49 +00:00
|
|
|
* @since 1.28
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getMagicISBNLinks() {
|
2017-05-12 21:38:12 +00:00
|
|
|
return $this->getOption( 'magicISBNLinks' );
|
2016-09-09 07:28:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Are magic PMID links enabled?
|
2016-09-09 07:28:49 +00:00
|
|
|
* @since 1.28
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getMagicPMIDLinks() {
|
2017-05-12 21:38:12 +00:00
|
|
|
return $this->getOption( 'magicPMIDLinks' );
|
2016-09-09 07:28:49 +00:00
|
|
|
}
|
2019-01-15 15:04:58 +00:00
|
|
|
|
2016-09-09 07:28:49 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Are magic RFC links enabled?
|
2016-09-09 07:28:49 +00:00
|
|
|
* @since 1.28
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getMagicRFCLinks() {
|
|
|
|
|
return $this->getOption( 'magicRFCLinks' );
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 16:20:35 +00:00
|
|
|
/**
|
|
|
|
|
* Should the table of contents be suppressed?
|
|
|
|
|
* Used when parsing "code" pages (like JavaScript) as wikitext
|
|
|
|
|
* for backlink support and categories, but where we don't want
|
|
|
|
|
* other metadata generated (like the table of contents).
|
|
|
|
|
* @see T307691
|
|
|
|
|
* @since 1.39
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getSuppressTOC() {
|
|
|
|
|
return $this->getOption( 'suppressTOC' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Suppress generation of the table of contents.
|
|
|
|
|
* Used when parsing "code" pages (like JavaScript) as wikitext
|
|
|
|
|
* for backlink support and categories, but where we don't want
|
|
|
|
|
* other metadata generated (like the table of contents).
|
|
|
|
|
* @see T307691
|
|
|
|
|
* @since 1.39
|
2023-11-15 18:05:51 +00:00
|
|
|
* @deprecated since 1.42; just clear the metadata in the final
|
|
|
|
|
* parser output
|
2022-05-27 16:20:35 +00:00
|
|
|
*/
|
|
|
|
|
public function setSuppressTOC() {
|
2023-11-15 18:05:51 +00:00
|
|
|
wfDeprecated( __METHOD__, '1.42' );
|
2022-05-27 16:20:35 +00:00
|
|
|
$this->setOption( 'suppressTOC', true );
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-06 20:43:31 +00:00
|
|
|
/**
|
|
|
|
|
* Should section edit links be suppressed?
|
|
|
|
|
* Used when parsing wikitext which will be presented in a
|
|
|
|
|
* non-interactive context: previews, UX text, etc.
|
|
|
|
|
* @since 1.42
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getSuppressSectionEditLinks() {
|
|
|
|
|
return $this->getOption( 'suppressSectionEditLinks' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Suppress section edit links in the output.
|
|
|
|
|
* Used when parsing wikitext which will be presented in a
|
|
|
|
|
* non-interactive context: previews, UX text, etc.
|
|
|
|
|
* @since 1.42
|
|
|
|
|
*/
|
|
|
|
|
public function setSuppressSectionEditLinks() {
|
|
|
|
|
$this->setOption( 'suppressSectionEditLinks', true );
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 22:46:57 +00:00
|
|
|
/**
|
|
|
|
|
* Should section contents be wrapped in <div> to make them
|
|
|
|
|
* collapsible?
|
|
|
|
|
* @since 1.42
|
|
|
|
|
*/
|
|
|
|
|
public function getCollapsibleSections(): bool {
|
|
|
|
|
return $this->getOption( 'collapsibleSections' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Wrap section contents in a <div> to allow client-side code
|
|
|
|
|
* to collapse them.
|
|
|
|
|
* @since 1.42
|
|
|
|
|
*/
|
|
|
|
|
public function setCollapsibleSections(): void {
|
|
|
|
|
$this->setOption( 'collapsibleSections', true );
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* If the wiki is configured to allow raw html ($wgRawHtml = true)
|
|
|
|
|
* is it allowed in the specific case of parsing this page.
|
|
|
|
|
*
|
|
|
|
|
* This is meant to disable unsafe parser tags in cases where
|
|
|
|
|
* a malicious user may control the input to the parser.
|
|
|
|
|
*
|
|
|
|
|
* @note This is expected to be true for normal pages even if the
|
|
|
|
|
* wiki has $wgRawHtml disabled in general. The setting only
|
|
|
|
|
* signifies that raw html would be unsafe in the current context
|
|
|
|
|
* provided that raw html is allowed at all.
|
|
|
|
|
* @since 1.29
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getAllowUnsafeRawHtml() {
|
|
|
|
|
return $this->getOption( 'allowUnsafeRawHtml' );
|
2016-09-09 07:28:49 +00:00
|
|
|
}
|
2017-02-06 03:00:39 +00:00
|
|
|
|
|
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* If the wiki is configured to allow raw html ($wgRawHtml = true)
|
|
|
|
|
* is it allowed in the specific case of parsing this page.
|
|
|
|
|
* @see self::getAllowUnsafeRawHtml()
|
2017-02-06 03:00:39 +00:00
|
|
|
* @since 1.29
|
2017-08-11 16:09:41 +00:00
|
|
|
* @param bool|null $x Value to set or null to get current value
|
2017-05-12 21:38:12 +00:00
|
|
|
* @return bool Current value for allowUnsafeRawHtml
|
2017-02-06 03:00:39 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setAllowUnsafeRawHtml( $x ) {
|
|
|
|
|
return $this->setOptionLegacy( 'allowUnsafeRawHtml', $x );
|
2017-02-06 03:00:39 +00:00
|
|
|
}
|
|
|
|
|
|
2017-04-27 16:58:17 +00:00
|
|
|
/**
|
|
|
|
|
* Class to use to wrap output from Parser::parse()
|
|
|
|
|
* @since 1.30
|
2022-07-31 00:02:18 +00:00
|
|
|
* @return string|false
|
2017-04-27 16:58:17 +00:00
|
|
|
*/
|
|
|
|
|
public function getWrapOutputClass() {
|
2017-05-12 21:38:12 +00:00
|
|
|
return $this->getOption( 'wrapclass' );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* CSS class to use to wrap output from Parser::parse()
|
|
|
|
|
* @since 1.30
|
2017-12-22 18:47:33 +00:00
|
|
|
* @param string $className Class name to use for wrapping.
|
|
|
|
|
* Passing false to indicate "no wrapping" was deprecated in MediaWiki 1.31.
|
2022-07-31 00:02:18 +00:00
|
|
|
* @return string|false Current value
|
2017-05-12 21:38:12 +00:00
|
|
|
*/
|
|
|
|
|
public function setWrapOutputClass( $className ) {
|
|
|
|
|
if ( $className === true ) { // DWIM, they probably want the default class name
|
|
|
|
|
$className = 'mw-parser-output';
|
|
|
|
|
}
|
2017-12-22 18:47:33 +00:00
|
|
|
if ( $className === false ) {
|
|
|
|
|
wfDeprecated( __METHOD__ . '( false )', '1.31' );
|
|
|
|
|
}
|
2017-05-12 21:38:12 +00:00
|
|
|
return $this->setOption( 'wrapclass', $className );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Callback for current revision fetching; first argument to call_user_func().
|
2020-04-09 03:36:39 +00:00
|
|
|
* @internal
|
|
|
|
|
* @since 1.35
|
|
|
|
|
* @return callable
|
|
|
|
|
*/
|
|
|
|
|
public function getCurrentRevisionRecordCallback() {
|
2021-04-06 03:09:28 +00:00
|
|
|
return $this->getOption( 'currentRevisionRecordCallback' );
|
2014-09-16 00:07:52 +00:00
|
|
|
}
|
|
|
|
|
|
2020-04-09 03:36:39 +00:00
|
|
|
/**
|
|
|
|
|
* Callback for current revision fetching; first argument to call_user_func().
|
|
|
|
|
* @internal
|
|
|
|
|
* @since 1.35
|
|
|
|
|
* @param callable|null $x New value
|
|
|
|
|
* @return callable Old value
|
|
|
|
|
*/
|
|
|
|
|
public function setCurrentRevisionRecordCallback( $x ) {
|
|
|
|
|
return $this->setOption( 'currentRevisionRecordCallback', $x );
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Callback for template fetching; first argument to call_user_func().
|
|
|
|
|
* @return callable
|
|
|
|
|
*/
|
|
|
|
|
public function getTemplateCallback() {
|
|
|
|
|
return $this->getOption( 'templateCallback' );
|
2016-06-19 05:30:21 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Callback for template fetching; first argument to call_user_func().
|
|
|
|
|
* @param callable|null $x New value (null is no change)
|
|
|
|
|
* @return callable Old value
|
|
|
|
|
*/
|
2014-08-11 20:24:54 +00:00
|
|
|
public function setTemplateCallback( $x ) {
|
2017-05-12 21:38:12 +00:00
|
|
|
return $this->setOptionLegacy( 'templateCallback', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2018-08-07 16:52:40 +00:00
|
|
|
/**
|
|
|
|
|
* A guess for {{REVISIONID}}, calculated using the callback provided via
|
|
|
|
|
* setSpeculativeRevIdCallback(). For consistency, the value will be calculated upon the
|
|
|
|
|
* first call of this method, and re-used for subsequent calls.
|
|
|
|
|
*
|
|
|
|
|
* If no callback was defined via setSpeculativeRevIdCallback(), this method will return false.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.32
|
|
|
|
|
* @return int|false
|
|
|
|
|
*/
|
|
|
|
|
public function getSpeculativeRevId() {
|
|
|
|
|
return $this->getOption( 'speculativeRevId' );
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-27 04:30:35 +00:00
|
|
|
/**
|
|
|
|
|
* A guess for {{PAGEID}}, calculated using the callback provided via
|
|
|
|
|
* setSpeculativeRevPageCallback(). For consistency, the value will be calculated upon the
|
|
|
|
|
* first call of this method, and re-used for subsequent calls.
|
|
|
|
|
*
|
|
|
|
|
* If no callback was defined via setSpeculativePageIdCallback(), this method will return false.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.34
|
|
|
|
|
* @return int|false
|
|
|
|
|
*/
|
|
|
|
|
public function getSpeculativePageId() {
|
|
|
|
|
return $this->getOption( 'speculativePageId' );
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-07 16:52:40 +00:00
|
|
|
/**
|
|
|
|
|
* Callback registered with ParserOptions::$lazyOptions, triggered by getSpeculativeRevId().
|
|
|
|
|
*
|
|
|
|
|
* @param ParserOptions $popt
|
2019-07-26 06:39:16 +00:00
|
|
|
* @return int|false
|
2018-08-07 16:52:40 +00:00
|
|
|
*/
|
|
|
|
|
private static function initSpeculativeRevId( ParserOptions $popt ) {
|
|
|
|
|
$cb = $popt->getOption( 'speculativeRevIdCallback' );
|
|
|
|
|
$id = $cb ? $cb() : null;
|
|
|
|
|
|
|
|
|
|
// returning null would result in this being re-called every access
|
|
|
|
|
return $id ?? false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
2019-06-27 04:30:35 +00:00
|
|
|
* Callback registered with ParserOptions::$lazyOptions, triggered by getSpeculativePageId().
|
|
|
|
|
*
|
|
|
|
|
* @param ParserOptions $popt
|
2019-07-26 06:39:16 +00:00
|
|
|
* @return int|false
|
2017-05-12 21:38:12 +00:00
|
|
|
*/
|
2019-06-27 04:30:35 +00:00
|
|
|
private static function initSpeculativePageId( ParserOptions $popt ) {
|
|
|
|
|
$cb = $popt->getOption( 'speculativePageIdCallback' );
|
|
|
|
|
$id = $cb ? $cb() : null;
|
|
|
|
|
|
|
|
|
|
// returning null would result in this being re-called every access
|
|
|
|
|
return $id ?? false;
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Callback to generate a guess for {{REVISIONID}}
|
2019-06-27 04:30:35 +00:00
|
|
|
* @param callable|null $x New value
|
2017-05-12 21:38:12 +00:00
|
|
|
* @return callable|null Old value
|
2019-06-27 04:30:35 +00:00
|
|
|
* @since 1.28
|
2017-05-12 21:38:12 +00:00
|
|
|
*/
|
|
|
|
|
public function setSpeculativeRevIdCallback( $x ) {
|
2018-08-07 16:52:40 +00:00
|
|
|
$this->setOption( 'speculativeRevId', null ); // reset
|
2019-06-27 04:30:35 +00:00
|
|
|
return $this->setOption( 'speculativeRevIdCallback', $x );
|
2014-05-10 23:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
2019-06-27 04:30:35 +00:00
|
|
|
/**
|
|
|
|
|
* Callback to generate a guess for {{PAGEID}}
|
|
|
|
|
* @param callable|null $x New value
|
|
|
|
|
* @return callable|null Old value
|
|
|
|
|
* @since 1.34
|
|
|
|
|
*/
|
|
|
|
|
public function setSpeculativePageIdCallback( $x ) {
|
|
|
|
|
$this->setOption( 'speculativePageId', null ); // reset
|
|
|
|
|
return $this->setOption( 'speculativePageIdCallback', $x );
|
|
|
|
|
}
|
2009-07-07 16:13:58 +00:00
|
|
|
|
2017-02-06 03:00:39 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Timestamp used for {{CURRENTDAY}} etc.
|
2019-04-19 01:36:40 +00:00
|
|
|
* @return string TS_MW timestamp
|
2017-02-06 03:00:39 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function getTimestamp() {
|
|
|
|
|
if ( !isset( $this->mTimestamp ) ) {
|
|
|
|
|
$this->mTimestamp = wfTimestampNow();
|
|
|
|
|
}
|
|
|
|
|
return $this->mTimestamp;
|
2017-02-06 03:00:39 +00:00
|
|
|
}
|
|
|
|
|
|
2017-04-27 16:58:17 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Timestamp used for {{CURRENTDAY}} etc.
|
|
|
|
|
* @param string|null $x New value (null is no change)
|
|
|
|
|
* @return string Old value
|
2017-04-27 16:58:17 +00:00
|
|
|
*/
|
2017-05-12 21:38:12 +00:00
|
|
|
public function setTimestamp( $x ) {
|
|
|
|
|
return wfSetVar( $this->mTimestamp, $x );
|
2017-04-27 16:58:17 +00:00
|
|
|
}
|
|
|
|
|
|
2014-05-02 20:16:51 +00:00
|
|
|
/**
|
|
|
|
|
* Note that setting or changing this does not *make* the page a redirect
|
|
|
|
|
* or change its target, it merely records the information for reference
|
|
|
|
|
* during the parse.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.24
|
|
|
|
|
* @param Title|null $title
|
|
|
|
|
*/
|
2020-05-16 21:42:24 +00:00
|
|
|
public function setRedirectTarget( $title ) {
|
2014-05-02 20:16:51 +00:00
|
|
|
$this->redirectTarget = $title;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the previously-set redirect target.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.24
|
|
|
|
|
* @return Title|null
|
|
|
|
|
*/
|
2020-05-16 21:42:24 +00:00
|
|
|
public function getRedirectTarget() {
|
2014-05-02 20:16:51 +00:00
|
|
|
return $this->redirectTarget;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-10 14:38:14 +00:00
|
|
|
/**
|
|
|
|
|
* Extra key that should be present in the parser cache key.
|
2017-05-12 21:38:12 +00:00
|
|
|
* @warning Consider registering your additional options with the
|
|
|
|
|
* ParserOptionsRegister hook instead of using this method.
|
2014-08-14 18:22:52 +00:00
|
|
|
* @param string $key
|
2010-08-10 14:38:14 +00:00
|
|
|
*/
|
2014-08-11 20:24:54 +00:00
|
|
|
public function addExtraKey( $key ) {
|
2010-08-10 14:38:14 +00:00
|
|
|
$this->mExtraKey .= '!' . $key;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-23 14:26:18 +00:00
|
|
|
/**
|
|
|
|
|
* Get the identity of the user for whom the parse is made.
|
|
|
|
|
* @since 1.36
|
|
|
|
|
* @return UserIdentity
|
|
|
|
|
*/
|
|
|
|
|
public function getUserIdentity(): UserIdentity {
|
2017-05-12 21:38:12 +00:00
|
|
|
return $this->mUser;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 22:07:09 +00:00
|
|
|
/**
|
2021-06-24 00:38:27 +00:00
|
|
|
* @param UserIdentity $user
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param Language|null $lang
|
2012-01-27 22:07:09 +00:00
|
|
|
*/
|
2021-06-24 00:38:27 +00:00
|
|
|
public function __construct( UserIdentity $user, $lang = null ) {
|
2011-10-19 14:16:01 +00:00
|
|
|
if ( $lang === null ) {
|
|
|
|
|
global $wgLang;
|
2020-06-14 22:29:36 +00:00
|
|
|
StubObject::unstub( $wgLang );
|
2011-10-19 14:16:01 +00:00
|
|
|
$lang = $wgLang;
|
|
|
|
|
}
|
|
|
|
|
$this->initialiseFromUser( $user, $lang );
|
2007-01-20 12:50:56 +00:00
|
|
|
}
|
|
|
|
|
|
2016-02-03 20:41:00 +00:00
|
|
|
/**
|
|
|
|
|
* Get a ParserOptions object for an anonymous user
|
2016-02-10 01:56:46 +00:00
|
|
|
* @since 1.27
|
2016-02-03 20:41:00 +00:00
|
|
|
* @return ParserOptions
|
|
|
|
|
*/
|
|
|
|
|
public static function newFromAnon() {
|
2024-01-22 20:10:42 +00:00
|
|
|
return new ParserOptions( MediaWikiServices::getInstance()->getUserFactory()->newAnonymous(),
|
2018-07-29 12:24:54 +00:00
|
|
|
MediaWikiServices::getInstance()->getContentLanguage() );
|
2016-02-03 20:41:00 +00:00
|
|
|
}
|
|
|
|
|
|
2007-01-20 12:50:56 +00:00
|
|
|
/**
|
2011-10-19 14:16:01 +00:00
|
|
|
* Get a ParserOptions object from a given user.
|
|
|
|
|
* Language will be taken from $wgLang.
|
2010-06-09 14:57:59 +00:00
|
|
|
*
|
2021-03-23 14:26:18 +00:00
|
|
|
* @param UserIdentity $user
|
2014-04-21 23:38:39 +00:00
|
|
|
* @return ParserOptions
|
2007-01-20 12:50:56 +00:00
|
|
|
*/
|
2011-10-19 14:16:01 +00:00
|
|
|
public static function newFromUser( $user ) {
|
2007-01-20 12:50:56 +00:00
|
|
|
return new ParserOptions( $user );
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-19 14:16:01 +00:00
|
|
|
/**
|
|
|
|
|
* Get a ParserOptions object from a given user and language
|
|
|
|
|
*
|
2021-03-23 14:26:18 +00:00
|
|
|
* @param UserIdentity $user
|
2014-04-21 23:38:39 +00:00
|
|
|
* @param Language $lang
|
|
|
|
|
* @return ParserOptions
|
2011-10-19 14:16:01 +00:00
|
|
|
*/
|
2021-03-23 14:26:18 +00:00
|
|
|
public static function newFromUserAndLang( UserIdentity $user, Language $lang ) {
|
2011-10-19 14:16:01 +00:00
|
|
|
return new ParserOptions( $user, $lang );
|
|
|
|
|
}
|
2010-01-23 15:24:44 +00:00
|
|
|
|
2011-10-19 14:16:01 +00:00
|
|
|
/**
|
|
|
|
|
* Get a ParserOptions object from a IContextSource object
|
|
|
|
|
*
|
2014-04-21 23:38:39 +00:00
|
|
|
* @param IContextSource $context
|
|
|
|
|
* @return ParserOptions
|
2011-10-19 14:16:01 +00:00
|
|
|
*/
|
|
|
|
|
public static function newFromContext( IContextSource $context ) {
|
2024-09-04 17:33:26 +00:00
|
|
|
$contextUser = $context->getUser();
|
|
|
|
|
|
|
|
|
|
// Use the stashed temporary account name instead of an IP address as the user for the ParserOptions
|
|
|
|
|
// (if a stashed name is set). This is so that magic words like {{REVISIONUSER}} show the temporary account
|
|
|
|
|
// name instead of IP address.
|
|
|
|
|
$tempUserCreator = MediaWikiServices::getInstance()->getTempUserCreator();
|
|
|
|
|
if ( $tempUserCreator->isEnabled() && IPUtils::isIPAddress( $contextUser->getName() ) ) {
|
|
|
|
|
// We do not attempt to acquire a temporary account name if no name is stashed, as this may be called in
|
|
|
|
|
// contexts (such as the parse API) where the user will not be performing an edit on their next action
|
|
|
|
|
// and therefore would be increasing the rate limit unnecessarily.
|
|
|
|
|
$tempName = $tempUserCreator->getStashedName( $context->getRequest()->getSession() );
|
|
|
|
|
if ( $tempName !== null ) {
|
|
|
|
|
$contextUser = UserIdentityValue::newAnonymous( $tempName );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new ParserOptions( $contextUser, $context->getLanguage() );
|
2011-10-19 14:16:01 +00:00
|
|
|
}
|
2010-01-23 15:24:44 +00:00
|
|
|
|
2012-10-10 18:13:40 +00:00
|
|
|
/**
|
2017-05-12 21:38:12 +00:00
|
|
|
* Creates a "canonical" ParserOptions object
|
2012-01-27 22:07:09 +00:00
|
|
|
*
|
2017-05-12 21:38:12 +00:00
|
|
|
* For historical reasons, certain options have default values that are
|
|
|
|
|
* different from the canonical values used for caching.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.30
|
2018-07-11 16:13:18 +00:00
|
|
|
* @since 1.32 Added string and IContextSource as options for the first parameter
|
2021-03-23 14:26:18 +00:00
|
|
|
* @since 1.36 UserIdentity is also allowed
|
2021-11-09 16:31:27 +00:00
|
|
|
* @deprecated since 1.38. Use ::newFromContext, ::newFromAnon or ::newFromUserAndLang instead.
|
|
|
|
|
* Canonical ParserOptions are now exactly the same as non-canonical.
|
2021-06-24 00:38:27 +00:00
|
|
|
* @param IContextSource|string|UserIdentity $context
|
2021-03-23 14:26:18 +00:00
|
|
|
* - If an IContextSource, the options are initialized based on the source's UserIdentity and Language.
|
2018-07-11 16:13:18 +00:00
|
|
|
* - If the string 'canonical', the options are initialized with an anonymous user and
|
2018-07-29 12:24:54 +00:00
|
|
|
* the content language.
|
2021-06-24 00:38:27 +00:00
|
|
|
* - If a UserIdentity, the options are initialized for that UserIdentity
|
2018-07-11 16:13:18 +00:00
|
|
|
* 'userlang' is taken from the $userLang parameter, defaulting to $wgLang if that is null.
|
|
|
|
|
* @param Language|StubObject|null $userLang (see above)
|
2017-05-12 21:38:12 +00:00
|
|
|
* @return ParserOptions
|
2012-01-27 22:07:09 +00:00
|
|
|
*/
|
2021-06-24 00:38:27 +00:00
|
|
|
public static function newCanonical( $context, $userLang = null ) {
|
2018-07-11 16:13:18 +00:00
|
|
|
if ( $context instanceof IContextSource ) {
|
|
|
|
|
$ret = self::newFromContext( $context );
|
|
|
|
|
} elseif ( $context === 'canonical' ) {
|
|
|
|
|
$ret = self::newFromAnon();
|
2021-06-24 00:38:27 +00:00
|
|
|
} elseif ( $context instanceof UserIdentity ) {
|
2018-07-11 16:13:18 +00:00
|
|
|
$ret = new self( $context, $userLang );
|
|
|
|
|
} else {
|
|
|
|
|
throw new InvalidArgumentException(
|
2021-06-24 00:38:27 +00:00
|
|
|
'$context must be an IContextSource, the string "canonical", or a UserIdentity'
|
2018-07-11 16:13:18 +00:00
|
|
|
);
|
|
|
|
|
}
|
2017-05-12 21:38:12 +00:00
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-01 05:20:42 +00:00
|
|
|
/**
|
|
|
|
|
* Reset static caches
|
|
|
|
|
* @internal For testing
|
|
|
|
|
*/
|
|
|
|
|
public static function clearStaticCache() {
|
2021-08-18 22:09:17 +00:00
|
|
|
if ( !defined( 'MW_PHPUNIT_TEST' ) && !defined( 'MW_PARSER_TEST' ) ) {
|
2023-07-20 13:31:47 +00:00
|
|
|
throw new LogicException( __METHOD__ . ' is just for testing' );
|
2021-03-01 05:20:42 +00:00
|
|
|
}
|
|
|
|
|
self::$defaults = null;
|
|
|
|
|
self::$lazyOptions = null;
|
|
|
|
|
self::$cacheVaryingOptionsHash = null;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Get default option values
|
2021-11-09 16:31:27 +00:00
|
|
|
* @warning If you change the default for an existing option, all existing
|
|
|
|
|
* parser cache entries will be invalid. To avoid bugs, you'll need to handle
|
2017-05-12 21:38:12 +00:00
|
|
|
* that somehow (e.g. with the RejectParserCacheValue hook) because
|
|
|
|
|
* MediaWiki won't do it for you.
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
private static function getDefaults() {
|
2021-03-16 15:16:18 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
2022-01-28 19:39:24 +00:00
|
|
|
$mainConfig = $services->getMainConfig();
|
2022-04-26 15:48:03 +00:00
|
|
|
$interwikiMagic = $mainConfig->get( MainConfigNames::InterwikiMagic );
|
|
|
|
|
$allowExternalImages = $mainConfig->get( MainConfigNames::AllowExternalImages );
|
|
|
|
|
$allowExternalImagesFrom = $mainConfig->get( MainConfigNames::AllowExternalImagesFrom );
|
|
|
|
|
$enableImageWhitelist = $mainConfig->get( MainConfigNames::EnableImageWhitelist );
|
|
|
|
|
$allowSpecialInclusion = $mainConfig->get( MainConfigNames::AllowSpecialInclusion );
|
|
|
|
|
$maxArticleSize = $mainConfig->get( MainConfigNames::MaxArticleSize );
|
|
|
|
|
$maxPPNodeCount = $mainConfig->get( MainConfigNames::MaxPPNodeCount );
|
|
|
|
|
$maxTemplateDepth = $mainConfig->get( MainConfigNames::MaxTemplateDepth );
|
|
|
|
|
$maxPPExpandDepth = $mainConfig->get( MainConfigNames::MaxPPExpandDepth );
|
|
|
|
|
$cleanSignatures = $mainConfig->get( MainConfigNames::CleanSignatures );
|
|
|
|
|
$externalLinkTarget = $mainConfig->get( MainConfigNames::ExternalLinkTarget );
|
|
|
|
|
$expensiveParserFunctionLimit = $mainConfig->get( MainConfigNames::ExpensiveParserFunctionLimit );
|
|
|
|
|
$enableMagicLinks = $mainConfig->get( MainConfigNames::EnableMagicLinks );
|
2021-03-16 15:16:18 +00:00
|
|
|
$languageConverterFactory = $services->getLanguageConverterFactory();
|
|
|
|
|
$userOptionsLookup = $services->getUserOptionsLookup();
|
|
|
|
|
$contentLanguage = $services->getContentLanguage();
|
2017-05-12 21:38:12 +00:00
|
|
|
|
|
|
|
|
if ( self::$defaults === null ) {
|
|
|
|
|
// *UPDATE* ParserOptions::matches() if any of this changes as needed
|
|
|
|
|
self::$defaults = [
|
|
|
|
|
'dateformat' => null,
|
|
|
|
|
'interfaceMessage' => false,
|
|
|
|
|
'targetLanguage' => null,
|
|
|
|
|
'removeComments' => true,
|
2022-05-27 16:20:35 +00:00
|
|
|
'suppressTOC' => false,
|
2023-11-06 20:43:31 +00:00
|
|
|
'suppressSectionEditLinks' => false,
|
2024-04-05 22:46:57 +00:00
|
|
|
'collapsibleSections' => false,
|
2017-05-12 21:38:12 +00:00
|
|
|
'enableLimitReport' => false,
|
|
|
|
|
'preSaveTransform' => true,
|
|
|
|
|
'isPreview' => false,
|
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
|
'printable' => false,
|
|
|
|
|
'allowUnsafeRawHtml' => true,
|
|
|
|
|
'wrapclass' => 'mw-parser-output',
|
2020-04-09 03:36:39 +00:00
|
|
|
'currentRevisionRecordCallback' => [ Parser::class, 'statelessFetchRevisionRecord' ],
|
2018-01-13 00:02:09 +00:00
|
|
|
'templateCallback' => [ Parser::class, 'statelessFetchTemplate' ],
|
2017-05-12 21:38:12 +00:00
|
|
|
'speculativeRevIdCallback' => null,
|
2018-08-07 16:52:40 +00:00
|
|
|
'speculativeRevId' => null,
|
2019-06-27 04:30:35 +00:00
|
|
|
'speculativePageIdCallback' => null,
|
|
|
|
|
'speculativePageId' => null,
|
2022-05-27 16:38:32 +00:00
|
|
|
'useParsoid' => false,
|
2017-05-12 21:38:12 +00:00
|
|
|
];
|
|
|
|
|
|
2021-03-01 05:20:42 +00:00
|
|
|
self::$cacheVaryingOptionsHash = self::$initialCacheVaryingOptionsHash;
|
|
|
|
|
self::$lazyOptions = self::$initialLazyOptions;
|
|
|
|
|
|
2023-05-06 20:01:10 +00:00
|
|
|
( new HookRunner( $services->getHookContainer() ) )->onParserOptionsRegister(
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
self::$defaults,
|
2021-03-01 05:20:42 +00:00
|
|
|
self::$cacheVaryingOptionsHash,
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
self::$lazyOptions
|
|
|
|
|
);
|
2017-05-12 21:38:12 +00:00
|
|
|
|
2021-03-01 05:20:42 +00:00
|
|
|
ksort( self::$cacheVaryingOptionsHash );
|
2017-05-12 21:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unit tests depend on being able to modify the globals at will
|
|
|
|
|
return self::$defaults + [
|
2022-01-28 19:39:24 +00:00
|
|
|
'interwikiMagic' => $interwikiMagic,
|
|
|
|
|
'allowExternalImages' => $allowExternalImages,
|
|
|
|
|
'allowExternalImagesFrom' => $allowExternalImagesFrom,
|
|
|
|
|
'enableImageWhitelist' => $enableImageWhitelist,
|
|
|
|
|
'allowSpecialInclusion' => $allowSpecialInclusion,
|
|
|
|
|
'maxIncludeSize' => $maxArticleSize * 1024,
|
|
|
|
|
'maxPPNodeCount' => $maxPPNodeCount,
|
|
|
|
|
'maxPPExpandDepth' => $maxPPExpandDepth,
|
|
|
|
|
'maxTemplateDepth' => $maxTemplateDepth,
|
|
|
|
|
'expensiveParserFunctionLimit' => $expensiveParserFunctionLimit,
|
|
|
|
|
'externalLinkTarget' => $externalLinkTarget,
|
|
|
|
|
'cleanSignatures' => $cleanSignatures,
|
2020-11-25 04:35:49 +00:00
|
|
|
'disableContentConversion' => $languageConverterFactory->isConversionDisabled(),
|
|
|
|
|
'disableTitleConversion' => $languageConverterFactory->isLinkConversionDisabled(),
|
2022-06-02 11:21:57 +00:00
|
|
|
// FIXME: The fallback to false for enableMagicLinks is a band-aid to allow
|
|
|
|
|
// the phpunit entrypoint patch (I82045c207738d152d5b0006f353637cfaa40bb66)
|
|
|
|
|
// to be merged.
|
|
|
|
|
// It is possible that a test somewhere is globally resetting $wgEnableMagicLinks
|
|
|
|
|
// to null, or that ParserOptions is somehow similarly getting reset in such a way
|
|
|
|
|
// that $enableMagicLinks ends up as null rather than an array. This workaround
|
|
|
|
|
// seems harmless, but would be nice to eventually fix the underlying issue.
|
|
|
|
|
'magicISBNLinks' => $enableMagicLinks['ISBN'] ?? false,
|
|
|
|
|
'magicPMIDLinks' => $enableMagicLinks['PMID'] ?? false,
|
|
|
|
|
'magicRFCLinks' => $enableMagicLinks['RFC'] ?? false,
|
2021-03-16 15:16:18 +00:00
|
|
|
'thumbsize' => $userOptionsLookup->getDefaultOption( 'thumbsize' ),
|
|
|
|
|
'userlang' => $contentLanguage,
|
2017-05-12 21:38:12 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get user options
|
|
|
|
|
*
|
2021-03-23 14:26:18 +00:00
|
|
|
* @param UserIdentity $user
|
2017-05-12 21:38:12 +00:00
|
|
|
* @param Language $lang
|
|
|
|
|
*/
|
2021-03-23 14:26:18 +00:00
|
|
|
private function initialiseFromUser( UserIdentity $user, Language $lang ) {
|
2021-08-10 15:43:29 +00:00
|
|
|
// Initially lazy loaded option defaults must not be taken into account,
|
|
|
|
|
// otherwise lazy loading does not work. Setting a default for lazy option
|
|
|
|
|
// is useful for matching with canonical options.
|
|
|
|
|
$this->options = $this->nullifyLazyOption( self::getDefaults() );
|
2010-12-18 17:56:38 +00:00
|
|
|
|
2011-10-19 14:16:01 +00:00
|
|
|
$this->mUser = $user;
|
2021-03-23 14:26:18 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
$optionsLookup = $services->getUserOptionsLookup();
|
|
|
|
|
$this->options['thumbsize'] = $optionsLookup->getOption( $user, 'thumbsize' );
|
2017-05-12 21:38:12 +00:00
|
|
|
$this->options['userlang'] = $lang;
|
2007-01-20 12:50:56 +00:00
|
|
|
}
|
2010-08-09 21:53:21 +00:00
|
|
|
|
2014-12-04 09:42:20 +00:00
|
|
|
/**
|
|
|
|
|
* Check if these options match that of another options set
|
|
|
|
|
*
|
|
|
|
|
* This ignores report limit settings that only affect HTML comments
|
|
|
|
|
*
|
2015-04-01 00:13:47 +00:00
|
|
|
* @param ParserOptions $other
|
2014-12-04 09:42:20 +00:00
|
|
|
* @return bool
|
|
|
|
|
* @since 1.25
|
|
|
|
|
*/
|
|
|
|
|
public function matches( ParserOptions $other ) {
|
2017-05-12 21:38:12 +00:00
|
|
|
// Compare most options
|
|
|
|
|
$options = array_keys( $this->options );
|
|
|
|
|
$options = array_diff( $options, [
|
|
|
|
|
'enableLimitReport', // only affects HTML comments
|
2020-08-12 21:54:55 +00:00
|
|
|
'tidy', // Has no effect since 1.35; removed in 1.36
|
2017-05-12 21:38:12 +00:00
|
|
|
] );
|
|
|
|
|
foreach ( $options as $option ) {
|
2018-10-26 22:42:26 +00:00
|
|
|
// Resolve any lazy options
|
|
|
|
|
$this->lazyLoadOption( $option );
|
|
|
|
|
$other->lazyLoadOption( $option );
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
$o1 = $this->optionToString( $this->options[$option] );
|
|
|
|
|
$o2 = $this->optionToString( $other->options[$option] );
|
|
|
|
|
if ( $o1 !== $o2 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compare most other fields
|
2021-03-01 05:20:42 +00:00
|
|
|
foreach ( ( new ReflectionClass( $this ) )->getProperties() as $property ) {
|
|
|
|
|
$field = $property->getName();
|
|
|
|
|
if ( $property->isStatic() ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ( in_array( $field, [
|
|
|
|
|
'options', // Already checked above
|
|
|
|
|
'onAccessCallback', // only used for ParserOutput option tracking
|
|
|
|
|
] ) ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-04 09:42:20 +00:00
|
|
|
if ( !is_object( $this->$field ) && $this->$field !== $other->$field ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-12 21:38:12 +00:00
|
|
|
|
|
|
|
|
return true;
|
2014-12-04 09:42:20 +00:00
|
|
|
}
|
|
|
|
|
|
2018-10-26 22:42:26 +00:00
|
|
|
/**
|
|
|
|
|
* @param ParserOptions $other
|
|
|
|
|
* @return bool Whether the cache key relevant options match those of $other
|
|
|
|
|
* @since 1.33
|
|
|
|
|
*/
|
|
|
|
|
public function matchesForCacheKey( ParserOptions $other ) {
|
|
|
|
|
foreach ( self::allCacheVaryingOptions() as $option ) {
|
|
|
|
|
// Populate any lazy options
|
|
|
|
|
$this->lazyLoadOption( $option );
|
|
|
|
|
$other->lazyLoadOption( $option );
|
|
|
|
|
|
|
|
|
|
$o1 = $this->optionToString( $this->options[$option] );
|
|
|
|
|
$o2 = $this->optionToString( $other->options[$option] );
|
|
|
|
|
if ( $o1 !== $o2 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-09 21:53:21 +00:00
|
|
|
/**
|
2010-12-26 19:21:45 +00:00
|
|
|
* Registers a callback for tracking which ParserOptions which are used.
|
2021-11-08 20:33:36 +00:00
|
|
|
*
|
|
|
|
|
* @since 1.16
|
2021-01-16 09:40:31 +00:00
|
|
|
* @param callable|null $callback
|
2010-08-09 21:53:21 +00:00
|
|
|
*/
|
2014-08-11 20:24:54 +00:00
|
|
|
public function registerWatcher( $callback ) {
|
2010-12-26 19:21:45 +00:00
|
|
|
$this->onAccessCallback = $callback;
|
2010-08-09 21:53:21 +00:00
|
|
|
}
|
2010-12-11 03:52:35 +00:00
|
|
|
|
2010-08-09 21:53:21 +00:00
|
|
|
/**
|
2021-11-08 20:33:36 +00:00
|
|
|
* Record that an option was internally accessed.
|
|
|
|
|
*
|
|
|
|
|
* This calls the watcher set by ParserOptions::registerWatcher().
|
2023-06-29 23:19:25 +00:00
|
|
|
* Typically, the watcher callback is ParserOutput::recordOption().
|
2021-11-08 20:33:36 +00:00
|
|
|
* The information registered this way is consumed by ParserCache::save().
|
2015-08-26 16:17:05 +00:00
|
|
|
*
|
2014-04-21 23:38:39 +00:00
|
|
|
* @param string $optionName Name of the option
|
2010-08-09 21:53:21 +00:00
|
|
|
*/
|
2021-11-08 20:33:36 +00:00
|
|
|
private function optionUsed( $optionName ) {
|
2010-12-26 19:21:45 +00:00
|
|
|
if ( $this->onAccessCallback ) {
|
|
|
|
|
call_user_func( $this->onAccessCallback, $optionName );
|
|
|
|
|
}
|
2010-08-09 21:53:21 +00:00
|
|
|
}
|
2010-12-11 03:52:35 +00:00
|
|
|
|
2017-06-21 16:21:45 +00:00
|
|
|
/**
|
|
|
|
|
* Return all option keys that vary the options hash
|
|
|
|
|
* @since 1.30
|
|
|
|
|
* @return string[]
|
|
|
|
|
*/
|
|
|
|
|
public static function allCacheVaryingOptions() {
|
2021-03-01 05:20:42 +00:00
|
|
|
return array_keys( array_filter( self::getCacheVaryingOptionsHash() ) );
|
2017-06-21 16:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* Convert an option to a string value
|
|
|
|
|
* @param mixed $value
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
private function optionToString( $value ) {
|
|
|
|
|
if ( $value === true ) {
|
|
|
|
|
return '1';
|
|
|
|
|
} elseif ( $value === false ) {
|
|
|
|
|
return '0';
|
|
|
|
|
} elseif ( $value === null ) {
|
|
|
|
|
return '';
|
|
|
|
|
} elseif ( $value instanceof Language ) {
|
|
|
|
|
return $value->getCode();
|
|
|
|
|
} elseif ( is_array( $value ) ) {
|
2018-02-17 12:29:13 +00:00
|
|
|
return '[' . implode( ',', array_map( [ $this, 'optionToString' ], $value ) ) . ']';
|
2017-05-12 21:38:12 +00:00
|
|
|
} else {
|
|
|
|
|
return (string)$value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-09 21:53:21 +00:00
|
|
|
/**
|
|
|
|
|
* Generate a hash string with the values set on these ParserOptions
|
|
|
|
|
* for the keys given in the array.
|
|
|
|
|
* This will be used as part of the hash key for the parser cache,
|
2014-04-23 14:20:40 +00:00
|
|
|
* so users sharing the options with vary for the same page share
|
2010-08-09 21:53:21 +00:00
|
|
|
* the same cached data safely.
|
2010-12-11 03:52:35 +00:00
|
|
|
*
|
2010-08-09 21:53:21 +00:00
|
|
|
* @since 1.17
|
2018-04-18 12:31:13 +00:00
|
|
|
* @param string[] $forOptions
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param Title|null $title Used to get the content language of the page (since r97636)
|
2012-01-27 22:07:09 +00:00
|
|
|
* @return string Page rendering hash
|
2010-08-09 21:53:21 +00:00
|
|
|
*/
|
2011-09-20 15:55:08 +00:00
|
|
|
public function optionsHash( $forOptions, $title = null ) {
|
2022-04-26 15:48:03 +00:00
|
|
|
$renderHashAppend = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::RenderHashAppend );
|
2010-08-09 21:53:21 +00:00
|
|
|
|
2018-03-26 17:59:24 +00:00
|
|
|
$inCacheKey = self::allCacheVaryingOptions();
|
|
|
|
|
|
|
|
|
|
// Resolve any lazy options
|
2021-03-01 05:20:42 +00:00
|
|
|
$lazyOpts = array_intersect( $forOptions,
|
|
|
|
|
$inCacheKey, array_keys( self::getLazyOptions() ) );
|
2018-10-26 22:42:26 +00:00
|
|
|
foreach ( $lazyOpts as $k ) {
|
|
|
|
|
$this->lazyLoadOption( $k );
|
2018-03-26 17:59:24 +00:00
|
|
|
}
|
|
|
|
|
|
2017-06-11 14:49:32 +00:00
|
|
|
$options = $this->options;
|
2021-11-09 16:31:27 +00:00
|
|
|
$defaults = self::getDefaults();
|
2017-06-11 14:49:32 +00:00
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
// We only include used options with non-canonical values in the key
|
|
|
|
|
// so adding a new option doesn't invalidate the entire parser cache.
|
|
|
|
|
// The drawback to this is that changing the default value of an option
|
|
|
|
|
// requires manual invalidation of existing cache entries, as mentioned
|
|
|
|
|
// in the docs on the relevant methods and hooks.
|
|
|
|
|
$values = [];
|
2018-03-26 17:59:24 +00:00
|
|
|
foreach ( array_intersect( $inCacheKey, $forOptions ) as $option ) {
|
|
|
|
|
$v = $this->optionToString( $options[$option] );
|
|
|
|
|
$d = $this->optionToString( $defaults[$option] );
|
|
|
|
|
if ( $v !== $d ) {
|
|
|
|
|
$values[] = "$option=$v";
|
2017-05-12 21:38:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-17 12:29:13 +00:00
|
|
|
$confstr = $values ? implode( '!', $values ) : 'canonical';
|
2017-05-12 21:38:12 +00:00
|
|
|
|
|
|
|
|
// add in language specific options, if any
|
|
|
|
|
// @todo FIXME: This is just a way of retrieving the url/user preferred variant
|
2021-01-16 09:40:31 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
$lang = $title ? $title->getPageLanguage() : $services->getContentLanguage();
|
|
|
|
|
$converter = $services->getLanguageConverterFactory()->getLanguageConverter( $lang );
|
2020-01-23 18:39:23 +00:00
|
|
|
$confstr .= $converter->getExtraHashOptions();
|
2017-05-12 21:38:12 +00:00
|
|
|
|
2022-01-28 19:39:24 +00:00
|
|
|
$confstr .= $renderHashAppend;
|
2017-05-12 21:38:12 +00:00
|
|
|
|
|
|
|
|
if ( $this->mExtraKey != '' ) {
|
|
|
|
|
$confstr .= $this->mExtraKey;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-28 14:08:59 +00:00
|
|
|
$user = $services->getUserFactory()->newFromUserIdentity( $this->getUserIdentity() );
|
2017-05-12 21:38:12 +00:00
|
|
|
// Give a chance for extensions to modify the hash, if they have
|
|
|
|
|
// extra options or other effects on the parser cache.
|
2023-05-06 20:01:10 +00:00
|
|
|
( new HookRunner( $services->getHookContainer() ) )->onPageRenderingHash(
|
2021-07-28 14:08:59 +00:00
|
|
|
$confstr,
|
|
|
|
|
$user,
|
|
|
|
|
$forOptions
|
|
|
|
|
);
|
2017-05-12 21:38:12 +00:00
|
|
|
|
|
|
|
|
// Make it a valid memcached key fragment
|
|
|
|
|
$confstr = str_replace( ' ', '_', $confstr );
|
|
|
|
|
|
|
|
|
|
return $confstr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test whether these options are safe to cache
|
2020-12-15 18:56:00 +00:00
|
|
|
* @param string[]|null $usedOptions the list of options actually used in the parse. Defaults to all options.
|
2017-05-12 21:38:12 +00:00
|
|
|
* @return bool
|
2020-12-15 18:56:00 +00:00
|
|
|
* @since 1.30
|
2017-05-12 21:38:12 +00:00
|
|
|
*/
|
2024-10-16 18:58:33 +00:00
|
|
|
public function isSafeToCache( ?array $usedOptions = null ) {
|
2021-11-09 16:31:27 +00:00
|
|
|
$defaults = self::getDefaults();
|
2021-03-01 05:20:42 +00:00
|
|
|
$inCacheKey = self::getCacheVaryingOptionsHash();
|
2022-10-21 02:26:49 +00:00
|
|
|
$usedOptions ??= array_keys( $this->options );
|
2020-12-15 18:56:00 +00:00
|
|
|
foreach ( $usedOptions as $option ) {
|
2021-03-01 05:20:42 +00:00
|
|
|
if ( empty( $inCacheKey[$option] ) && empty( self::$callbacks[$option] ) ) {
|
2021-01-28 00:33:35 +00:00
|
|
|
$v = $this->optionToString( $this->options[$option] ?? null );
|
|
|
|
|
$d = $this->optionToString( $defaults[$option] ?? null );
|
2017-05-12 21:38:12 +00:00
|
|
|
if ( $v !== $d ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-03 20:10:02 +00:00
|
|
|
/**
|
2015-09-26 17:17:49 +00:00
|
|
|
* Sets a hook to force that a page exists, and sets a current revision callback to return
|
|
|
|
|
* a revision with custom content when the current revision of the page is requested.
|
2014-06-03 20:10:02 +00:00
|
|
|
*
|
|
|
|
|
* @since 1.25
|
|
|
|
|
* @param Title $title
|
|
|
|
|
* @param Content $content
|
2021-03-23 14:26:18 +00:00
|
|
|
* @param UserIdentity $user The user that the fake revision is attributed to
|
2014-06-03 20:10:02 +00:00
|
|
|
* @return ScopedCallback to unset the hook
|
|
|
|
|
*/
|
|
|
|
|
public function setupFakeRevision( $title, $content, $user ) {
|
2020-06-03 03:48:42 +00:00
|
|
|
$oldCallback = $this->setCurrentRevisionRecordCallback(
|
2021-02-10 22:31:02 +00:00
|
|
|
static function (
|
2020-06-03 03:48:42 +00:00
|
|
|
$titleToCheck, $parser = null ) use ( $title, $content, $user, &$oldCallback
|
2015-09-26 17:17:49 +00:00
|
|
|
) {
|
|
|
|
|
if ( $titleToCheck->equals( $title ) ) {
|
2020-06-10 08:40:05 +00:00
|
|
|
$revRecord = new MutableRevisionRecord( $title );
|
2020-11-27 04:24:16 +00:00
|
|
|
$revRecord->setContent( SlotRecord::MAIN, $content )
|
|
|
|
|
->setUser( $user )
|
|
|
|
|
->setTimestamp( MWTimestamp::now( TS_MW ) )
|
|
|
|
|
->setPageId( $title->getArticleID() )
|
|
|
|
|
->setParentId( $title->getLatestRevID() );
|
2020-06-10 08:40:05 +00:00
|
|
|
return $revRecord;
|
2015-09-26 17:17:49 +00:00
|
|
|
} else {
|
|
|
|
|
return call_user_func( $oldCallback, $titleToCheck, $parser );
|
|
|
|
|
}
|
2014-06-03 20:10:02 +00:00
|
|
|
}
|
2015-09-26 17:17:49 +00:00
|
|
|
);
|
|
|
|
|
|
2022-01-28 19:39:24 +00:00
|
|
|
$hookContainer = MediaWikiServices::getInstance()->getHookContainer();
|
|
|
|
|
$hookScope = $hookContainer->scopedRegister(
|
|
|
|
|
'TitleExists',
|
2021-02-10 22:31:02 +00:00
|
|
|
static function ( $titleToCheck, &$exists ) use ( $title ) {
|
2014-06-03 20:10:02 +00:00
|
|
|
if ( $titleToCheck->equals( $title ) ) {
|
|
|
|
|
$exists = true;
|
|
|
|
|
}
|
2022-01-28 19:39:24 +00:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2018-06-11 06:55:11 +00:00
|
|
|
$linkCache = MediaWikiServices::getInstance()->getLinkCache();
|
|
|
|
|
$linkCache->clearBadLink( $title->getPrefixedDBkey() );
|
2022-01-28 19:39:24 +00:00
|
|
|
|
|
|
|
|
return new ScopedCallback( function () use ( $title, $hookScope, $linkCache, $oldCallback ) {
|
|
|
|
|
ScopedCallback::consume( $hookScope );
|
2018-06-11 06:55:11 +00:00
|
|
|
$linkCache->clearLink( $title );
|
2022-01-28 19:39:24 +00:00
|
|
|
$this->setCurrentRevisionRecordCallback( $oldCallback );
|
2014-06-03 20:10:02 +00:00
|
|
|
} );
|
|
|
|
|
}
|
2022-11-05 20:49:52 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns reason for rendering the content. This human-readable, intended for logging and debugging only.
|
|
|
|
|
* Expected values include "edit", "view", "purge", "LinksUpdate", etc.
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function getRenderReason(): string {
|
|
|
|
|
return $this->renderReason;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets reason for rendering the content. This human-readable, intended for logging and debugging only.
|
|
|
|
|
* Expected values include "edit", "view", "purge", "LinksUpdate", etc.
|
|
|
|
|
* @param string $renderReason
|
|
|
|
|
*/
|
|
|
|
|
public function setRenderReason( string $renderReason ): void {
|
|
|
|
|
$this->renderReason = $renderReason;
|
|
|
|
|
}
|
2007-01-20 12:50:56 +00:00
|
|
|
}
|
2017-05-12 21:38:12 +00:00
|
|
|
|
2024-10-03 18:39:06 +00:00
|
|
|
/** @deprecated class alias since 1.43 */
|
|
|
|
|
class_alias( ParserOptions::class, 'ParserOptions' );
|
|
|
|
|
|
2017-05-12 21:38:12 +00:00
|
|
|
/**
|
|
|
|
|
* For really cool vim folding this needs to be at the end:
|
|
|
|
|
* vim: foldmarker=@{,@} foldmethod=marker
|
|
|
|
|
*/
|