Add a new type of database to the installer from extension
Decouple Installer services Implement injection class Autoloader and i18n messages from extension.json Implement extension selector by type Add i18n message key `version-database` Extensions for testing: - https://github.com/MWStake/PerconaDB - real Percona extension - https://github.com/killev/mediawiki-dbext2 - fake extension for test Bug: T226857, T255151 Change-Id: I9ec8a18ad19283f6be67ac000110ac370afc0815
This commit is contained in:
parent
52f1bfa61b
commit
46eabe275c
17 changed files with 629 additions and 87 deletions
|
|
@ -237,6 +237,8 @@ For notes on 1.34.x and older releases, see HISTORY.
|
|||
Doing so using that hook is now soft deprecated.
|
||||
* A new BlockPermissionChecker service was introduced for checking
|
||||
block-related permissions.
|
||||
* The support of 'database' type of extensions has been added to allow 3d party
|
||||
databases like Percona be used as storage. See T226857, T253248.
|
||||
* …
|
||||
|
||||
=== External library changes in 1.35 ===
|
||||
|
|
@ -1243,6 +1245,8 @@ because of Phabricator reports.
|
|||
instead.
|
||||
* Using Skin::addToBodyAttributes() method to add body attributes has been
|
||||
deprecated. Use OutputPageBodyAttributes hook instead.
|
||||
* Installer::getDBTypes has been hard deprecated in favor of
|
||||
InstallerDBSupport::getDatabases
|
||||
* …
|
||||
|
||||
=== Other changes in 1.35 ===
|
||||
|
|
|
|||
|
|
@ -820,6 +820,29 @@ class LocalisationCache {
|
|||
return $used;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $messagesDirs = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $additionalMessagesDirs = [];
|
||||
|
||||
/**
|
||||
* Add additional path for looking for i18n localization messages
|
||||
*
|
||||
* @internal for Installer with DbType extensions
|
||||
* @since 1.35
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
*/
|
||||
public function addMessagesDir( string $name, string $path ) : void {
|
||||
$this->additionalMessagesDirs[$name] = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the combined list of messages dirs from
|
||||
* core and extensions
|
||||
|
|
@ -837,7 +860,7 @@ class LocalisationCache {
|
|||
'rest' => "$IP/includes/Rest/i18n",
|
||||
'oojs-ui' => "$IP/resources/lib/ooui/i18n",
|
||||
'paramvalidator' => "$IP/includes/libs/ParamValidator/i18n",
|
||||
] + $this->options->get( 'MessagesDirs' );
|
||||
] + $this->options->get( 'MessagesDirs' ) + $this->additionalMessagesDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
* @file
|
||||
* @ingroup Installer
|
||||
*/
|
||||
use MediaWiki\Installer\Services\InstallerDBSupport;
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use Wikimedia\Rdbms\IDatabase;
|
||||
use Wikimedia\Rdbms\IMaintainableDatabase;
|
||||
|
|
@ -195,8 +196,8 @@ abstract class DatabaseUpdater {
|
|||
Maintenance $maintenance = null
|
||||
) {
|
||||
$type = $db->getType();
|
||||
if ( in_array( $type, Installer::getDBTypes() ) ) {
|
||||
$class = ucfirst( $type ) . 'Updater';
|
||||
if ( InstallerDBSupport::getInstance()->hasDatabase( $type ) ) {
|
||||
$class = InstallerDBSupport::getInstance()->getDBUpdaterClass( $type );
|
||||
|
||||
return new $class( $db, $shared, $maintenance );
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
* @ingroup Installer
|
||||
*/
|
||||
|
||||
use MediaWiki\Installer\Services\InstallerDBSupport;
|
||||
use MediaWiki\Interwiki\NullInterwikiLookup;
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use MediaWiki\Shell\Shell;
|
||||
|
|
@ -100,19 +101,9 @@ abstract class Installer {
|
|||
protected $parserOptions;
|
||||
|
||||
/**
|
||||
* Known database types. These correspond to the class names <type>Installer,
|
||||
* and are also MediaWiki database types valid for $wgDBtype.
|
||||
*
|
||||
* To add a new type, create a <type>Installer class and a Database<type>
|
||||
* class, and add a config-type-<type> message to MessagesEn.php.
|
||||
*
|
||||
* @var array
|
||||
* @var InstallerDBSupport
|
||||
*/
|
||||
protected static $dbTypes = [
|
||||
'mysql',
|
||||
'postgres',
|
||||
'sqlite',
|
||||
];
|
||||
protected $installerDbSupport;
|
||||
|
||||
/**
|
||||
* A list of environment check methods called by doEnvironmentChecks().
|
||||
|
|
@ -406,7 +397,6 @@ abstract class Installer {
|
|||
*/
|
||||
public function __construct() {
|
||||
global $wgMemc, $wgUser, $wgObjectCaches;
|
||||
|
||||
$defaultConfig = new GlobalVarConfig(); // all the stuff from DefaultSettings.php
|
||||
$installerConfig = self::getInstallerConfig( $defaultConfig );
|
||||
|
||||
|
|
@ -458,7 +448,9 @@ abstract class Installer {
|
|||
$this->doEnvironmentPreps();
|
||||
|
||||
$this->compiledDBs = [];
|
||||
foreach ( self::getDBTypes() as $type ) {
|
||||
$this->installerDbSupport = InstallerDBSupport::getInstance();
|
||||
|
||||
foreach ( $this->installerDbSupport->getDatabases() as $type ) {
|
||||
$installer = $this->getDBInstaller( $type );
|
||||
|
||||
if ( !$installer->isCompiled() ) {
|
||||
|
|
@ -475,11 +467,13 @@ abstract class Installer {
|
|||
|
||||
/**
|
||||
* Get a list of known DB types.
|
||||
* @deprecated since 1.35 use InstallerDBSupport::getDatabases instead
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getDBTypes() {
|
||||
return self::$dbTypes;
|
||||
wfDeprecated( __METHOD__, '1.35' );
|
||||
return InstallerDBSupport::getInstance()->getDatabases();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -561,13 +555,16 @@ abstract class Installer {
|
|||
|
||||
/**
|
||||
* Get the DatabaseInstaller class name for this type
|
||||
* @deprecated since 1.35 use InstallerDBSupport instead
|
||||
*
|
||||
* @param string $type database type ($wgDBtype)
|
||||
* @return string Class name
|
||||
* @since 1.30
|
||||
*/
|
||||
public static function getDBInstallerClass( $type ) {
|
||||
return ucfirst( $type ) . 'Installer';
|
||||
wfDeprecated( __METHOD__, '1.35' );
|
||||
|
||||
return InstallerDBSupport::getInstance()->getDBInstallerClass( $type );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -582,10 +579,8 @@ abstract class Installer {
|
|||
$type = $this->getVar( 'wgDBtype' );
|
||||
}
|
||||
|
||||
$type = strtolower( $type );
|
||||
|
||||
if ( !isset( $this->dbInstallers[$type] ) ) {
|
||||
$class = self::getDBInstallerClass( $type );
|
||||
$class = $this->installerDbSupport->getDBInstallerClass( $type );
|
||||
$this->dbInstallers[$type] = new $class( $this );
|
||||
}
|
||||
|
||||
|
|
@ -769,7 +764,7 @@ abstract class Installer {
|
|||
$allNames = [];
|
||||
|
||||
// Messages: config-type-mysql, config-type-postgres, config-type-sqlite
|
||||
foreach ( self::getDBTypes() as $name ) {
|
||||
foreach ( $this->installerDbSupport->getDatabases() as $name ) {
|
||||
$allNames[] = wfMessage( "config-type-$name" )->text();
|
||||
}
|
||||
|
||||
|
|
|
|||
336
includes/installer/Services/InstallerDBSupport.php
Normal file
336
includes/installer/Services/InstallerDBSupport.php
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @file
|
||||
* @ingroup Installer
|
||||
*
|
||||
* @author Art Baltai
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
namespace MediaWiki\Installer\Services;
|
||||
|
||||
use DatabaseInstaller;
|
||||
use DatabaseUpdater;
|
||||
use InvalidArgumentException;
|
||||
use MediaWiki\Logger\LoggerFactory;
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use MysqlInstaller;
|
||||
use MysqlUpdater;
|
||||
use PostgresInstaller;
|
||||
use PostgresUpdater;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use SqliteInstaller;
|
||||
use SqliteUpdater;
|
||||
use Wikimedia\Rdbms\Database;
|
||||
use Wikimedia\Rdbms\DatabaseMysqli;
|
||||
use Wikimedia\Rdbms\DatabasePostgres;
|
||||
use Wikimedia\Rdbms\DatabaseSqlite;
|
||||
|
||||
/**
|
||||
* @since 1.35
|
||||
*/
|
||||
class InstallerDBSupport {
|
||||
public const EXTENSION_TYPE_DATABASE = 'database';
|
||||
|
||||
/**
|
||||
* @var InstallerDBSupport $instance
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Known database types. These correspond to the class names <type>Installer,
|
||||
* and are also MediaWiki database types valid for $wgDBtype.
|
||||
*
|
||||
* To add a new type, create a <type>Installer class and a Database<type>
|
||||
* class, and add a config-type-<type> message to MessagesEn.php.
|
||||
*
|
||||
* @var array<string, array>
|
||||
*/
|
||||
private $databaseInfo = [
|
||||
'mysql' => [
|
||||
'installer' => MysqlInstaller::class,
|
||||
'updater' => MysqlUpdater::class,
|
||||
'driver' => DatabaseMysqli::class,
|
||||
'extension' => 'core'
|
||||
],
|
||||
'postgres' => [
|
||||
'installer' => PostgresInstaller::class,
|
||||
'updater' => PostgresUpdater::class,
|
||||
'driver' => DatabasePostgres::class,
|
||||
'extension' => 'core'
|
||||
],
|
||||
'sqlite' => [
|
||||
'installer' => SqliteInstaller::class,
|
||||
'updater' => SqliteUpdater::class,
|
||||
'driver' => DatabaseSqlite::class,
|
||||
'extension' => 'core'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var InstallerExtensionRegistration
|
||||
*/
|
||||
private $extensionRegistration;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
public static function getInstance(): self {
|
||||
global $IP, $wgExtensionDirectory;
|
||||
if ( !isset( self::$instance ) ) {
|
||||
$extensionDir = $wgExtensionDirectory ?: "$IP/extensions";
|
||||
$installerRegistration = new InstallerExtensionRegistration(
|
||||
$extensionDir,
|
||||
MediaWikiServices::getInstance()->getLocalisationCache()
|
||||
);
|
||||
self::$instance = new InstallerDBSupport(
|
||||
$installerRegistration,
|
||||
LoggerFactory::getInstance( 'Installer' )
|
||||
);
|
||||
self::$instance->registerDbExtensions(
|
||||
( new InstallerExtensionSelector( $extensionDir ) )
|
||||
->getExtOptionsByType( self::EXTENSION_TYPE_DATABASE )
|
||||
);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __construct(
|
||||
InstallerExtensionRegistration $extensionRegistration,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->extensionRegistration = $extensionRegistration;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
private function registerDbExtensions( iterable $extDbOptionsByType ): bool {
|
||||
$registered = false;
|
||||
foreach ( $extDbOptionsByType as $extensionName => $extDbOptions ) {
|
||||
$result = $this->registerDbExtension(
|
||||
$extensionName,
|
||||
$extDbOptions
|
||||
);
|
||||
$registered = $result || $registered;
|
||||
}
|
||||
|
||||
return $registered;
|
||||
}
|
||||
|
||||
private function registerDbExtension(
|
||||
string $extensionName,
|
||||
array $extJsonOptions
|
||||
): bool {
|
||||
if ( !isset( $extJsonOptions['Providers']['Databases'] ) ) {
|
||||
return false;
|
||||
}
|
||||
$newDatabases = $extJsonOptions['Providers']['Databases'];
|
||||
$isRegistred = false;
|
||||
foreach ( $newDatabases as $database => $options ) {
|
||||
if ( is_numeric( $database ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( $isRegistred === false ) {
|
||||
$this->extensionRegistration->register(
|
||||
$extensionName,
|
||||
$extJsonOptions
|
||||
);
|
||||
$isRegistred = true;
|
||||
}
|
||||
|
||||
$this->registerDatabase(
|
||||
$database,
|
||||
$options['Installer'] ?? $this->getInstallerClassAuto( $database ),
|
||||
$options['Updater'] ?? $this->getUpdaterClassAuto( $database ),
|
||||
$options['Driver'] ?? $this->getDriverClassAuto( $database ),
|
||||
$extensionName
|
||||
);
|
||||
}
|
||||
|
||||
return $isRegistred;
|
||||
}
|
||||
|
||||
private function isDatabaseInfoValid(
|
||||
string $dbInstaller,
|
||||
string $dbUpdater,
|
||||
string $dbDriver
|
||||
): bool {
|
||||
$isValid = true;
|
||||
if ( !is_subclass_of( $dbInstaller, DatabaseInstaller::class ) ) {
|
||||
$this->logger->warning( 'Database `Installer` should be a subclass of '
|
||||
. DatabaseInstaller::class );
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
if ( !is_subclass_of( $dbUpdater, DatabaseUpdater::class ) ) {
|
||||
$this->logger->warning( 'Database `Updater` should be a subclass of '
|
||||
. DatabaseUpdater::class );
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
if ( !is_subclass_of( $dbDriver, Database::class ) ) {
|
||||
$this->logger->warning( 'Database `Driver` should be a subclass of ' . Database::class );
|
||||
$isValid = false;
|
||||
}
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $database
|
||||
* @param string $dbInstaller
|
||||
* @param string $dbUpdater
|
||||
* @param string $dbDriver
|
||||
* @param string $extensionName
|
||||
*/
|
||||
private function registerDatabase(
|
||||
string $database,
|
||||
string $dbInstaller,
|
||||
string $dbUpdater,
|
||||
string $dbDriver,
|
||||
string $extensionName
|
||||
): void {
|
||||
if ( !$this->isDatabaseInfoValid( $dbInstaller, $dbUpdater, $dbDriver ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->databaseInfo[ strtolower( $database ) ] = [
|
||||
'installer' => $dbInstaller,
|
||||
'updater' => $dbUpdater,
|
||||
'driver' => $dbDriver,
|
||||
'extension' => $extensionName,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of known DB types.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDatabases(): array {
|
||||
return array_keys( $this->databaseInfo );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks wheather given database type is registered
|
||||
* @param string $database
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDatabase( string $database ): bool {
|
||||
return array_key_exists( $database, $this->databaseInfo );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database installer class for given database type, throws an
|
||||
* InvalidArgumentException if no given database registerred
|
||||
*
|
||||
* @param string $database
|
||||
* @return string Class name
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getDBInstallerClass( string $database ): string {
|
||||
if ( !isset( $this->databaseInfo[strtolower( $database )] ) ) {
|
||||
throw new InvalidArgumentException( __METHOD__ .
|
||||
" no registered database found for type '$database'" );
|
||||
}
|
||||
return $this->databaseInfo[strtolower( $database )]['installer'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database updater class for given database type, throws an
|
||||
* InvalidArgumentException if no given database registerred
|
||||
*
|
||||
* @param string $database
|
||||
* @return string class name
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getDBUpdaterClass( string $database ): string {
|
||||
if ( !isset( $this->databaseInfo[strtolower( $database )] ) ) {
|
||||
throw new InvalidArgumentException( __METHOD__ .
|
||||
" no registered database found for type '$database'" );
|
||||
}
|
||||
return $this->databaseInfo[strtolower( $database )]['updater'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database driver class for given database type, throws an
|
||||
* InvalidArgumentException if no given database registerred
|
||||
*
|
||||
* @param string $database
|
||||
* @return string class name
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getDBDriverClass( string $database ): string {
|
||||
if ( !isset( $this->databaseInfo[strtolower( $database )] ) ) {
|
||||
throw new InvalidArgumentException( __METHOD__ .
|
||||
" no registered database found for type '$database'" );
|
||||
}
|
||||
return $this->databaseInfo[strtolower( $database )]['driver'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets extension name that implements classes for given database type, throws an
|
||||
* InvalidArgumentException if no given database registerred
|
||||
*
|
||||
* @param string $database
|
||||
* @return string|null
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getExtensionNameForDatabase(
|
||||
string $database
|
||||
): string {
|
||||
if ( !isset( $this->databaseInfo[strtolower( $database )] ) ) {
|
||||
throw new InvalidArgumentException( __METHOD__ .
|
||||
" no registered database found for type '$database'" );
|
||||
}
|
||||
return $this->databaseInfo[strtolower( $database )]['extension'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate default name for database Installer
|
||||
*
|
||||
* @param string $type database type ($wgDBtype)
|
||||
* @return string Class name
|
||||
*/
|
||||
private function getInstallerClassAuto( string $type ): string {
|
||||
return ucfirst( $type ) . 'Installer';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate default name for database Updater
|
||||
*
|
||||
* @param string $type database type ($wgDBtype)
|
||||
* @return string Class name
|
||||
*/
|
||||
private function getUpdaterClassAuto( string $type ): string {
|
||||
return ucfirst( $type ) . 'Updater';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate default name for database Driver
|
||||
*
|
||||
* @param string $type database type ($wgDBtype)
|
||||
* @return string Class name
|
||||
*/
|
||||
private function getDriverClassAuto( string $type ): string {
|
||||
return 'Database' . ucfirst( $type );
|
||||
}
|
||||
}
|
||||
110
includes/installer/Services/InstallerExtensionRegistration.php
Normal file
110
includes/installer/Services/InstallerExtensionRegistration.php
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @file
|
||||
* @ingroup Installer
|
||||
*
|
||||
* @author Art Baltai
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
namespace MediaWiki\Installer\Services;
|
||||
|
||||
use ExtensionRegistry;
|
||||
use LocalisationCache;
|
||||
|
||||
/**
|
||||
* @since 1.35
|
||||
*/
|
||||
class InstallerExtensionRegistration {
|
||||
|
||||
/** @var string */
|
||||
private $extensionDir;
|
||||
|
||||
/** @var LocalisationCache */
|
||||
private $localisationCache;
|
||||
|
||||
public function __construct( string $extensionDir, LocalisationCache $localisationCache ) {
|
||||
$this->extensionDir = $extensionDir;
|
||||
$this->localisationCache = $localisationCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register class autoloader, i18n messages
|
||||
* @param string $extensionName
|
||||
* @param array $extJsonOptions
|
||||
*/
|
||||
public function register(
|
||||
string $extensionName,
|
||||
array $extJsonOptions
|
||||
): void {
|
||||
$this->registerClassAutoloader( $extensionName, $extJsonOptions );
|
||||
$this->registerMessagesDirs( $extensionName, $extJsonOptions );
|
||||
}
|
||||
|
||||
public function registerClassAutoloader(
|
||||
string $extensionName,
|
||||
array $extJsonOptions
|
||||
): void {
|
||||
global $wgAutoloadLocalClasses;
|
||||
|
||||
$extPath = $this->getExtensionPath( $extensionName );
|
||||
ExtensionRegistry::exportAutoloadClassesAndNamespaces(
|
||||
$extPath,
|
||||
$extJsonOptions
|
||||
);
|
||||
// important for upgrade (mw-config/?page=ExistingWiki) with existing LocalSettings.php
|
||||
if (
|
||||
!empty( $extJsonOptions['AutoloadClasses'] )
|
||||
&& is_array( $extJsonOptions['AutoloadClasses'] )
|
||||
) {
|
||||
foreach ( $extJsonOptions['AutoloadClasses'] as $extensionName => $path ) {
|
||||
$wgAutoloadLocalClasses[$extensionName] = "$extPath/$path";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function registerMessagesDirs(
|
||||
string $extensionName,
|
||||
array $extJsonOptions
|
||||
): void {
|
||||
if ( !is_array( $extJsonOptions['MessagesDirs'] ?? null ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $extJsonOptions['MessagesDirs'] as $messageType => $messagesDirs ) {
|
||||
$messagesDirs = (array)$messagesDirs;
|
||||
foreach ( $messagesDirs as $key => $messageDir ) {
|
||||
$i18nDir = $this->getExtensionPath( $extensionName ) . "/$messageDir";
|
||||
if ( !is_dir( $i18nDir ) ) {
|
||||
continue;
|
||||
}
|
||||
$this->localisationCache->addMessagesDir(
|
||||
count( $messagesDirs ) === 1
|
||||
? $messageType
|
||||
: "{$messageType}_{$key}",
|
||||
$i18nDir
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getExtensionPath( string $extensionName ) : string {
|
||||
return "{$this->extensionDir}/{$extensionName}";
|
||||
}
|
||||
}
|
||||
104
includes/installer/Services/InstallerExtensionSelector.php
Normal file
104
includes/installer/Services/InstallerExtensionSelector.php
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @file
|
||||
* @ingroup Installer
|
||||
*
|
||||
* @author Art Baltai
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
namespace MediaWiki\Installer\Services;
|
||||
|
||||
use Generator;
|
||||
|
||||
/**
|
||||
* @since 1.35
|
||||
*/
|
||||
class InstallerExtensionSelector {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $extensionDir;
|
||||
|
||||
/**
|
||||
* @var string[]|null
|
||||
*/
|
||||
private $extensionJsonPath;
|
||||
|
||||
public function __construct( string $extensionDir ) {
|
||||
$this->extensionDir = $extensionDir;
|
||||
}
|
||||
|
||||
private function getExtensionJsonPaths() : array {
|
||||
if ( $this->extensionJsonPath === null ) {
|
||||
$this->extensionJsonPath = [];
|
||||
if ( is_dir( $this->extensionDir ) ) {
|
||||
$dh = opendir( $this->extensionDir );
|
||||
while ( ( $extension = readdir( $dh ) ) !== false ) {
|
||||
if ( mb_substr( $extension, 0, 1 ) === '.' ) {
|
||||
continue;
|
||||
}
|
||||
$jsonPath = "{$this->extensionDir}/{$extension}/extension.json";
|
||||
if ( is_file( $jsonPath ) && is_readable( $jsonPath ) ) {
|
||||
$this->extensionJsonPath[$extension] = $jsonPath;
|
||||
}
|
||||
}
|
||||
closedir( $dh );
|
||||
|
||||
uksort( $this->extensionJsonPath, 'strnatcasecmp' );
|
||||
} // @todo else:warning?
|
||||
}
|
||||
|
||||
return $this->extensionJsonPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Generator for interating through all exntension.json files converterd
|
||||
* into array. Key is string of exntension name and value is decoded exntension.json.
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
private function getExtensionJsons() : Generator {
|
||||
foreach ( $this->getExtensionJsonPaths() as $extension => $jsonPath ) {
|
||||
$json = file_get_contents( $jsonPath );
|
||||
$options = json_decode( $json, true );
|
||||
if ( !is_array( $options ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
yield $extension => $options;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Generator for interating through extension's options
|
||||
* filtered by certain type.
|
||||
*
|
||||
* @param string $type
|
||||
* @return Generator
|
||||
*/
|
||||
public function getExtOptionsByType( string $type ) : Generator {
|
||||
foreach ( $this->getExtensionJsons() as $extensionName => $options ) {
|
||||
if ( $type === ( $options['type'] ?? null ) ) {
|
||||
yield $extensionName => $options;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
* @file
|
||||
* @ingroup Installer
|
||||
*/
|
||||
use MediaWiki\Installer\Services\InstallerDBSupport;
|
||||
|
||||
class WebInstallerDBConnect extends WebInstallerPage {
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ class WebInstallerDBConnect extends WebInstallerPage {
|
|||
|
||||
// Messages: config-dbsupport-mysql, config-dbsupport-postgres, config-dbsupport-sqlite
|
||||
$dbSupport = '';
|
||||
foreach ( Installer::getDBTypes() as $type ) {
|
||||
foreach ( InstallerDBSupport::getInstance()->getDatabases() as $type ) {
|
||||
$dbSupport .= wfMessage( "config-dbsupport-$type" )->plain() . "\n";
|
||||
}
|
||||
$this->addHTML( $this->parent->getInfoBox(
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
* @ingroup Installer
|
||||
*/
|
||||
|
||||
use MediaWiki\Installer\Services\InstallerDBSupport;
|
||||
|
||||
class WebInstallerExistingWiki extends WebInstallerPage {
|
||||
|
||||
/**
|
||||
|
|
@ -140,7 +142,7 @@ class WebInstallerExistingWiki extends WebInstallerPage {
|
|||
protected function handleExistingUpgrade( $vars ) {
|
||||
// Check $wgDBtype
|
||||
if ( !isset( $vars['wgDBtype'] ) ||
|
||||
!in_array( $vars['wgDBtype'], Installer::getDBTypes() )
|
||||
!InstallerDBSupport::getInstance()->hasDatabase( $vars['wgDBtype'] )
|
||||
) {
|
||||
return Status::newFatal( 'config-localsettings-connection-error', '' );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
* @ingroup Installer
|
||||
*/
|
||||
|
||||
use MediaWiki\Installer\Services\InstallerDBSupport;
|
||||
use Wikimedia\IPUtils;
|
||||
|
||||
class WebInstallerOptions extends WebInstallerPage {
|
||||
|
|
@ -147,7 +148,14 @@ class WebInstallerOptions extends WebInstallerPage {
|
|||
$this->getFieldsetEnd();
|
||||
$this->addHTML( $skinHtml );
|
||||
|
||||
$extensions = $this->parent->findExtensions()->value;
|
||||
$extensions = array_filter(
|
||||
$this->parent->findExtensions()->value,
|
||||
function ( $extensionInfo ) {
|
||||
return ( $extensionInfo['type'] ?? null )
|
||||
!== InstallerDBSupport::EXTENSION_TYPE_DATABASE;
|
||||
}
|
||||
);
|
||||
|
||||
'@phan-var array[] $extensions';
|
||||
$dependencyMap = [];
|
||||
|
||||
|
|
@ -182,7 +190,7 @@ class WebInstallerOptions extends WebInstallerPage {
|
|||
'class' => 'config-ext-input'
|
||||
];
|
||||
$labelAttribs = [];
|
||||
$fullDepList = [];
|
||||
|
||||
if ( isset( $info['requires']['extensions'] ) ) {
|
||||
$dependencyMap[$ext]['extensions'] = $info['requires']['extensions'];
|
||||
$labelAttribs['class'] = 'mw-ext-with-dependencies';
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ use Exception;
|
|||
use HashBagOStuff;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use MediaWiki\Installer\Services\InstallerDBSupport;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
|
@ -392,7 +393,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
|
|||
* @since 1.18
|
||||
*/
|
||||
final public static function factory( $type, $params = [], $connect = self::NEW_CONNECTED ) {
|
||||
$class = self::getClass( $type, $params['driver'] ?? null );
|
||||
$class = InstallerDBSupport::getInstance()->getDBDriverClass( $type );
|
||||
|
||||
if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
|
||||
$params += [
|
||||
|
|
@ -453,64 +454,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
|
|||
self::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
|
||||
];
|
||||
|
||||
$class = self::getClass( $dbType, $driver );
|
||||
$class = InstallerDBSupport::getInstance()->getDBDriverClass( $dbType );
|
||||
|
||||
return call_user_func( [ $class, 'getAttributes' ] ) + $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dbType A possible DB type (sqlite, mysql, postgres,...)
|
||||
* @param string|null $driver Optional name of a specific DB client driver
|
||||
* @return string Database subclass name to use
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private static function getClass( $dbType, $driver = null ) {
|
||||
// For database types with built-in support, the below maps type to IDatabase
|
||||
// implementations. For types with multiple driver implementations (PHP extensions),
|
||||
// an array can be used, keyed by extension name. In case of an array, the
|
||||
// optional 'driver' parameter can be used to force a specific driver. Otherwise,
|
||||
// we auto-detect the first available driver. For types without built-in support,
|
||||
// an class named "Database<Type>" us used, eg. DatabaseFoo for type 'foo'.
|
||||
static $builtinTypes = [
|
||||
'mysql' => [ 'mysqli' => DatabaseMysqli::class ],
|
||||
'sqlite' => DatabaseSqlite::class,
|
||||
'postgres' => DatabasePostgres::class,
|
||||
];
|
||||
|
||||
$dbType = strtolower( $dbType );
|
||||
$class = false;
|
||||
|
||||
if ( isset( $builtinTypes[$dbType] ) ) {
|
||||
$possibleDrivers = $builtinTypes[$dbType];
|
||||
if ( is_string( $possibleDrivers ) ) {
|
||||
$class = $possibleDrivers;
|
||||
} elseif ( (string)$driver !== '' ) {
|
||||
if ( !isset( $possibleDrivers[$driver] ) ) {
|
||||
throw new InvalidArgumentException( __METHOD__ .
|
||||
" type '$dbType' does not support driver '{$driver}'" );
|
||||
}
|
||||
|
||||
$class = $possibleDrivers[$driver];
|
||||
} else {
|
||||
foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
|
||||
if ( extension_loaded( $posDriver ) ) {
|
||||
$class = $possibleClass;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$class = 'Database' . ucfirst( $dbType );
|
||||
}
|
||||
|
||||
if ( $class === false ) {
|
||||
throw new InvalidArgumentException( __METHOD__ .
|
||||
" no viable database extension found for type '$dbType'" );
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Map of (Database::ATTR_* constant => value)
|
||||
* @since 1.31
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
|
||||
use MediaWiki\ExtensionInfo;
|
||||
use MediaWiki\Installer\Services\InstallerDBSupport;
|
||||
use MediaWiki\MediaWikiServices;
|
||||
|
||||
/**
|
||||
|
|
@ -416,6 +417,8 @@ class SpecialVersion extends SpecialPage {
|
|||
'antispam' => wfMessage( 'version-antispam' )->text(),
|
||||
'skin' => wfMessage( 'version-skins' )->text(),
|
||||
'api' => wfMessage( 'version-api' )->text(),
|
||||
InstallerDBSupport::EXTENSION_TYPE_DATABASE =>
|
||||
wfMessage( 'version-database' )->text(),
|
||||
'other' => wfMessage( 'version-other' )->text(),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -3561,6 +3561,7 @@
|
|||
"version-variables": "Variables",
|
||||
"version-editors": "Editors",
|
||||
"version-antispam": "Spam prevention",
|
||||
"version-database": "Custom database support",
|
||||
"version-api": "API",
|
||||
"version-other": "Other",
|
||||
"version-mediahandlers": "Media handlers",
|
||||
|
|
|
|||
|
|
@ -3778,6 +3778,7 @@
|
|||
"version-antispam": "Part of [[Special:Version]].\nThis message is followed by the list of SPAM prevention extensions.",
|
||||
"version-api": "{{optional}}",
|
||||
"version-other": "{{Identical|Other}}",
|
||||
"version-database": "Database type",
|
||||
"version-mediahandlers": "Used in [[Special:Version]]. It is the title of a section for media handler extensions (e.g. [[mw:Extension:OggHandler]]).\nThere are no such extensions here, so look at [[wikipedia:Special:Version]] for an example.",
|
||||
"version-hooks": "Shown in [[Special:Version]]\n{{Identical|Hook}}",
|
||||
"version-parser-extensiontags": "Part of [[Special:Version]].\nThis message is followed by the list of parser extension tags like <code><nowiki><charinsert></nowiki></code>, <code><nowiki><coordinates></nowiki></code>, etc.",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
require_once __DIR__ . '/Maintenance.php';
|
||||
|
||||
use MediaWiki\Installer\Services\InstallerDBSupport;
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use Wikimedia\Rdbms\DatabaseSqlite;
|
||||
|
||||
|
|
@ -141,8 +142,9 @@ class UpdateMediaWiki extends Maintenance {
|
|||
$db = $this->getDB( DB_MASTER );
|
||||
|
||||
# Check to see whether the database server meets the minimum requirements
|
||||
/** @var DatabaseInstaller $dbInstallerClass */
|
||||
$dbInstallerClass = Installer::getDBInstallerClass( $db->getType() );
|
||||
/** @var string|DatabaseInstaller $dbInstallerClass */
|
||||
$dbInstallerClass = InstallerDBSupport::getInstance()
|
||||
->getDBInstallerClass( $db->getType() );
|
||||
$status = $dbInstallerClass::meetsMinimumRequirement( $db->getServerVersion() );
|
||||
if ( !$status->isOK() ) {
|
||||
// This might output some wikitext like <strong> but it should be comprehensible
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ class DatabaseTest extends PHPUnit\Framework\TestCase {
|
|||
$m = Database::NEW_UNCONNECTED; // no-connect mode
|
||||
$p = [ 'host' => 'localhost', 'user' => 'me', 'password' => 'myself', 'dbname' => 'i' ];
|
||||
|
||||
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'mysqli', $p, $m ) );
|
||||
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySqli', $p, $m ) );
|
||||
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySQLi', $p, $m ) );
|
||||
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'mysql', $p, $m ) );
|
||||
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySql', $p, $m ) );
|
||||
$this->assertInstanceOf( DatabaseMysqli::class, Database::factory( 'MySQL', $p, $m ) );
|
||||
$this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'postgres', $p, $m ) );
|
||||
$this->assertInstanceOf( DatabasePostgres::class, Database::factory( 'Postgres', $p, $m ) );
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@
|
|||
<testsuite name="languages">
|
||||
<directory>languages</directory>
|
||||
</testsuite>
|
||||
<testsuite name="rdbms">
|
||||
<directory>includes/libs/rdbms</directory>
|
||||
</testsuite>
|
||||
<testsuite name="parsertests">
|
||||
<file>suites/CoreParserTestSuite.php</file>
|
||||
<file>suites/ExtensionsParserTestSuite.php</file>
|
||||
|
|
|
|||
Loading…
Reference in a new issue