From a652f306a542c0efdfa0bef33cd68c8f6587e0bc Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 14 Jan 2022 17:40:52 +0100 Subject: [PATCH] SettingsBuilder: allow maintenance scripts to manipulate config Maintenance scripts often need to manipulate configuration settings. This introduces a way to do this cleanly via SettingsBuilder, removing the need to rely on global variables. Bug: T294739 Bug: T294742 Bug: T300128 Change-Id: Ibf443fd564bbbf388cce8ab4dabba55ebca0dfa4 --- includes/Settings/SettingsBuilder.php | 91 ++++++++++++++++++- includes/Setup.php | 2 +- includes/WebStart.php | 4 +- maintenance/doMaintenance.php | 20 ++-- maintenance/dumpIterator.php | 16 +++- maintenance/fetchText.php | 10 +- maintenance/getConfiguration.php | 8 +- maintenance/includes/BackupDumper.php | 6 +- maintenance/includes/Maintenance.php | 75 +++++++++------ maintenance/includes/TextPassDumper.php | 5 +- maintenance/mergeMessageFileList.php | 13 +-- maintenance/rebuildFileCache.php | 12 +-- maintenance/rebuildLocalisationCache.php | 12 +-- maintenance/runJobs.php | 5 +- tests/parser/editTests.php | 6 +- tests/parser/fuzzTest.php | 3 +- tests/parser/parserTests.php | 5 +- .../includes/Settings/SettingsBuilderTest.php | 23 ++++- 18 files changed, 228 insertions(+), 88 deletions(-) diff --git a/includes/Settings/SettingsBuilder.php b/includes/Settings/SettingsBuilder.php index dfdd230312b..a55eb6e9e4a 100644 --- a/includes/Settings/SettingsBuilder.php +++ b/includes/Settings/SettingsBuilder.php @@ -37,6 +37,9 @@ class SettingsBuilder { /** @var ConfigBuilder */ private $configSink; + /** @var Config */ + private $config; + /** @var SettingsSource[] */ private $currentBatch; @@ -46,6 +49,16 @@ class SettingsBuilder { /** @var PhpIniSink */ private $phpIniSink; + /** + * Configuration that applies to SettingsBuilder itself. + * Initialized by the constructor, may be overwritten by regular + * config values. Merge strategies are currently not implemented + * but can be added if needed. + * + * @var array + */ + private $settingsConfig; + /** * When we're done applying all settings. * @@ -76,6 +89,10 @@ class SettingsBuilder { $this->configSink = $configSink; $this->configSchema = new ConfigSchemaAggregator(); $this->phpIniSink = $phpIniSink; + $this->settingsConfig = [ + 'ExtensionDirectory' => "$baseDir/extensions", + 'StyleDirectory' => "$baseDir/skins", + ]; $this->reset(); } @@ -153,7 +170,7 @@ class SettingsBuilder { * @return StatusValue */ public function validate(): StatusValue { - $config = $this->configSink->build(); + $config = $this->getConfig(); $validator = new Validator(); $result = StatusValue::newGood(); @@ -183,9 +200,12 @@ class SettingsBuilder { /** * Return a Config object with all the default settings loaded so far. * + * @note This will implicitly call apply() + * * @return Config */ public function getDefaultConfig(): Config { + $this->apply(); return new HashConfig( $this->configSchema->getDefaults() ); } @@ -201,7 +221,12 @@ class SettingsBuilder { * @throws SettingsBuilderException */ public function apply(): self { + if ( !$this->currentBatch ) { + return $this; + } + $this->assertNotFinished(); + $this->config = null; $allSettings = $this->loadRecursive( $this->currentBatch ); @@ -267,6 +292,16 @@ class SettingsBuilder { * @param array $settings */ private function applySettings( array $settings ) { + // First extract config variables that change the behavior of SettingsBuilder. + // No merge strategies are applied, defaults are set in the constructor. + if ( isset( $settings['config'] ) ) { + foreach ( $this->settingsConfig as $key => $dummy ) { + if ( array_key_exists( $key, $settings['config'] ) ) { + $this->settingsConfig[$key] = $settings['config'][$key]; + } + } + } + foreach ( $settings['config'] ?? [] as $key => $value ) { $this->configSink->set( $key, @@ -298,10 +333,8 @@ class SettingsBuilder { // not just queued. We can't do this right now, because we need to preserve // interoperability with wfLoadExtension() being called from LocalSettings.php. - $config = $this->configSink->build(); - if ( isset( $settings['extensions'] ) ) { - $extDir = $config->get( 'ExtensionDirectory' ); + $extDir = $this->settingsConfig['ExtensionDirectory']; foreach ( $settings['extensions'] ?? [] as $ext ) { $path = "$extDir/$ext/extension.json"; // see wfLoadExtension $this->extensionRegistry->queue( $path ); @@ -309,7 +342,7 @@ class SettingsBuilder { } if ( isset( $settings['skins'] ) ) { - $skinDir = $config->get( 'StyleDirectory' ); + $skinDir = $this->settingsConfig['StyleDirectory']; foreach ( $settings['skins'] ?? [] as $skin ) { $path = "$skinDir/$skin/skin.json"; // see wfLoadSkin $this->extensionRegistry->queue( $path ); @@ -317,6 +350,54 @@ class SettingsBuilder { } } + /** + * Sets the value of a config variable. + * This is a shorthand for loadArray(). + * @unstable + * + * @param string $key the name of the config setting + * @param mixed $value The value to set + * + * @return $this + */ + public function setConfigValue( string $key, $value ): self { + $this->loadArray( [ 'config' => [ $key => $value ] ] ); + return $this; + } + + /** + * Sets the value of multiple config variables. + * This is a shorthand for loadArray(). + * @unstable + * + * @param array $values An associative array mapping names to values. + * + * @return $this + */ + public function setConfigValues( array $values ): self { + $this->loadArray( [ 'config' => $values ] ); + return $this; + } + + /** + * Returns the config loaded so far. Implicitly triggers apply() when needed. + * + * @note This will implicitly call apply() + * + * @unstable + * @return Config + */ + public function getConfig(): Config { + if ( $this->config && !$this->currentBatch ) { + return $this->config; + } + + $this->apply(); + $this->config = $this->configSink->build(); + + return $this->config; + } + private function reset() { $this->currentBatch = []; } diff --git a/includes/Setup.php b/includes/Setup.php index e17e100422c..fe21619f871 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -175,7 +175,7 @@ $wgSettings->apply(); */ if ( defined( 'MW_SETUP_CALLBACK' ) ) { - call_user_func( MW_SETUP_CALLBACK ); + call_user_func( MW_SETUP_CALLBACK, $wgSettings ); // Make any additional settings available in globals for use here $wgSettings->apply(); } diff --git a/includes/WebStart.php b/includes/WebStart.php index a8c880134c1..7c440bc66eb 100644 --- a/includes/WebStart.php +++ b/includes/WebStart.php @@ -36,6 +36,8 @@ # T17461: Make IE8 turn off content sniffing. Everybody else should ignore this # We're adding it here so that it's *always* set, even for alternate entry # points and when $wgOut gets disabled or overridden. +use MediaWiki\Settings\SettingsBuilder; + header( 'X-Content-Type-Options: nosniff' ); # Valid web server entry point, enable includes. @@ -74,7 +76,7 @@ if ( !defined( 'MW_CONFIG_CALLBACK' ) ) { } } -function wfWebStartSetup() { +function wfWebStartSetup( SettingsBuilder $settings ) { // Initialise output buffering // Check for previously set up buffers, to avoid a mix of gzip and non-gzip output. if ( ob_get_level() == 0 ) { diff --git a/maintenance/doMaintenance.php b/maintenance/doMaintenance.php index 58860d70561..3bafb2e1ada 100644 --- a/maintenance/doMaintenance.php +++ b/maintenance/doMaintenance.php @@ -25,6 +25,7 @@ * @ingroup Maintenance */ use MediaWiki\MediaWikiServices; +use MediaWiki\Settings\SettingsBuilder; if ( !defined( 'RUN_MAINTENANCE_IF_MAIN' ) ) { echo "This file must be included after Maintenance.php\n"; @@ -70,18 +71,23 @@ if ( !defined( 'MW_CONFIG_CALLBACK' ) && !defined( 'MW_CONFIG_FILE' ) ) { // Custom setup for Maintenance entry point if ( !defined( 'MW_SETUP_CALLBACK' ) ) { - function wfMaintenanceSetup() { - global $maintenance, $wgLocalisationCacheConf, $wgCacheDirectory; + function wfMaintenanceSetup( SettingsBuilder $settingsBuilder ) { + global $maintenance; + $config = $settingsBuilder->getConfig(); + if ( $maintenance->getDbType() === Maintenance::DB_NONE ) { - if ( $wgLocalisationCacheConf['storeClass'] === false - && ( $wgLocalisationCacheConf['store'] == 'db' - || ( $wgLocalisationCacheConf['store'] == 'detect' && !$wgCacheDirectory ) ) + $cacheConf = $config->get( 'LocalisationCacheConf' ); + if ( $cacheConf['storeClass'] === false + && ( $cacheConf['store'] == 'db' + || ( $cacheConf['store'] == 'detect' + && !$config->get( 'CacheDirectory' ) ) ) ) { - $wgLocalisationCacheConf['storeClass'] = LCStoreNull::class; + $cacheConf['storeClass'] = LCStoreNull::class; + $settingsBuilder->setConfigValue( 'LocalisationCacheConf', $cacheConf ); } } - $maintenance->finalSetup(); + $maintenance->finalSetup( $settingsBuilder ); } define( 'MW_SETUP_CALLBACK', 'wfMaintenanceSetup' ); diff --git a/maintenance/dumpIterator.php b/maintenance/dumpIterator.php index 4e8a2122bc6..c8ed42013e1 100644 --- a/maintenance/dumpIterator.php +++ b/maintenance/dumpIterator.php @@ -28,6 +28,7 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Revision\SlotRecord; +use MediaWiki\Settings\SettingsBuilder; require_once __DIR__ . '/Maintenance.php'; @@ -109,14 +110,19 @@ abstract class DumpIterator extends Maintenance { $this->error( "Memory peak usage of " . memory_get_peak_usage() . " bytes\n" ); } - public function finalSetup() { - parent::finalSetup(); + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { + parent::finalSetup( $settingsBuilder ); if ( $this->getDbType() == Maintenance::DB_NONE ) { - global $wgUseDatabaseMessages, $wgLocalisationCacheConf, $wgHooks; - $wgUseDatabaseMessages = false; - $wgLocalisationCacheConf['storeClass'] = LCStoreNull::class; + // TODO: Allow hooks to be registered via SettingsBuilder as well! + // This matches the idea of unifying SettingsBuilder with ExtensionRegistry. + global $wgHooks; $wgHooks['InterwikiLoadPrefix'][] = 'DumpIterator::disableInterwikis'; + + $settingsBuilder->setConfigValues( [ + 'UseDatabaseMessages' => false, + 'LocalisationCacheConf' => [ 'storeClass' => LCStoreNull::class ], + ] ); } } diff --git a/maintenance/fetchText.php b/maintenance/fetchText.php index d5fd133d7b7..3c87cd7c79f 100644 --- a/maintenance/fetchText.php +++ b/maintenance/fetchText.php @@ -25,6 +25,7 @@ require_once __DIR__ . '/Maintenance.php'; use MediaWiki\MediaWikiServices; +use MediaWiki\Settings\SettingsBuilder; use MediaWiki\Storage\BlobAccessException; use MediaWiki\Storage\BlobStore; use MediaWiki\Storage\SqlBlobStore; @@ -45,17 +46,16 @@ class FetchText extends Maintenance { ); } - public function finalSetup() { + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { // This script should always try to run all db queries in the 'dump' group if such // a group exists, just like the BackupDumper and TextPassDumper modules. // To account for parts of MediaWiki that get their own db connection outside of // Maintenance::getDB(), we set this global variable so that they will attempt // to use this group. - global $wgDBDefaultGroup; - - $wgDBDefaultGroup = "dump"; + $settingsBuilder->setConfigValue( 'DBDefaultGroup', 'dump' ); // do this last so that options can override - parent::finalSetup(); + + parent::finalSetup( $settingsBuilder ); } /** diff --git a/maintenance/getConfiguration.php b/maintenance/getConfiguration.php index a23f1897f5c..7f6685eb1a2 100644 --- a/maintenance/getConfiguration.php +++ b/maintenance/getConfiguration.php @@ -23,6 +23,8 @@ * @author Antoine Musso */ +use MediaWiki\Settings\SettingsBuilder; + require_once __DIR__ . '/Maintenance.php'; /** @@ -89,9 +91,11 @@ class GetConfiguration extends Maintenance { /** * finalSetup() since we need MWException + * + * @param SettingsBuilder|null $settingsBuilder */ - public function finalSetup() { - parent::finalSetup(); + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { + parent::finalSetup( $settingsBuilder ); $this->regex = $this->getOption( 'regex' ) ?: $this->getOption( 'iregex' ); if ( $this->regex ) { diff --git a/maintenance/includes/BackupDumper.php b/maintenance/includes/BackupDumper.php index 3f147fdb93e..30e9dd39456 100644 --- a/maintenance/includes/BackupDumper.php +++ b/maintenance/includes/BackupDumper.php @@ -29,6 +29,7 @@ require_once __DIR__ . '/../Maintenance.php'; require_once __DIR__ . '/../../includes/export/WikiExporter.php'; use MediaWiki\MediaWikiServices; +use MediaWiki\Settings\SettingsBuilder; use Wikimedia\Rdbms\IMaintainableDatabase; use Wikimedia\Rdbms\LoadBalancer; @@ -150,11 +151,12 @@ abstract class BackupDumper extends Maintenance { } } - public function validateParamsAndArgs() { + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { // re-declare the --schema-version option to include the default schema version // in the description. + $schemaVersion = $settingsBuilder->getConfig()->get( 'XmlDumpSchemaVersion' ); $this->addOption( 'schema-version', 'Schema version to use for output. ' . - 'Default: ' . WikiExporter::schemaVersion(), false, true ); + 'Default: ' . $schemaVersion, false, true ); parent::validateParamsAndArgs(); } diff --git a/maintenance/includes/Maintenance.php b/maintenance/includes/Maintenance.php index 60f088b8a6a..283ab4bd14c 100644 --- a/maintenance/includes/Maintenance.php +++ b/maintenance/includes/Maintenance.php @@ -22,6 +22,7 @@ use MediaWiki\HookContainer\HookContainer; use MediaWiki\HookContainer\HookRunner; use MediaWiki\Logger\LoggerFactory; use MediaWiki\MediaWikiServices; +use MediaWiki\Settings\SettingsBuilder; use MediaWiki\Shell\Shell; use Wikimedia\Rdbms\IDatabase; use Wikimedia\Rdbms\IMaintainableDatabase; @@ -725,7 +726,7 @@ abstract class Maintenance { $this->loadParamsAndArgs(); # Set the memory limit - # Note we need to set it again later in cache LocalSettings changed it + # Note we need to set it again later in case LocalSettings changed it $this->adjustMemoryLimit(); # Set max execution time to 0 (no limit). PHP.net says that @@ -1124,34 +1125,46 @@ abstract class Maintenance { /** * Handle some last-minute setup here. + * * @stable to override + * + * @param SettingsBuilder|null $settingsBuilder */ - public function finalSetup() { - global $wgCommandLineMode, $wgServer, $wgShowExceptionDetails, $wgShowHostnames; - global $wgDBadminuser, $wgDBadminpassword, $wgDBDefaultGroup; - global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf; + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { + if ( !$settingsBuilder ) { + // HACK for backwards compatibility. All subclasses that override + // finalSetup() should be updated to pass $settingsBuilder along. + // XXX: We don't want the parameter to be nullable! How can we make it required + // without breaking backwards compatibility? + $settingsBuilder = $GLOBALS['wgSettings']; + } + + $config = $settingsBuilder->getConfig(); + $overrides = []; + $overrides['DBadminuser'] = $config->get( 'DBadminuser' ); + $overrides['DBadminpassword'] = $config->get( 'DBadminpassword' ); # Turn off output buffering again, it might have been turned on in the settings files if ( ob_get_level() ) { ob_end_flush(); } # Same with these - $wgCommandLineMode = true; + $overrides['CommandLineMode'] = true; # Override $wgServer if ( $this->hasOption( 'server' ) ) { - $wgServer = $this->getOption( 'server', $wgServer ); + $overrides['Server'] = $this->getOption( 'server', $config->get( 'Server' ) ); } # If these were passed, use them if ( $this->mDbUser ) { - $wgDBadminuser = $this->mDbUser; + $overrides['DBadminuser'] = $this->mDbUser; } if ( $this->mDbPass ) { - $wgDBadminpassword = $this->mDbPass; + $overrides['DBadminpassword'] = $this->mDbPass; } if ( $this->hasOption( 'dbgroupdefault' ) ) { - $wgDBDefaultGroup = $this->getOption( 'dbgroupdefault', null ); + $overrides['DBDefaultGroup'] = $this->getOption( 'dbgroupdefault', null ); // TODO: once MediaWikiServices::getInstance() starts throwing exceptions // and not deprecation warnings for premature access to service container, // we can remove this line. This method is called before Setup.php, @@ -1164,23 +1177,27 @@ abstract class Maintenance { } } - if ( $this->getDbType() == self::DB_ADMIN && isset( $wgDBadminuser ) ) { - $wgDBuser = $wgDBadminuser; - $wgDBpassword = $wgDBadminpassword; + if ( $this->getDbType() == self::DB_ADMIN && isset( $overrides[ 'DBadminuser' ] ) ) { + $overrides['DBuser'] = $overrides[ 'DBadminuser' ]; + $overrides['DBpassword'] = $overrides[ 'DBadminpassword' ]; - if ( $wgDBservers ) { - /** - * @var array $wgDBservers - */ - foreach ( $wgDBservers as $i => $server ) { - $wgDBservers[$i]['user'] = $wgDBuser; - $wgDBservers[$i]['password'] = $wgDBpassword; + /** @var array $dbServers */ + $dbServers = $config->get( 'DBservers' ); + if ( $dbServers ) { + foreach ( $dbServers as $i => $server ) { + $dbServers[$i]['user'] = $overrides['DBuser']; + $dbServers[$i]['password'] = $overrides['DBpassword']; } + $overrides['DBservers'] = $dbServers; } - if ( isset( $wgLBFactoryConf['serverTemplate'] ) ) { - $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser; - $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword; + + $lbFactoryConf = $config->get( 'LBFactoryConf' ); + if ( isset( $lbFactoryConf['serverTemplate'] ) ) { + $lbFactoryConf['serverTemplate']['user'] = $overrides['DBuser']; + $lbFactoryConf['serverTemplate']['password'] = $overrides['DBpassword']; + $overrides['LBFactoryConf'] = $lbFactoryConf; } + // TODO: once MediaWikiServices::getInstance() starts throwing exceptions // and not deprecation warnings for premature access to service container, // we can remove this line. This method is called before Setup.php, @@ -1198,14 +1215,16 @@ abstract class Maintenance { $this->afterFinalSetup(); - $wgShowExceptionDetails = true; - $wgShowHostnames = true; + $overrides['ShowExceptionDetails'] = true; + $overrides['ShowHostname'] = true; - Wikimedia\suppressWarnings(); - set_time_limit( 0 ); - Wikimedia\restoreWarnings(); + $ini = [ + 'max_execution_time' => 0, + ]; $this->adjustMemoryLimit(); + + $settingsBuilder->loadArray( [ 'config' => $overrides, 'php-ini' => $ini ] ); } /** diff --git a/maintenance/includes/TextPassDumper.php b/maintenance/includes/TextPassDumper.php index ddfb6cf78f5..50fccfbe3ea 100644 --- a/maintenance/includes/TextPassDumper.php +++ b/maintenance/includes/TextPassDumper.php @@ -30,6 +30,7 @@ require_once __DIR__ . '/../../includes/export/WikiExporter.php'; use MediaWiki\MediaWikiServices; use MediaWiki\Revision\SlotRecord; +use MediaWiki\Settings\SettingsBuilder; use MediaWiki\Shell\Shell; use MediaWiki\Storage\BlobAccessException; use MediaWiki\Storage\BlobStore; @@ -151,8 +152,8 @@ TEXT } } - public function finalSetup() { - parent::finalSetup(); + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { + parent::finalSetup( $settingsBuilder ); SevenZipStream::register(); } diff --git a/maintenance/mergeMessageFileList.php b/maintenance/mergeMessageFileList.php index 633aa01f0aa..8dfa8260821 100644 --- a/maintenance/mergeMessageFileList.php +++ b/maintenance/mergeMessageFileList.php @@ -25,6 +25,8 @@ // NO_AUTOLOAD -- file-scope define() used to modify behaviour # Start from scratch +use MediaWiki\Settings\SettingsBuilder; + define( 'MW_NO_EXTENSION_MESSAGES', 1 ); require_once __DIR__ . '/Maintenance.php'; @@ -120,13 +122,12 @@ class MergeMessageFileList extends Maintenance { } } - public function finalSetup() { + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { # This script commonly needs to be run before the l10n cache. But if - # $wgLanguageCode is not 'en', it won't be able to run because there is - # no l10n cache. Break the cycle by forcing $wgLanguageCode = 'en'. - global $wgLanguageCode; - $wgLanguageCode = 'en'; - parent::finalSetup(); + # LanguageCode is not 'en', it won't be able to run because there is + # no l10n cache. Break the cycle by forcing the LanguageCode setting to 'en'. + $settingsBuilder->setConfigValue( 'LanguageCode', 'en' ); + parent::finalSetup( $settingsBuilder ); } /** diff --git a/maintenance/rebuildFileCache.php b/maintenance/rebuildFileCache.php index 86c76996d1b..305d29144a0 100644 --- a/maintenance/rebuildFileCache.php +++ b/maintenance/rebuildFileCache.php @@ -22,6 +22,7 @@ */ use MediaWiki\MediaWikiServices; +use MediaWiki\Settings\SettingsBuilder; require_once __DIR__ . '/Maintenance.php'; @@ -43,12 +44,11 @@ class RebuildFileCache extends Maintenance { $this->setBatchSize( 100 ); } - public function finalSetup() { - global $wgUseFileCache; - - $this->enabled = $wgUseFileCache; + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { + $this->enabled = $settingsBuilder->getConfig()->get( 'UseFileCache' ); // Script will handle capturing output and saving it itself - $wgUseFileCache = false; + $settingsBuilder->setConfigValue( 'UseFileCache', false ); + // Avoid DB writes (like enotif/counters) MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode() ->setReason( 'Building cache' ); @@ -56,7 +56,7 @@ class RebuildFileCache extends Maintenance { // Ensure no debug-specific logic ends up in the cache (must be after Setup.php) MWDebug::deinit(); - parent::finalSetup(); + parent::finalSetup( $settingsBuilder ); } public function execute() { diff --git a/maintenance/rebuildLocalisationCache.php b/maintenance/rebuildLocalisationCache.php index 99d8631aef1..e191d330aeb 100644 --- a/maintenance/rebuildLocalisationCache.php +++ b/maintenance/rebuildLocalisationCache.php @@ -33,6 +33,7 @@ use MediaWiki\Config\ServiceOptions; use MediaWiki\Languages\LanguageNameUtils; use MediaWiki\Logger\LoggerFactory; use MediaWiki\MediaWikiServices; +use MediaWiki\Settings\SettingsBuilder; require_once __DIR__ . '/Maintenance.php'; @@ -76,13 +77,12 @@ class RebuildLocalisationCache extends Maintenance { ); } - public function finalSetup() { + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { # This script needs to be run to build the initial l10n cache. But if - # $wgLanguageCode is not 'en', it won't be able to run because there is - # no l10n cache. Break the cycle by forcing $wgLanguageCode = 'en'. - global $wgLanguageCode; - $wgLanguageCode = 'en'; - parent::finalSetup(); + # LanguageCode is not 'en', it won't be able to run because there is + # no l10n cache. Break the cycle by forcing the LanguageCode setting to 'en'. + $settingsBuilder->setConfigValue( 'LanguageCode', 'en' ); + parent::finalSetup( $settingsBuilder ); } public function execute() { diff --git a/maintenance/runJobs.php b/maintenance/runJobs.php index 7c61a358481..b2e030e01c5 100644 --- a/maintenance/runJobs.php +++ b/maintenance/runJobs.php @@ -24,6 +24,7 @@ require_once __DIR__ . '/Maintenance.php'; use MediaWiki\MediaWikiServices; +use MediaWiki\Settings\SettingsBuilder; /** * Maintenance script that runs pending jobs. @@ -43,11 +44,11 @@ class RunJobs extends Maintenance { $this->addOption( 'wait', 'Wait for new jobs instead of exiting', false, false ); } - public function finalSetup() { + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { // So extensions (and other code) can check whether they're running in job mode. // This is not defined if this script is included from installer/updater or phpunit. define( 'MEDIAWIKI_JOB_RUNNER', true ); - parent::finalSetup(); + parent::finalSetup( $settingsBuilder ); } public function memoryLimit() { diff --git a/tests/parser/editTests.php b/tests/parser/editTests.php index 57c96aeaec7..5df60fea719 100644 --- a/tests/parser/editTests.php +++ b/tests/parser/editTests.php @@ -1,5 +1,7 @@ addOption( 'seed', 'Start the fuzz test from the specified seed', false, true ); } - public function finalSetup() { + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { // Make RequestContext::resetMain() happy define( 'MW_PARSER_TEST', 1 ); diff --git a/tests/parser/parserTests.php b/tests/parser/parserTests.php index 3065cc88130..978a6793df1 100644 --- a/tests/parser/parserTests.php +++ b/tests/parser/parserTests.php @@ -27,6 +27,7 @@ require_once __DIR__ . '/../../maintenance/Maintenance.php'; use MediaWiki\MediaWikiServices; +use MediaWiki\Settings\SettingsBuilder; class ParserTestsMaintenance extends Maintenance { public function __construct() { @@ -72,12 +73,12 @@ class ParserTestsMaintenance extends Maintenance { 'defaults.' ); } - public function finalSetup() { + public function finalSetup( SettingsBuilder $settingsBuilder = null ) { // Some methods which are discouraged for normal code throw exceptions unless // we declare this is just a test. define( 'MW_PARSER_TEST', true ); - parent::finalSetup(); + parent::finalSetup( $settingsBuilder ); self::requireTestsAutoloader(); TestSetup::applyInitialConfig(); } diff --git a/tests/phpunit/unit/includes/Settings/SettingsBuilderTest.php b/tests/phpunit/unit/includes/Settings/SettingsBuilderTest.php index a62febe300a..21dd2364a0c 100644 --- a/tests/phpunit/unit/includes/Settings/SettingsBuilderTest.php +++ b/tests/phpunit/unit/includes/Settings/SettingsBuilderTest.php @@ -33,18 +33,15 @@ class SettingsBuilderTest extends TestCase { } public function testLoadingFromFile() { - $configBuilder = new ArrayConfigBuilder(); - $phpIniSinkMock = $this->createMock( PhpIniSink::class ); $phpIniSinkMock->expects( $this->once() )->method( 'set' )->with( 'foo', 'bar' ); $setting = $this->newSettingsBuilder( [ - 'configBuilder' => $configBuilder, 'phpIniSink' => $phpIniSinkMock ] ); $setting->loadFile( 'fixtures/settings.json' )->apply(); - $config = $configBuilder->build(); + $config = $setting->getConfig(); $this->assertSame( 'TEST', $config->get( 'Something' ) ); } @@ -266,10 +263,26 @@ class SettingsBuilderTest extends TestCase { } } + public function testSetConfig() { + $setting = $this->newSettingsBuilder(); + + $setting->setConfigValues( [ 'a' => 1, 'b' => 2 ] ); + + $config = $setting->getConfig(); + $this->assertSame( 1, $config->get( 'a' ) ); + $this->assertSame( 2, $config->get( 'b' ) ); + + $setting->setConfigValue( 'b', 22 ); + + $config = $setting->getConfig(); + $this->assertSame( 1, $config->get( 'a' ) ); + $this->assertSame( 22, $config->get( 'b' ) ); + } + public function testApplyPurgesState() { $configBuilder = new ArrayConfigBuilder(); $setting = $this->newSettingsBuilder( [ 'configBuilder' => $configBuilder ] ); - $setting->loadArray( [ 'config' => [ 'MySetting' => 'MyValue', ], ] ) + $setting->setConfigValue( 'MySetting', 'MyValue' ) ->apply(); $this->assertSame( 'MyValue', $configBuilder->build()->get( 'MySetting' ) ); $configBuilder->set( 'MySetting', 'MyOtherValue' );