Merge "Simplify boolean logic for Special:Block widgets"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 16 Jan 2019 20:17:16 +0000 (20:17 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 16 Jan 2019 20:17:16 +0000 (20:17 +0000)
97 files changed:
RELEASE-NOTES-1.33
autoload.php
includes/AutoLoader.php
includes/Block.php
includes/HistoryBlob.php
includes/MediaWikiServices.php
includes/Revision.php
includes/Setup.php
includes/api/ApiQueryAllUsers.php
includes/api/ApiQueryBase.php
includes/api/ApiQueryContributors.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryPageProps.php
includes/api/ApiQueryUserContribs.php
includes/clientpool/SquidPurgeClientPool.php
includes/config/ConfigFactory.php
includes/config/ConfigRepository.php
includes/db/DatabaseOracle.php
includes/diff/TextSlotDiffRenderer.php
includes/editpage/TextboxBuilder.php
includes/exception/MWUnknownContentModelException.php
includes/filerepo/LocalRepo.php
includes/filerepo/file/ArchivedFile.php
includes/installer/Installer.php
includes/jobqueue/JobQueue.php
includes/jobqueue/JobQueueDB.php
includes/jobqueue/JobQueueGroup.php
includes/jobqueue/JobQueueRedis.php
includes/libs/filebackend/FileBackend.php
includes/libs/filebackend/filejournal/FileJournal.php
includes/libs/lockmanager/FSLockManager.php
includes/libs/lockmanager/PostgreSqlLockManager.php
includes/libs/lockmanager/QuorumLockManager.php
includes/libs/mime/XmlTypeCheck.php
includes/libs/rdbms/ChronologyProtector.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabaseMysqlBase.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/libs/services/CannotReplaceActiveServiceException.php [new file with mode: 0644]
includes/libs/services/ContainerDisabledException.php [new file with mode: 0644]
includes/libs/services/DestructibleService.php [new file with mode: 0644]
includes/libs/services/NoSuchServiceException.php [new file with mode: 0644]
includes/libs/services/SalvageableService.php [new file with mode: 0644]
includes/libs/services/ServiceAlreadyDefinedException.php [new file with mode: 0644]
includes/libs/services/ServiceContainer.php [new file with mode: 0644]
includes/libs/services/ServiceDisabledException.php [new file with mode: 0644]
includes/mail/EmailNotification.php
includes/parser/ParserOptions.php
includes/parser/Preprocessor_DOM.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderImage.php
includes/search/SearchOracle.php
includes/search/SearchSqlite.php
includes/services/CannotReplaceActiveServiceException.php [deleted file]
includes/services/ContainerDisabledException.php [deleted file]
includes/services/DestructibleService.php [deleted file]
includes/services/NoSuchServiceException.php [deleted file]
includes/services/SalvageableService.php [deleted file]
includes/services/ServiceAlreadyDefinedException.php [deleted file]
includes/services/ServiceContainer.php [deleted file]
includes/services/ServiceDisabledException.php [deleted file]
includes/skins/Skin.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialListgrants.php
includes/specials/SpecialPasswordPolicies.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialSearch.php
includes/specials/SpecialSpecialpages.php
includes/specials/SpecialTrackingCategories.php
includes/specials/pagers/BlockListPager.php
includes/user/User.php
includes/widget/search/InterwikiSearchResultSetWidget.php
languages/data/ZhConversion.php
languages/messages/MessagesGom_deva.php
maintenance/language/zhtable/toCN.manual
maintenance/language/zhtable/toHK.manual
maintenance/language/zhtable/toSimp.manual
maintenance/language/zhtable/toTW.manual
maintenance/language/zhtable/toTrad.manual
maintenance/language/zhtable/trad2simp.manual
maintenance/language/zhtable/tradphrases.manual
tests/phpunit/data/resourceloader/def.svg
tests/phpunit/data/resourceloader/def_variantize.svg
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/Revision/RevisionStoreDbTestBase.php
tests/phpunit/includes/libs/services/ServiceContainerTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/services/TestWiring1.php [new file with mode: 0644]
tests/phpunit/includes/libs/services/TestWiring2.php [new file with mode: 0644]
tests/phpunit/includes/media/WebPTest.php
tests/phpunit/includes/poolcounter/PoolWorkArticleViewTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderLessVarFileModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/includes/services/ServiceContainerTest.php [deleted file]
tests/phpunit/includes/services/TestWiring1.php [deleted file]
tests/phpunit/includes/services/TestWiring2.php [deleted file]
tests/phpunit/includes/upload/UploadStashTest.php

index c0dd84f..5175667 100644 (file)
@@ -101,6 +101,8 @@ Below only new and removed languages are listed, as well as changes to languages
 because of Phabricator reports.
 
 * (T203908) Added language support for Eastern Pwo (kjp).
+* (T213717) Fixed a translation error on Goan Konkani (gom-deva) translations
+  for NS_TEMPLATE.
 
 === Breaking changes in 1.33 ===
 * The parameteter $lang in DifferenceEngine::setTextLanguage must be of type
@@ -201,6 +203,8 @@ because of Phabricator reports.
   Title::getUserPermissionsErrors() and Title::userCan(). Previously, the method
   was only called in Action::checkCanExecute(). Actions should ensure that their
   requiresUnblock() returns the proper result (the default is `true`).
+* (T211608) The MediaWiki\Services namespace has been renamed to
+  Wikimedia\Services. The old name is still supported, but deprecated.
 * …
 
 === Other changes in 1.33 ===
index 91be2e7..afc187f 100644 (file)
@@ -903,6 +903,14 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\OutputHandler' => __DIR__ . '/includes/OutputHandler.php',
        'MediaWiki\\ProcOpenError' => __DIR__ . '/includes/exception/ProcOpenError.php',
        'MediaWiki\\Search\\ParserOutputSearchDataExtractor' => __DIR__ . '/includes/search/ParserOutputSearchDataExtractor.php',
+       'MediaWiki\\Services\\CannotReplaceActiveServiceException' => __DIR__ . '/includes/libs/services/CannotReplaceActiveServiceException.php',
+       'MediaWiki\\Services\\ContainerDisabledException' => __DIR__ . '/includes/libs/services/ContainerDisabledException.php',
+       'MediaWiki\\Services\\DestructibleService' => __DIR__ . '/includes/libs/services/DestructibleService.php',
+       'MediaWiki\\Services\\NoSuchServiceException' => __DIR__ . '/includes/libs/services/NoSuchServiceException.php',
+       'MediaWiki\\Services\\SalvageableService' => __DIR__ . '/includes/libs/services/SalvageableService.php',
+       'MediaWiki\\Services\\ServiceAlreadyDefinedException' => __DIR__ . '/includes/libs/services/ServiceAlreadyDefinedException.php',
+       'MediaWiki\\Services\\ServiceContainer' => __DIR__ . '/includes/libs/services/ServiceContainer.php',
+       'MediaWiki\\Services\\ServiceDisabledException' => __DIR__ . '/includes/libs/services/ServiceDisabledException.php',
        'MediaWiki\\ShellDisabledError' => __DIR__ . '/includes/exception/ShellDisabledError.php',
        'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php',
        'MediaWiki\\Special\\SpecialPageFactory' => __DIR__ . '/includes/specialpage/SpecialPageFactory.php',
index 677fd01..9dbc9eb 100644 (file)
@@ -136,12 +136,12 @@ class AutoLoader {
                        'MediaWiki\\Linker\\' => __DIR__ . '/linker/',
                        'MediaWiki\\Preferences\\' => __DIR__ . '/preferences/',
                        'MediaWiki\\Revision\\' => __DIR__ . '/Revision/',
-                       'MediaWiki\\Services\\' => __DIR__ . '/services/',
                        'MediaWiki\\Session\\' => __DIR__ . '/session/',
                        'MediaWiki\\Shell\\' => __DIR__ . '/shell/',
                        'MediaWiki\\Sparql\\' => __DIR__ . '/sparql/',
                        'MediaWiki\\Storage\\' => __DIR__ . '/Storage/',
                        'MediaWiki\\Tidy\\' => __DIR__ . '/tidy/',
+                       'Wikimedia\\Services\\' => __DIR__ . '/libs/services/',
                ];
        }
 }
index fb3caf6..7138301 100644 (file)
@@ -1307,7 +1307,7 @@ class Block {
         * @since 1.22
         */
        public static function getBlocksForIPList( array $ipChain, $isAnon, $fromMaster = false ) {
-               if ( !count( $ipChain ) ) {
+               if ( $ipChain === [] ) {
                        return [];
                }
 
@@ -1332,7 +1332,7 @@ class Block {
                        $conds[] = self::getRangeCond( IP::toHex( $ipaddr ) );
                }
 
-               if ( !count( $conds ) ) {
+               if ( $conds === [] ) {
                        return [];
                }
 
@@ -1388,7 +1388,7 @@ class Block {
         * @return Block|null The "best" block from the list
         */
        public static function chooseBlock( array $blocks, array $ipChain ) {
-               if ( !count( $blocks ) ) {
+               if ( $blocks === [] ) {
                        return null;
                } elseif ( count( $blocks ) == 1 ) {
                        return $blocks[0];
index 1d4f6e4..bca6c7e 100644 (file)
@@ -445,8 +445,7 @@ class DiffHistoryBlob implements HistoryBlob {
                        // Already compressed
                        return;
                }
-               if ( !count( $this->mItems ) ) {
-                       // Empty
+               if ( $this->mItems === [] ) {
                        return;
                }
 
@@ -492,7 +491,7 @@ class DiffHistoryBlob implements HistoryBlob {
                $this->mDiffs = [];
                $this->mDiffMap = [];
                foreach ( $sequences as $seq ) {
-                       if ( !count( $seq['diffs'] ) ) {
+                       if ( $seq['diffs'] === [] ) {
                                continue;
                        }
                        if ( $tail === '' ) {
@@ -627,8 +626,7 @@ class DiffHistoryBlob implements HistoryBlob {
         */
        function __sleep() {
                $this->compress();
-               if ( !count( $this->mItems ) ) {
-                       // Empty object
+               if ( $this->mItems === [] ) {
                        $info = false;
                } else {
                        // Take forward differences to improve the compression ratio for sequences
index 0e36b22..4abd729 100644 (file)
@@ -36,9 +36,6 @@ use MediaHandlerFactory;
 use MediaWiki\Config\ConfigRepository;
 use MediaWiki\Linker\LinkRenderer;
 use MediaWiki\Linker\LinkRendererFactory;
-use MediaWiki\Services\SalvageableService;
-use MediaWiki\Services\ServiceContainer;
-use MediaWiki\Services\NoSuchServiceException;
 use MWException;
 use MimeAnalyzer;
 use ObjectCache;
@@ -58,6 +55,9 @@ use SkinFactory;
 use TitleFormatter;
 use TitleParser;
 use VirtualRESTServiceClient;
+use Wikimedia\Services\SalvageableService;
+use Wikimedia\Services\ServiceContainer;
+use Wikimedia\Services\NoSuchServiceException;
 use MediaWiki\Interwiki\InterwikiLookup;
 use MagicWordFactory;
 
index b0a3ba3..c99f88a 100644 (file)
@@ -846,6 +846,7 @@ class Revision implements IDBAccessObject {
                $user = $this->mRecord->getUser( $audience, $user );
                return $user ? $user->getName() : '';
        }
+
        /**
         * Fetch revision comment if it's available to the specified audience.
         * If the specified audience does not have access to the comment, an
index aba050d..4ebe426 100644 (file)
@@ -367,10 +367,9 @@ if ( $wgRCFilterByAge ) {
        // Note that we allow 1 link higher than the max for things like 56 days but a 60 day link.
        sort( $wgRCLinkDays );
 
-       // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall
-       for ( $i = 0; $i < count( $wgRCLinkDays ); $i++ ) {
-               if ( $wgRCLinkDays[$i] >= $rcMaxAgeDays ) {
-                       $wgRCLinkDays = array_slice( $wgRCLinkDays, 0, $i + 1, false );
+       foreach ( $wgRCLinkDays as $i => $days ) {
+               if ( $days >= $rcMaxAgeDays ) {
+                       array_splice( $wgRCLinkDays, $i + 1 );
                        break;
                }
        }
@@ -873,11 +872,19 @@ if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) {
 
        $session->renew();
        if ( MediaWiki\Session\PHPSessionHandler::isEnabled() &&
-               ( $session->isPersistent() || $session->shouldRememberUser() )
+               ( $session->isPersistent() || $session->shouldRememberUser() ) &&
+               session_id() !== $session->getId()
        ) {
                // Start the PHP-session for backwards compatibility
+               if ( session_id() !== '' ) {
+                       wfDebugLog( 'session', 'PHP session {old_id} was already started, changing to {new_id}', 'all', [
+                               'old_id' => session_id(),
+                               'new_id' => $session->getId(),
+                       ] );
+                       session_write_close();
+               }
                session_id( $session->getId() );
-               Wikimedia\quietCall( 'session_start' );
+               session_start();
        }
 
        unset( $session );
index 7d5f6e2..7b5df50 100644 (file)
@@ -94,7 +94,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
                        }
 
                        // no group with the given right(s) exists, no need for a query
-                       if ( !count( $groups ) ) {
+                       if ( $groups === [] ) {
                                $this->getResult()->addIndexedTagName( [ 'query', $this->getModuleName() ], '' );
 
                                return;
index d9fe50b..c92f037 100644 (file)
@@ -277,7 +277,7 @@ abstract class ApiQueryBase extends ApiBase {
                if ( count( $ids ) ) {
                        $ids = $this->filterIDs( [ [ $table, $field ] ], $ids );
 
-                       if ( !count( $ids ) ) {
+                       if ( $ids === [] ) {
                                // Return nothing, no IDs are valid
                                $this->where[] = '0 = 1';
                        } else {
index 642c9ac..a8f970e 100644 (file)
@@ -64,7 +64,7 @@ class ApiQueryContributors extends ApiQueryBase {
                                return $v >= $cont_page;
                        } );
                }
-               if ( !count( $pages ) ) {
+               if ( $pages === [] ) {
                        // Nothing to do
                        return;
                }
index 2ab3c56..8a54c0b 100644 (file)
@@ -721,7 +721,7 @@ class ApiQueryInfo extends ApiQueryBase {
                                $getTitles[] = $t->getTalkPage();
                        }
                }
-               if ( !count( $getTitles ) ) {
+               if ( $getTitles === [] ) {
                        return;
                }
 
@@ -751,7 +751,7 @@ class ApiQueryInfo extends ApiQueryBase {
 
                $pageIds = array_keys( $this->titles );
 
-               if ( !count( $pageIds ) ) {
+               if ( $pageIds === [] ) {
                        return;
                }
 
@@ -768,7 +768,7 @@ class ApiQueryInfo extends ApiQueryBase {
        }
 
        private function getVariantTitles() {
-               if ( !count( $this->titles ) ) {
+               if ( $this->titles === [] ) {
                        return;
                }
                $this->variantTitles = [];
index 2bee698..3258004 100644 (file)
@@ -50,7 +50,7 @@ class ApiQueryPageProps extends ApiQueryBase {
                        $pages = $filteredPages;
                }
 
-               if ( !count( $pages ) ) {
+               if ( $pages === [] ) {
                        # Nothing to do
                        return;
                }
index ed83130..6082617 100644 (file)
@@ -136,7 +136,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
                        // prepareQuery might try to sort by actor and confuse everything.
                        $batchSize = 1;
                } elseif ( isset( $this->params['userids'] ) ) {
-                       if ( !count( $this->params['userids'] ) ) {
+                       if ( $this->params['userids'] === [] ) {
                                $encParamName = $this->encodeParamName( 'userids' );
                                $this->dieWithError( [ 'apierror-paramempty', $encParamName ], "paramempty_$encParamName" );
                        }
index 5acac63..3a2a971 100644 (file)
@@ -59,7 +59,7 @@ class SquidPurgeClientPool {
                                        $writeSockets["$clientIndex/$i"] = $socket;
                                }
                        }
-                       if ( !count( $readSockets ) && !count( $writeSockets ) ) {
+                       if ( $readSockets === [] && $writeSockets === [] ) {
                                break;
                        }
 
index 2c7afda..769364f 100644 (file)
@@ -20,8 +20,8 @@
  *
  * @file
  */
-use MediaWiki\Services\SalvageableService;
 use Wikimedia\Assert\Assert;
+use Wikimedia\Services\SalvageableService;
 
 /**
  * Factory class to create Config objects
index c87a344..96dc51c 100644 (file)
@@ -22,8 +22,8 @@
 
 namespace MediaWiki\Config;
 
-use MediaWiki\Services\SalvageableService;
 use Wikimedia\Assert\Assert;
+use Wikimedia\Services\SalvageableService;
 
 /**
  * Object which holds currently registered configuration options.
index 628b47b..6af6de5 100644 (file)
@@ -589,7 +589,7 @@ class DatabaseOracle extends Database {
        public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
                $fname = __METHOD__
        ) {
-               if ( !count( $rows ) ) {
+               if ( $rows === [] ) {
                        return true; // nothing to do
                }
 
index 1cc6646..e2cdd82 100644 (file)
@@ -85,6 +85,7 @@ class TextSlotDiffRenderer extends SlotDiffRenderer {
        public function setLanguage( Language $language ) {
                $this->language = $language;
        }
+
        /**
         * @param int $cutoff
         * @see $wgWikiDiff2MovedParagraphDetectionCutoff
index 81dc78d..354cc61 100644 (file)
@@ -58,7 +58,7 @@ class TextboxBuilder {
         * @return mixed[]
         */
        public function mergeClassesIntoAttributes( array $classes, array $attribs ) {
-               if ( !count( $classes ) ) {
+               if ( $classes === [] ) {
                        return $attribs;
                }
 
index df7111a..9f22815 100644 (file)
@@ -18,6 +18,7 @@ class MWUnknownContentModelException extends MWException {
                        'handle this content model.' );
                $this->modelId = $modelId;
        }
+
        /** @return string */
        public function getModelId() {
                return $this->modelId;
index b3eae90..bb65b0a 100644 (file)
@@ -405,7 +405,7 @@ class LocalRepo extends FileRepo {
         * @return array[] An Array of arrays or iterators of file objects and the hash as key
         */
        function findBySha1s( array $hashes ) {
-               if ( !count( $hashes ) ) {
+               if ( $hashes === [] ) {
                        return []; // empty parameter
                }
 
index 4a84cff..6a3e819 100644 (file)
@@ -169,7 +169,7 @@ class ArchivedFile {
                        $conds['fa_sha1'] = $this->sha1;
                }
 
-               if ( !count( $conds ) ) {
+               if ( $conds === [] ) {
                        throw new MWException( "No specific information for retrieving archived file" );
                }
 
index 029f67d..5a3d77a 100644 (file)
@@ -694,7 +694,7 @@ abstract class Installer {
                                'enableSectionEditLinks' => false,
                                'unwrap' => true,
                        ] );
-               } catch ( MediaWiki\Services\ServiceDisabledException $e ) {
+               } catch ( Wikimedia\Services\ServiceDisabledException $e ) {
                        $html = '<!--DB access attempted during parse-->  ' . htmlspecialchars( $text );
                }
 
index 3689ba4..4f4728d 100644 (file)
@@ -323,7 +323,7 @@ abstract class JobQueue {
        final public function batchPush( array $jobs, $flags = 0 ) {
                $this->assertNotReadOnly();
 
-               if ( !count( $jobs ) ) {
+               if ( $jobs === [] ) {
                        return; // nothing to do
                }
 
index 9931d83..fa17284 100644 (file)
@@ -214,7 +214,7 @@ class JobQueueDB extends JobQueue {
         * @return void
         */
        public function doBatchPushInternal( IDatabase $dbw, array $jobs, $flags, $method ) {
-               if ( !count( $jobs ) ) {
+               if ( $jobs === [] ) {
                        return;
                }
 
index b103b8e..4853c4a 100644 (file)
@@ -143,7 +143,7 @@ class JobQueueGroup {
                }
 
                $jobs = is_array( $jobs ) ? $jobs : [ $jobs ];
-               if ( !count( $jobs ) ) {
+               if ( $jobs === [] ) {
                        return;
                }
 
index b868128..a1ef28b 100644 (file)
@@ -203,7 +203,7 @@ class JobQueueRedis extends JobQueue {
                        }
                }
 
-               if ( !count( $items ) ) {
+               if ( $items === [] ) {
                        return; // nothing to do
                }
 
index 27e6924..06a155a 100644 (file)
@@ -417,7 +417,7 @@ abstract class FileBackend implements LoggerAwareInterface {
                if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
-               if ( !count( $ops ) ) {
+               if ( $ops === [] ) {
                        return $this->newStatus(); // nothing to do
                }
 
@@ -436,6 +436,7 @@ abstract class FileBackend implements LoggerAwareInterface {
         * @see FileBackend::doOperations()
         * @param array $ops
         * @param array $opts
+        * @return StatusValue
         */
        abstract protected function doOperationsInternal( array $ops, array $opts );
 
@@ -655,7 +656,7 @@ abstract class FileBackend implements LoggerAwareInterface {
                if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
-               if ( !count( $ops ) ) {
+               if ( $ops === [] ) {
                        return $this->newStatus(); // nothing to do
                }
 
@@ -673,6 +674,7 @@ abstract class FileBackend implements LoggerAwareInterface {
        /**
         * @see FileBackend::doQuickOperations()
         * @param array $ops
+        * @return StatusValue
         * @since 1.20
         */
        abstract protected function doQuickOperationsInternal( array $ops );
@@ -819,6 +821,7 @@ abstract class FileBackend implements LoggerAwareInterface {
        /**
         * @see FileBackend::prepare()
         * @param array $params
+        * @return StatusValue
         */
        abstract protected function doPrepare( array $params );
 
@@ -850,6 +853,7 @@ abstract class FileBackend implements LoggerAwareInterface {
        /**
         * @see FileBackend::secure()
         * @param array $params
+        * @return StatusValue
         */
        abstract protected function doSecure( array $params );
 
@@ -883,6 +887,7 @@ abstract class FileBackend implements LoggerAwareInterface {
        /**
         * @see FileBackend::publish()
         * @param array $params
+        * @return StatusValue
         */
        abstract protected function doPublish( array $params );
 
@@ -909,6 +914,7 @@ abstract class FileBackend implements LoggerAwareInterface {
        /**
         * @see FileBackend::clean()
         * @param array $params
+        * @return StatusValue
         */
        abstract protected function doClean( array $params );
 
index 47be4eb..999594b 100644 (file)
@@ -97,7 +97,7 @@ abstract class FileJournal {
         * @return StatusValue
         */
        final public function logChangeBatch( array $entries, $batchId ) {
-               if ( !count( $entries ) ) {
+               if ( $entries === [] ) {
                        return StatusValue::newGood();
                }
 
index f2624e7..019029c 100644 (file)
@@ -169,7 +169,7 @@ class FSLockManager extends LockManager {
                        if ( $this->locksHeld[$path][$type] <= 0 ) {
                                unset( $this->locksHeld[$path][$type] );
                        }
-                       if ( !count( $this->locksHeld[$path] ) ) {
+                       if ( $this->locksHeld[$path] === [] ) {
                                unset( $this->locksHeld[$path] ); // no locks on this path
                                if ( isset( $this->handles[$path] ) ) {
                                        $handlesToClose[] = $this->handles[$path];
index 65c6993..fd3ffa5 100644 (file)
@@ -18,7 +18,7 @@ class PostgreSqlLockManager extends DBLockManager {
 
        protected function doGetLocksOnServer( $lockSrv, array $paths, $type ) {
                $status = StatusValue::newGood();
-               if ( !count( $paths ) ) {
+               if ( $paths === [] ) {
                        return $status; // nothing to lock
                }
 
index 1d2e21a..1ef4642 100644 (file)
@@ -98,7 +98,7 @@ abstract class QuorumLockManager extends LockManager {
                                                $bucket = $this->getBucketFromPath( $path );
                                                $pathsToUnlock[$bucket][$type][] = $path;
                                        }
-                                       if ( !count( $this->locksHeld[$path] ) ) {
+                                       if ( $this->locksHeld[$path] === [] ) {
                                                unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
                                        }
                                }
@@ -110,7 +110,7 @@ abstract class QuorumLockManager extends LockManager {
                foreach ( $pathsToUnlock as $bucket => $pathsToUnlockByType ) {
                        $status->merge( $this->doUnlockingRequestBucket( $bucket, $pathsToUnlockByType ) );
                }
-               if ( !count( $this->locksHeld ) ) {
+               if ( $this->locksHeld === [] ) {
                        $status->merge( $this->releaseAllLocks() );
                        $this->degradedBuckets = []; // safe to retry the normal quorum
                }
index a270105..993eea9 100644 (file)
@@ -364,6 +364,7 @@ class XmlTypeCheck {
                        $this->filterMatchType = $callbackReturn;
                }
        }
+
        /**
         * Handle coming across a <!DOCTYPE declaration.
         *
index 938e534..3e71e36 100644 (file)
@@ -202,7 +202,7 @@ class ChronologyProtector implements LoggerAwareInterface {
                        );
                }
 
-               if ( !count( $this->shutdownPositions ) ) {
+               if ( $this->shutdownPositions === [] ) {
                        return []; // nothing to save
                }
 
index 9a9e36a..7d971af 100644 (file)
@@ -2855,7 +2855,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
        public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
                $fname = __METHOD__
        ) {
-               if ( !count( $rows ) ) {
+               if ( $rows === [] ) {
                        return true; // nothing to do
                }
 
index 3fcbcf9..186c89f 100644 (file)
@@ -1337,7 +1337,7 @@ abstract class DatabaseMysqlBase extends Database {
        public function upsert( $table, array $rows, array $uniqueIndexes,
                array $set, $fname = __METHOD__
        ) {
-               if ( !count( $rows ) ) {
+               if ( $rows === [] ) {
                        return true; // nothing to do
                }
 
index ca18122..ab5c3cd 100644 (file)
@@ -408,7 +408,7 @@ class LoadBalancer implements ILoadBalancer {
         * @return array (reader index, lagged replica mode) or false on failure
         */
        private function pickReaderIndex( array $loads, $domain = false ) {
-               if ( !count( $loads ) ) {
+               if ( $loads === [] ) {
                        throw new InvalidArgumentException( "Empty server array given to LoadBalancer" );
                }
 
@@ -476,7 +476,7 @@ class LoadBalancer implements ILoadBalancer {
                }
 
                // If all servers were down, quit now
-               if ( !count( $currentLoads ) ) {
+               if ( $currentLoads === [] ) {
                        $this->connLogger->error( __METHOD__ . ": all servers down" );
                }
 
diff --git a/includes/libs/services/CannotReplaceActiveServiceException.php b/includes/libs/services/CannotReplaceActiveServiceException.php
new file mode 100644 (file)
index 0000000..ee0d7d0
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when trying to replace an already active service.
+ *
+ * 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
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when trying to replace an already active service.
+ */
+class CannotReplaceActiveServiceException extends RuntimeException {
+
+       /**
+        * @param string $serviceName
+        * @param Exception|null $previous
+        */
+       public function __construct( $serviceName, Exception $previous = null ) {
+               parent::__construct( "Cannot replace an active service: $serviceName", 0, $previous );
+       }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( CannotReplaceActiveServiceException::class,
+       'MediaWiki\Services\CannotReplaceActiveServiceException' );
diff --git a/includes/libs/services/ContainerDisabledException.php b/includes/libs/services/ContainerDisabledException.php
new file mode 100644 (file)
index 0000000..c909879
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when trying to access a service on a disabled container or factory.
+ *
+ * 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
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when trying to access a service on a disabled container or factory.
+ */
+class ContainerDisabledException extends RuntimeException {
+
+       /**
+        * @param Exception|null $previous
+        */
+       public function __construct( Exception $previous = null ) {
+               parent::__construct( 'Container disabled!', 0, $previous );
+       }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( ContainerDisabledException::class, 'MediaWiki\Services\ContainerDisabledException' );
diff --git a/includes/libs/services/DestructibleService.php b/includes/libs/services/DestructibleService.php
new file mode 100644 (file)
index 0000000..46e2f82
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+namespace Wikimedia\Services;
+
+/**
+ * Interface for destructible services.
+ *
+ * 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
+ *
+ * @since 1.27
+ */
+
+/**
+ * DestructibleService defines a standard interface for shutting down a service instance.
+ * The intended use is for a service container to be able to shut down services that should
+ * no longer be used, and allow such services to release any system resources.
+ *
+ * @note There is no expectation that services will be destroyed when the process (or web request)
+ * terminates.
+ */
+interface DestructibleService {
+
+       /**
+        * Notifies the service object that it should expect to no longer be used, and should release
+        * any system resources it may own. The behavior of all service methods becomes undefined after
+        * destroy() has been called. It is recommended that implementing classes should throw an
+        * exception when service methods are accessed after destroy() has been called.
+        */
+       public function destroy();
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( DestructibleService::class, 'MediaWiki\Services\DestructibleService' );
diff --git a/includes/libs/services/NoSuchServiceException.php b/includes/libs/services/NoSuchServiceException.php
new file mode 100644 (file)
index 0000000..3a0cdf4
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when the requested service is not known.
+ *
+ * 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
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when the requested service is not known.
+ */
+class NoSuchServiceException extends RuntimeException {
+
+       /**
+        * @param string $serviceName
+        * @param Exception|null $previous
+        */
+       public function __construct( $serviceName, Exception $previous = null ) {
+               parent::__construct( "No such service: $serviceName", 0, $previous );
+       }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( NoSuchServiceException::class, 'MediaWiki\Services\NoSuchServiceException' );
diff --git a/includes/libs/services/SalvageableService.php b/includes/libs/services/SalvageableService.php
new file mode 100644 (file)
index 0000000..4f94515
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+namespace Wikimedia\Services;
+
+/**
+ * Interface for salvageable services.
+ *
+ * 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
+ *
+ * @since 1.28
+ */
+
+/**
+ * SalvageableService defines an interface for services that are able to salvage state from a
+ * previous instance of the same class. The intent is to allow new service instances to re-use
+ * resources that would be expensive to re-create, such as cached data or network connections.
+ *
+ * @note There is no expectation that services will be destroyed when the process (or web request)
+ * terminates.
+ */
+interface SalvageableService {
+
+       /**
+        * Re-uses state from $other. $other must not be used after being passed to salvage(),
+        * and should be considered to be destroyed.
+        *
+        * @note Implementations are responsible for determining what parts of $other can be re-used
+        * safely. In particular, implementations should check that the relevant configuration of
+        * $other is the same as in $this before re-using resources from $other.
+        *
+        * @note Implementations must take care to detach any re-used resources from the original
+        * service instance. If $other is destroyed later, resources that are now used by the
+        * new service instance must not be affected.
+        *
+        * @note If $other is a DestructibleService, implementations should make sure that $other
+        * is in destroyed state after salvage finished. This may be done by calling $other->destroy()
+        * after carefully detaching all relevant resources.
+        *
+        * @param SalvageableService $other The object to salvage state from. $other must have the
+        * exact same type as $this.
+        */
+       public function salvage( SalvageableService $other );
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( SalvageableService::class, 'MediaWiki\Services\SalvageableService' );
diff --git a/includes/libs/services/ServiceAlreadyDefinedException.php b/includes/libs/services/ServiceAlreadyDefinedException.php
new file mode 100644 (file)
index 0000000..39c8384
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when a service was already defined, but the
+ * caller expected it to not exist.
+ *
+ * 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
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when a service was already defined, but the
+ * caller expected it to not exist.
+ */
+class ServiceAlreadyDefinedException extends RuntimeException {
+
+       /**
+        * @param string $serviceName
+        * @param Exception|null $previous
+        */
+       public function __construct( $serviceName, Exception $previous = null ) {
+               parent::__construct( "Service already defined: $serviceName", 0, $previous );
+       }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( ServiceAlreadyDefinedException::class,
+       'MediaWiki\Services\ServiceAlreadyDefinedException' );
diff --git a/includes/libs/services/ServiceContainer.php b/includes/libs/services/ServiceContainer.php
new file mode 100644 (file)
index 0000000..59e5c4b
--- /dev/null
@@ -0,0 +1,471 @@
+<?php
+namespace Wikimedia\Services;
+
+use InvalidArgumentException;
+use RuntimeException;
+use Wikimedia\Assert\Assert;
+
+/**
+ * Generic service container.
+ *
+ * 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
+ *
+ * @since 1.27
+ */
+
+/**
+ * ServiceContainer provides a generic service to manage named services using
+ * lazy instantiation based on instantiator callback functions.
+ *
+ * Services managed by an instance of ServiceContainer may or may not implement
+ * a common interface.
+ *
+ * @note When using ServiceContainer to manage a set of services, consider
+ * creating a wrapper or a subclass that provides access to the services via
+ * getter methods with more meaningful names and more specific return type
+ * declarations.
+ *
+ * @see docs/injection.txt for an overview of using dependency injection in the
+ *      MediaWiki code base.
+ */
+class ServiceContainer implements DestructibleService {
+
+       /**
+        * @var object[]
+        */
+       private $services = [];
+
+       /**
+        * @var callable[]
+        */
+       private $serviceInstantiators = [];
+
+       /**
+        * @var callable[][]
+        */
+       private $serviceManipulators = [];
+
+       /**
+        * @var bool[] disabled status, per service name
+        */
+       private $disabled = [];
+
+       /**
+        * @var array
+        */
+       private $extraInstantiationParams;
+
+       /**
+        * @var bool
+        */
+       private $destroyed = false;
+
+       /**
+        * @param array $extraInstantiationParams Any additional parameters to be passed to the
+        * instantiator function when creating a service. This is typically used to provide
+        * access to additional ServiceContainers or Config objects.
+        */
+       public function __construct( array $extraInstantiationParams = [] ) {
+               $this->extraInstantiationParams = $extraInstantiationParams;
+       }
+
+       /**
+        * Destroys all contained service instances that implement the DestructibleService
+        * interface. This will render all services obtained from this ServiceContainer
+        * instance unusable. In particular, this will disable access to the storage backend
+        * via any of these services. Any future call to getService() will throw an exception.
+        *
+        * @see resetGlobalInstance()
+        */
+       public function destroy() {
+               foreach ( $this->getServiceNames() as $name ) {
+                       $service = $this->peekService( $name );
+                       if ( $service !== null && $service instanceof DestructibleService ) {
+                               $service->destroy();
+                       }
+               }
+
+               // Break circular references due to the $this reference in closures, by
+               // erasing the instantiator array. This allows the ServiceContainer to
+               // be deleted when it goes out of scope.
+               $this->serviceInstantiators = [];
+               // Also remove the services themselves, to avoid confusion.
+               $this->services = [];
+               $this->destroyed = true;
+       }
+
+       /**
+        * @param array $wiringFiles A list of PHP files to load wiring information from.
+        * Each file is loaded using PHP's include mechanism. Each file is expected to
+        * return an associative array that maps service names to instantiator functions.
+        */
+       public function loadWiringFiles( array $wiringFiles ) {
+               foreach ( $wiringFiles as $file ) {
+                       // the wiring file is required to return an array of instantiators.
+                       $wiring = require $file;
+
+                       Assert::postcondition(
+                               is_array( $wiring ),
+                               "Wiring file $file is expected to return an array!"
+                       );
+
+                       $this->applyWiring( $wiring );
+               }
+       }
+
+       /**
+        * Registers multiple services (aka a "wiring").
+        *
+        * @param array $serviceInstantiators An associative array mapping service names to
+        *        instantiator functions.
+        */
+       public function applyWiring( array $serviceInstantiators ) {
+               Assert::parameterElementType( 'callable', $serviceInstantiators, '$serviceInstantiators' );
+
+               foreach ( $serviceInstantiators as $name => $instantiator ) {
+                       $this->defineService( $name, $instantiator );
+               }
+       }
+
+       /**
+        * Imports all wiring defined in $container. Wiring defined in $container
+        * will override any wiring already defined locally. However, already
+        * existing service instances will be preserved.
+        *
+        * @since 1.28
+        *
+        * @param ServiceContainer $container
+        * @param string[] $skip A list of service names to skip during import
+        */
+       public function importWiring( ServiceContainer $container, $skip = [] ) {
+               $newInstantiators = array_diff_key(
+                       $container->serviceInstantiators,
+                       array_flip( $skip )
+               );
+
+               $this->serviceInstantiators = array_merge(
+                       $this->serviceInstantiators,
+                       $newInstantiators
+               );
+
+               $newManipulators = array_diff(
+                       array_keys( $container->serviceManipulators ),
+                       $skip
+               );
+
+               foreach ( $newManipulators as $name ) {
+                       if ( isset( $this->serviceManipulators[$name] ) ) {
+                               $this->serviceManipulators[$name] = array_merge(
+                                       $this->serviceManipulators[$name],
+                                       $container->serviceManipulators[$name]
+                               );
+                       } else {
+                               $this->serviceManipulators[$name] = $container->serviceManipulators[$name];
+                       }
+               }
+       }
+
+       /**
+        * Returns true if a service is defined for $name, that is, if a call to getService( $name )
+        * would return a service instance.
+        *
+        * @param string $name
+        *
+        * @return bool
+        */
+       public function hasService( $name ) {
+               return isset( $this->serviceInstantiators[$name] );
+       }
+
+       /**
+        * Returns the service instance for $name only if that service has already been instantiated.
+        * This is intended for situations where services get destroyed/cleaned up, so we can
+        * avoid creating a service just to destroy it again.
+        *
+        * @note This is intended for internal use and for test fixtures.
+        * Application logic should use getService() instead.
+        *
+        * @see getService().
+        *
+        * @param string $name
+        *
+        * @return object|null The service instance, or null if the service has not yet been instantiated.
+        * @throws RuntimeException if $name does not refer to a known service.
+        */
+       public function peekService( $name ) {
+               if ( !$this->hasService( $name ) ) {
+                       throw new NoSuchServiceException( $name );
+               }
+
+               return $this->services[$name] ?? null;
+       }
+
+       /**
+        * @return string[]
+        */
+       public function getServiceNames() {
+               return array_keys( $this->serviceInstantiators );
+       }
+
+       /**
+        * Define a new service. The service must not be known already.
+        *
+        * @see getService().
+        * @see redefineService().
+        *
+        * @param string $name The name of the service to register, for use with getService().
+        * @param callable $instantiator Callback that returns a service instance.
+        *        Will be called with this ServiceContainer instance as the only parameter.
+        *        Any extra instantiation parameters provided to the constructor will be
+        *        passed as subsequent parameters when invoking the instantiator.
+        *
+        * @throws RuntimeException if there is already a service registered as $name.
+        */
+       public function defineService( $name, callable $instantiator ) {
+               Assert::parameterType( 'string', $name, '$name' );
+
+               if ( $this->hasService( $name ) ) {
+                       throw new ServiceAlreadyDefinedException( $name );
+               }
+
+               $this->serviceInstantiators[$name] = $instantiator;
+       }
+
+       /**
+        * Replace an already defined service.
+        *
+        * @see defineService().
+        *
+        * @note This will fail if the service was already instantiated. If the service was previously
+        * disabled, it will be re-enabled by this call. Any manipulators registered for the service
+        * will remain in place.
+        *
+        * @param string $name The name of the service to register.
+        * @param callable $instantiator Callback function that returns a service instance.
+        *        Will be called with this ServiceContainer instance as the only parameter.
+        *        The instantiator must return a service compatible with the originally defined service.
+        *        Any extra instantiation parameters provided to the constructor will be
+        *        passed as subsequent parameters when invoking the instantiator.
+        *
+        * @throws NoSuchServiceException if $name is not a known service.
+        * @throws CannotReplaceActiveServiceException if the service was already instantiated.
+        */
+       public function redefineService( $name, callable $instantiator ) {
+               Assert::parameterType( 'string', $name, '$name' );
+
+               if ( !$this->hasService( $name ) ) {
+                       throw new NoSuchServiceException( $name );
+               }
+
+               if ( isset( $this->services[$name] ) ) {
+                       throw new CannotReplaceActiveServiceException( $name );
+               }
+
+               $this->serviceInstantiators[$name] = $instantiator;
+               unset( $this->disabled[$name] );
+       }
+
+       /**
+        * Add a service manipulator callback for the given service.
+        * This method may be used by extensions that need to wrap, replace, or re-configure a
+        * service. It would typically be called from a MediaWikiServices hook handler.
+        *
+        * The manipulator callback is called just after the service is instantiated.
+        * It can call methods on the service to change configuration, or wrap or otherwise
+        * replace it.
+        *
+        * @see defineService().
+        * @see redefineService().
+        *
+        * @note This will fail if the service was already instantiated.
+        *
+        * @since 1.32
+        *
+        * @param string $name The name of the service to manipulate.
+        * @param callable $manipulator Callback function that manipulates, wraps or replaces a
+        * service instance. The callback receives the new service instance and this
+        * ServiceContainer as parameters, as well as any extra instantiation parameters specified
+        * when constructing this ServiceContainer. If the callback returns a value, that
+        * value replaces the original service instance.
+        *
+        * @throws NoSuchServiceException if $name is not a known service.
+        * @throws CannotReplaceActiveServiceException if the service was already instantiated.
+        */
+       public function addServiceManipulator( $name, callable $manipulator ) {
+               Assert::parameterType( 'string', $name, '$name' );
+
+               if ( !$this->hasService( $name ) ) {
+                       throw new NoSuchServiceException( $name );
+               }
+
+               if ( isset( $this->services[$name] ) ) {
+                       throw new CannotReplaceActiveServiceException( $name );
+               }
+
+               $this->serviceManipulators[$name][] = $manipulator;
+       }
+
+       /**
+        * Disables a service.
+        *
+        * @note Attempts to call getService() for a disabled service will result
+        * in a DisabledServiceException. Calling peekService for a disabled service will
+        * return null. Disabled services are listed by getServiceNames(). A disabled service
+        * can be enabled again using redefineService().
+        *
+        * @note If the service was already active (that is, instantiated) when getting disabled,
+        * and the service instance implements DestructibleService, destroy() is called on the
+        * service instance.
+        *
+        * @see redefineService()
+        * @see resetService()
+        *
+        * @param string $name The name of the service to disable.
+        *
+        * @throws RuntimeException if $name is not a known service.
+        */
+       public function disableService( $name ) {
+               $this->resetService( $name );
+
+               $this->disabled[$name] = true;
+       }
+
+       /**
+        * Resets a service by dropping the service instance.
+        * If the service instances implements DestructibleService, destroy()
+        * is called on the service instance.
+        *
+        * @warning This is generally unsafe! Other services may still retain references
+        * to the stale service instance, leading to failures and inconsistencies. Subclasses
+        * may use this method to reset specific services under specific instances, but
+        * it should not be exposed to application logic.
+        *
+        * @note This is declared final so subclasses can not interfere with the expectations
+        * disableService() has when calling resetService().
+        *
+        * @see redefineService()
+        * @see disableService().
+        *
+        * @param string $name The name of the service to reset.
+        * @param bool $destroy Whether the service instance should be destroyed if it exists.
+        *        When set to false, any existing service instance will effectively be detached
+        *        from the container.
+        *
+        * @throws RuntimeException if $name is not a known service.
+        */
+       final protected function resetService( $name, $destroy = true ) {
+               Assert::parameterType( 'string', $name, '$name' );
+
+               $instance = $this->peekService( $name );
+
+               if ( $destroy && $instance instanceof DestructibleService ) {
+                       $instance->destroy();
+               }
+
+               unset( $this->services[$name] );
+               unset( $this->disabled[$name] );
+       }
+
+       /**
+        * Returns a service object of the kind associated with $name.
+        * Services instances are instantiated lazily, on demand.
+        * This method may or may not return the same service instance
+        * when called multiple times with the same $name.
+        *
+        * @note Rather than calling this method directly, it is recommended to provide
+        * getters with more meaningful names and more specific return types, using
+        * a subclass or wrapper.
+        *
+        * @see redefineService().
+        *
+        * @param string $name The service name
+        *
+        * @throws NoSuchServiceException if $name is not a known service.
+        * @throws ContainerDisabledException if this container has already been destroyed.
+        * @throws ServiceDisabledException if the requested service has been disabled.
+        *
+        * @return object The service instance
+        */
+       public function getService( $name ) {
+               if ( $this->destroyed ) {
+                       throw new ContainerDisabledException();
+               }
+
+               if ( isset( $this->disabled[$name] ) ) {
+                       throw new ServiceDisabledException( $name );
+               }
+
+               if ( !isset( $this->services[$name] ) ) {
+                       $this->services[$name] = $this->createService( $name );
+               }
+
+               return $this->services[$name];
+       }
+
+       /**
+        * @param string $name
+        *
+        * @throws InvalidArgumentException if $name is not a known service.
+        * @return object
+        */
+       private function createService( $name ) {
+               if ( isset( $this->serviceInstantiators[$name] ) ) {
+                       $service = ( $this->serviceInstantiators[$name] )(
+                               $this,
+                               ...$this->extraInstantiationParams
+                       );
+
+                       if ( isset( $this->serviceManipulators[$name] ) ) {
+                               foreach ( $this->serviceManipulators[$name] as $callback ) {
+                                       $ret = call_user_func_array(
+                                               $callback,
+                                               array_merge( [ $service, $this ], $this->extraInstantiationParams )
+                                       );
+
+                                       // If the manipulator callback returns an object, that object replaces
+                                       // the original service instance. This allows the manipulator to wrap
+                                       // or fully replace the service.
+                                       if ( $ret !== null ) {
+                                               $service = $ret;
+                                       }
+                               }
+                       }
+
+                       // NOTE: when adding more wiring logic here, make sure importWiring() is kept in sync!
+               } else {
+                       throw new NoSuchServiceException( $name );
+               }
+
+               return $service;
+       }
+
+       /**
+        * @param string $name
+        * @return bool Whether the service is disabled
+        * @since 1.28
+        */
+       public function isServiceDisabled( $name ) {
+               return isset( $this->disabled[$name] );
+       }
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( ServiceContainer::class, 'MediaWiki\Services\ServiceContainer' );
diff --git a/includes/libs/services/ServiceDisabledException.php b/includes/libs/services/ServiceDisabledException.php
new file mode 100644 (file)
index 0000000..86b927b
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+namespace Wikimedia\Services;
+
+use Exception;
+use RuntimeException;
+
+/**
+ * Exception thrown when trying to access a disabled service.
+ *
+ * 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
+ *
+ * @since 1.27
+ */
+
+/**
+ * Exception thrown when trying to access a disabled service.
+ */
+class ServiceDisabledException extends RuntimeException {
+
+       /**
+        * @param string $serviceName
+        * @param Exception|null $previous
+        */
+       public function __construct( $serviceName, Exception $previous = null ) {
+               parent::__construct( "Service disabled: $serviceName", 0, $previous );
+       }
+
+}
+
+/**
+ * Retain the old class name for backwards compatibility.
+ * @deprecated since 1.33
+ */
+class_alias( ServiceDisabledException::class, 'MediaWiki\Services\ServiceDisabledException' );
index 8a089f6..76a7760 100644 (file)
@@ -154,7 +154,7 @@ class EmailNotification {
                // If nobody is watching the page, and there are no users notified on all changes
                // don't bother creating a job/trying to send emails, unless it's a
                // talk page with an applicable notification.
-               if ( !count( $watchers ) && !count( $wgUsersNotifiedOnAllChanges ) ) {
+               if ( $watchers === [] && !count( $wgUsersNotifiedOnAllChanges ) ) {
                        $sendEmail = false;
                        // Only send notification for non minor edits, unless $wgEnotifMinorEdits
                        if ( !$minorEdit || ( $wgEnotifMinorEdits && !$editor->isAllowed( 'nominornewtalk' ) ) ) {
index 8407992..a69e805 100644 (file)
@@ -733,6 +733,7 @@ class ParserOptions {
        public function getMagicPMIDLinks() {
                return $this->getOption( 'magicPMIDLinks' );
        }
+
        /**
         * Are magic RFC links enabled?
         * @since 1.28
index f4e4efa..3bcd012 100644 (file)
@@ -878,7 +878,7 @@ class PPDStack {
        }
 
        public function pop() {
-               if ( !count( $this->stack ) ) {
+               if ( $this->stack === [] ) {
                        throw new MWException( __METHOD__ . ': no elements remaining' );
                }
                $temp = array_pop( $this->stack );
@@ -902,7 +902,7 @@ class PPDStack {
         * @return array
         */
        public function getFlags() {
-               if ( !count( $this->stack ) ) {
+               if ( $this->stack === [] ) {
                        return [
                                'findEquals' => false,
                                'findPipe' => false,
index 9570e03..c513aed 100644 (file)
@@ -781,7 +781,7 @@ class ResourceLoader implements LoggerAwareInterface {
                }
 
                // Save response to file cache unless there are errors
-               if ( isset( $fileCache ) && !$this->errors && !count( $missing ) ) {
+               if ( isset( $fileCache ) && !$this->errors && $missing === [] ) {
                        // Cache single modules and images...and other requests if there are enough hits
                        if ( ResourceFileCache::useFileCache( $context ) ) {
                                if ( $fileCache->isCacheWorthy() ) {
@@ -1036,7 +1036,7 @@ class ResourceLoader implements LoggerAwareInterface {
                $out = '';
                $states = [];
 
-               if ( !count( $modules ) && !count( $missing ) ) {
+               if ( $modules === [] && $missing === [] ) {
                        return <<<MESSAGE
 /* This file is the Web entry point for MediaWiki's ResourceLoader:
    <https://www.mediawiki.org/wiki/ResourceLoader>. In this request,
index ef11628..8cd5b19 100644 (file)
@@ -295,9 +295,20 @@ class ResourceLoaderImage {
                $dom = new DOMDocument;
                $dom->loadXML( file_get_contents( $this->getPath( $context ) ) );
                $root = $dom->documentElement;
-               $wrapper = $dom->createElement( 'g' );
+               $titleNode = null;
+               $wrapper = $dom->createElementNS( 'http://www.w3.org/2000/svg', 'g' );
+               // Reattach all direct children of the `<svg>` root node to the `<g>` wrapper
                while ( $root->firstChild ) {
-                       $wrapper->appendChild( $root->firstChild );
+                       $node = $root->firstChild;
+                       if ( !$titleNode && $node->nodeType === XML_ELEMENT_NODE && $node->tagName === 'title' ) {
+                               // Remember the first encountered `<title>` node
+                               $titleNode = $node;
+                       }
+                       $wrapper->appendChild( $node );
+               }
+               if ( $titleNode ) {
+                       // Reattach the `<title>` node to the `<svg>` root node rather than the `<g>` wrapper
+                       $root->appendChild( $titleNode );
                }
                $root->appendChild( $wrapper );
                $wrapper->setAttribute( 'fill', $variantConf['color'] );
index 9cd245a..0cbb41c 100644 (file)
@@ -98,7 +98,7 @@ class SearchOracle extends SearchDatabase {
                if ( is_null( $this->namespaces ) ) {
                        return '';
                }
-               if ( !count( $this->namespaces ) ) {
+               if ( $this->namespaces === [] ) {
                        $namespaces = '0';
                } else {
                        $namespaces = $this->db->makeList( $this->namespaces );
index 6332ea2..f653796 100644 (file)
@@ -199,7 +199,7 @@ class SearchSqlite extends SearchDatabase {
                if ( is_null( $this->namespaces ) ) {
                        return '';  # search all
                }
-               if ( !count( $this->namespaces ) ) {
+               if ( $this->namespaces === [] ) {
                        $namespaces = '0';
                } else {
                        $namespaces = $this->db->makeList( $this->namespaces );
diff --git a/includes/services/CannotReplaceActiveServiceException.php b/includes/services/CannotReplaceActiveServiceException.php
deleted file mode 100644 (file)
index 4993073..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when trying to replace an already active service.
- *
- * 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
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when trying to replace an already active service.
- */
-class CannotReplaceActiveServiceException extends RuntimeException {
-
-       /**
-        * @param string $serviceName
-        * @param Exception|null $previous
-        */
-       public function __construct( $serviceName, Exception $previous = null ) {
-               parent::__construct( "Cannot replace an active service: $serviceName", 0, $previous );
-       }
-
-}
diff --git a/includes/services/ContainerDisabledException.php b/includes/services/ContainerDisabledException.php
deleted file mode 100644 (file)
index ede076d..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when trying to access a service on a disabled container or factory.
- *
- * 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
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when trying to access a service on a disabled container or factory.
- */
-class ContainerDisabledException extends RuntimeException {
-
-       /**
-        * @param Exception|null $previous
-        */
-       public function __construct( Exception $previous = null ) {
-               parent::__construct( 'Container disabled!', 0, $previous );
-       }
-
-}
diff --git a/includes/services/DestructibleService.php b/includes/services/DestructibleService.php
deleted file mode 100644 (file)
index 6ce9af2..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-namespace MediaWiki\Services;
-
-/**
- * Interface for destructible services.
- *
- * 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
- *
- * @since 1.27
- */
-
-/**
- * DestructibleService defines a standard interface for shutting down a service instance.
- * The intended use is for a service container to be able to shut down services that should
- * no longer be used, and allow such services to release any system resources.
- *
- * @note There is no expectation that services will be destroyed when the process (or web request)
- * terminates.
- */
-interface DestructibleService {
-
-       /**
-        * Notifies the service object that it should expect to no longer be used, and should release
-        * any system resources it may own. The behavior of all service methods becomes undefined after
-        * destroy() has been called. It is recommended that implementing classes should throw an
-        * exception when service methods are accessed after destroy() has been called.
-        */
-       public function destroy();
-
-}
diff --git a/includes/services/NoSuchServiceException.php b/includes/services/NoSuchServiceException.php
deleted file mode 100644 (file)
index 36e50d2..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when the requested service is not known.
- *
- * 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
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when the requested service is not known.
- */
-class NoSuchServiceException extends RuntimeException {
-
-       /**
-        * @param string $serviceName
-        * @param Exception|null $previous
-        */
-       public function __construct( $serviceName, Exception $previous = null ) {
-               parent::__construct( "No such service: $serviceName", 0, $previous );
-       }
-
-}
diff --git a/includes/services/SalvageableService.php b/includes/services/SalvageableService.php
deleted file mode 100644 (file)
index a613050..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-namespace MediaWiki\Services;
-
-/**
- * Interface for salvageable services.
- *
- * 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
- *
- * @since 1.28
- */
-
-/**
- * SalvageableService defines an interface for services that are able to salvage state from a
- * previous instance of the same class. The intent is to allow new service instances to re-use
- * resources that would be expensive to re-create, such as cached data or network connections.
- *
- * @note There is no expectation that services will be destroyed when the process (or web request)
- * terminates.
- */
-interface SalvageableService {
-
-       /**
-        * Re-uses state from $other. $other must not be used after being passed to salvage(),
-        * and should be considered to be destroyed.
-        *
-        * @note Implementations are responsible for determining what parts of $other can be re-used
-        * safely. In particular, implementations should check that the relevant configuration of
-        * $other is the same as in $this before re-using resources from $other.
-        *
-        * @note Implementations must take care to detach any re-used resources from the original
-        * service instance. If $other is destroyed later, resources that are now used by the
-        * new service instance must not be affected.
-        *
-        * @note If $other is a DestructibleService, implementations should make sure that $other
-        * is in destroyed state after salvage finished. This may be done by calling $other->destroy()
-        * after carefully detaching all relevant resources.
-        *
-        * @param SalvageableService $other The object to salvage state from. $other must have the
-        * exact same type as $this.
-        */
-       public function salvage( SalvageableService $other );
-
-}
diff --git a/includes/services/ServiceAlreadyDefinedException.php b/includes/services/ServiceAlreadyDefinedException.php
deleted file mode 100644 (file)
index c6344d3..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when a service was already defined, but the
- * caller expected it to not exist.
- *
- * 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
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when a service was already defined, but the
- * caller expected it to not exist.
- */
-class ServiceAlreadyDefinedException extends RuntimeException {
-
-       /**
-        * @param string $serviceName
-        * @param Exception|null $previous
-        */
-       public function __construct( $serviceName, Exception $previous = null ) {
-               parent::__construct( "Service already defined: $serviceName", 0, $previous );
-       }
-
-}
diff --git a/includes/services/ServiceContainer.php b/includes/services/ServiceContainer.php
deleted file mode 100644 (file)
index d934d27..0000000
+++ /dev/null
@@ -1,465 +0,0 @@
-<?php
-namespace MediaWiki\Services;
-
-use InvalidArgumentException;
-use RuntimeException;
-use Wikimedia\Assert\Assert;
-
-/**
- * Generic service container.
- *
- * 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
- *
- * @since 1.27
- */
-
-/**
- * ServiceContainer provides a generic service to manage named services using
- * lazy instantiation based on instantiator callback functions.
- *
- * Services managed by an instance of ServiceContainer may or may not implement
- * a common interface.
- *
- * @note When using ServiceContainer to manage a set of services, consider
- * creating a wrapper or a subclass that provides access to the services via
- * getter methods with more meaningful names and more specific return type
- * declarations.
- *
- * @see docs/injection.txt for an overview of using dependency injection in the
- *      MediaWiki code base.
- */
-class ServiceContainer implements DestructibleService {
-
-       /**
-        * @var object[]
-        */
-       private $services = [];
-
-       /**
-        * @var callable[]
-        */
-       private $serviceInstantiators = [];
-
-       /**
-        * @var callable[][]
-        */
-       private $serviceManipulators = [];
-
-       /**
-        * @var bool[] disabled status, per service name
-        */
-       private $disabled = [];
-
-       /**
-        * @var array
-        */
-       private $extraInstantiationParams;
-
-       /**
-        * @var bool
-        */
-       private $destroyed = false;
-
-       /**
-        * @param array $extraInstantiationParams Any additional parameters to be passed to the
-        * instantiator function when creating a service. This is typically used to provide
-        * access to additional ServiceContainers or Config objects.
-        */
-       public function __construct( array $extraInstantiationParams = [] ) {
-               $this->extraInstantiationParams = $extraInstantiationParams;
-       }
-
-       /**
-        * Destroys all contained service instances that implement the DestructibleService
-        * interface. This will render all services obtained from this MediaWikiServices
-        * instance unusable. In particular, this will disable access to the storage backend
-        * via any of these services. Any future call to getService() will throw an exception.
-        *
-        * @see resetGlobalInstance()
-        */
-       public function destroy() {
-               foreach ( $this->getServiceNames() as $name ) {
-                       $service = $this->peekService( $name );
-                       if ( $service !== null && $service instanceof DestructibleService ) {
-                               $service->destroy();
-                       }
-               }
-
-               // Break circular references due to the $this reference in closures, by
-               // erasing the instantiator array. This allows the ServiceContainer to
-               // be deleted when it goes out of scope.
-               $this->serviceInstantiators = [];
-               // Also remove the services themselves, to avoid confusion.
-               $this->services = [];
-               $this->destroyed = true;
-       }
-
-       /**
-        * @param array $wiringFiles A list of PHP files to load wiring information from.
-        * Each file is loaded using PHP's include mechanism. Each file is expected to
-        * return an associative array that maps service names to instantiator functions.
-        */
-       public function loadWiringFiles( array $wiringFiles ) {
-               foreach ( $wiringFiles as $file ) {
-                       // the wiring file is required to return an array of instantiators.
-                       $wiring = require $file;
-
-                       Assert::postcondition(
-                               is_array( $wiring ),
-                               "Wiring file $file is expected to return an array!"
-                       );
-
-                       $this->applyWiring( $wiring );
-               }
-       }
-
-       /**
-        * Registers multiple services (aka a "wiring").
-        *
-        * @param array $serviceInstantiators An associative array mapping service names to
-        *        instantiator functions.
-        */
-       public function applyWiring( array $serviceInstantiators ) {
-               Assert::parameterElementType( 'callable', $serviceInstantiators, '$serviceInstantiators' );
-
-               foreach ( $serviceInstantiators as $name => $instantiator ) {
-                       $this->defineService( $name, $instantiator );
-               }
-       }
-
-       /**
-        * Imports all wiring defined in $container. Wiring defined in $container
-        * will override any wiring already defined locally. However, already
-        * existing service instances will be preserved.
-        *
-        * @since 1.28
-        *
-        * @param ServiceContainer $container
-        * @param string[] $skip A list of service names to skip during import
-        */
-       public function importWiring( ServiceContainer $container, $skip = [] ) {
-               $newInstantiators = array_diff_key(
-                       $container->serviceInstantiators,
-                       array_flip( $skip )
-               );
-
-               $this->serviceInstantiators = array_merge(
-                       $this->serviceInstantiators,
-                       $newInstantiators
-               );
-
-               $newManipulators = array_diff(
-                       array_keys( $container->serviceManipulators ),
-                       $skip
-               );
-
-               foreach ( $newManipulators as $name ) {
-                       if ( isset( $this->serviceManipulators[$name] ) ) {
-                               $this->serviceManipulators[$name] = array_merge(
-                                       $this->serviceManipulators[$name],
-                                       $container->serviceManipulators[$name]
-                               );
-                       } else {
-                               $this->serviceManipulators[$name] = $container->serviceManipulators[$name];
-                       }
-               }
-       }
-
-       /**
-        * Returns true if a service is defined for $name, that is, if a call to getService( $name )
-        * would return a service instance.
-        *
-        * @param string $name
-        *
-        * @return bool
-        */
-       public function hasService( $name ) {
-               return isset( $this->serviceInstantiators[$name] );
-       }
-
-       /**
-        * Returns the service instance for $name only if that service has already been instantiated.
-        * This is intended for situations where services get destroyed/cleaned up, so we can
-        * avoid creating a service just to destroy it again.
-        *
-        * @note This is intended for internal use and for test fixtures.
-        * Application logic should use getService() instead.
-        *
-        * @see getService().
-        *
-        * @param string $name
-        *
-        * @return object|null The service instance, or null if the service has not yet been instantiated.
-        * @throws RuntimeException if $name does not refer to a known service.
-        */
-       public function peekService( $name ) {
-               if ( !$this->hasService( $name ) ) {
-                       throw new NoSuchServiceException( $name );
-               }
-
-               return $this->services[$name] ?? null;
-       }
-
-       /**
-        * @return string[]
-        */
-       public function getServiceNames() {
-               return array_keys( $this->serviceInstantiators );
-       }
-
-       /**
-        * Define a new service. The service must not be known already.
-        *
-        * @see getService().
-        * @see redefineService().
-        *
-        * @param string $name The name of the service to register, for use with getService().
-        * @param callable $instantiator Callback that returns a service instance.
-        *        Will be called with this MediaWikiServices instance as the only parameter.
-        *        Any extra instantiation parameters provided to the constructor will be
-        *        passed as subsequent parameters when invoking the instantiator.
-        *
-        * @throws RuntimeException if there is already a service registered as $name.
-        */
-       public function defineService( $name, callable $instantiator ) {
-               Assert::parameterType( 'string', $name, '$name' );
-
-               if ( $this->hasService( $name ) ) {
-                       throw new ServiceAlreadyDefinedException( $name );
-               }
-
-               $this->serviceInstantiators[$name] = $instantiator;
-       }
-
-       /**
-        * Replace an already defined service.
-        *
-        * @see defineService().
-        *
-        * @note This will fail if the service was already instantiated. If the service was previously
-        * disabled, it will be re-enabled by this call. Any manipulators registered for the service
-        * will remain in place.
-        *
-        * @param string $name The name of the service to register.
-        * @param callable $instantiator Callback function that returns a service instance.
-        *        Will be called with this MediaWikiServices instance as the only parameter.
-        *        The instantiator must return a service compatible with the originally defined service.
-        *        Any extra instantiation parameters provided to the constructor will be
-        *        passed as subsequent parameters when invoking the instantiator.
-        *
-        * @throws NoSuchServiceException if $name is not a known service.
-        * @throws CannotReplaceActiveServiceException if the service was already instantiated.
-        */
-       public function redefineService( $name, callable $instantiator ) {
-               Assert::parameterType( 'string', $name, '$name' );
-
-               if ( !$this->hasService( $name ) ) {
-                       throw new NoSuchServiceException( $name );
-               }
-
-               if ( isset( $this->services[$name] ) ) {
-                       throw new CannotReplaceActiveServiceException( $name );
-               }
-
-               $this->serviceInstantiators[$name] = $instantiator;
-               unset( $this->disabled[$name] );
-       }
-
-       /**
-        * Add a service manipulator callback for the given service.
-        * This method may be used by extensions that need to wrap, replace, or re-configure a
-        * service. It would typically be called from a MediaWikiServices hook handler.
-        *
-        * The manipulator callback is called just after the service is instantiated.
-        * It can call methods on the service to change configuration, or wrap or otherwise
-        * replace it.
-        *
-        * @see defineService().
-        * @see redefineService().
-        *
-        * @note This will fail if the service was already instantiated.
-        *
-        * @since 1.32
-        *
-        * @param string $name The name of the service to manipulate.
-        * @param callable $manipulator Callback function that manipulates, wraps or replaces a
-        * service instance. The callback receives the new service instance and this the
-        * ServiceContainer as parameters, as well as any extra instantiation parameters specified
-        * when constructing this ServiceContainer. If the callback returns a value, that
-        * value replaces the original service instance.
-        *
-        * @throws NoSuchServiceException if $name is not a known service.
-        * @throws CannotReplaceActiveServiceException if the service was already instantiated.
-        */
-       public function addServiceManipulator( $name, callable $manipulator ) {
-               Assert::parameterType( 'string', $name, '$name' );
-
-               if ( !$this->hasService( $name ) ) {
-                       throw new NoSuchServiceException( $name );
-               }
-
-               if ( isset( $this->services[$name] ) ) {
-                       throw new CannotReplaceActiveServiceException( $name );
-               }
-
-               $this->serviceManipulators[$name][] = $manipulator;
-       }
-
-       /**
-        * Disables a service.
-        *
-        * @note Attempts to call getService() for a disabled service will result
-        * in a DisabledServiceException. Calling peekService for a disabled service will
-        * return null. Disabled services are listed by getServiceNames(). A disabled service
-        * can be enabled again using redefineService().
-        *
-        * @note If the service was already active (that is, instantiated) when getting disabled,
-        * and the service instance implements DestructibleService, destroy() is called on the
-        * service instance.
-        *
-        * @see redefineService()
-        * @see resetService()
-        *
-        * @param string $name The name of the service to disable.
-        *
-        * @throws RuntimeException if $name is not a known service.
-        */
-       public function disableService( $name ) {
-               $this->resetService( $name );
-
-               $this->disabled[$name] = true;
-       }
-
-       /**
-        * Resets a service by dropping the service instance.
-        * If the service instances implements DestructibleService, destroy()
-        * is called on the service instance.
-        *
-        * @warning This is generally unsafe! Other services may still retain references
-        * to the stale service instance, leading to failures and inconsistencies. Subclasses
-        * may use this method to reset specific services under specific instances, but
-        * it should not be exposed to application logic.
-        *
-        * @note This is declared final so subclasses can not interfere with the expectations
-        * disableService() has when calling resetService().
-        *
-        * @see redefineService()
-        * @see disableService().
-        *
-        * @param string $name The name of the service to reset.
-        * @param bool $destroy Whether the service instance should be destroyed if it exists.
-        *        When set to false, any existing service instance will effectively be detached
-        *        from the container.
-        *
-        * @throws RuntimeException if $name is not a known service.
-        */
-       final protected function resetService( $name, $destroy = true ) {
-               Assert::parameterType( 'string', $name, '$name' );
-
-               $instance = $this->peekService( $name );
-
-               if ( $destroy && $instance instanceof DestructibleService ) {
-                       $instance->destroy();
-               }
-
-               unset( $this->services[$name] );
-               unset( $this->disabled[$name] );
-       }
-
-       /**
-        * Returns a service object of the kind associated with $name.
-        * Services instances are instantiated lazily, on demand.
-        * This method may or may not return the same service instance
-        * when called multiple times with the same $name.
-        *
-        * @note Rather than calling this method directly, it is recommended to provide
-        * getters with more meaningful names and more specific return types, using
-        * a subclass or wrapper.
-        *
-        * @see redefineService().
-        *
-        * @param string $name The service name
-        *
-        * @throws NoSuchServiceException if $name is not a known service.
-        * @throws ContainerDisabledException if this container has already been destroyed.
-        * @throws ServiceDisabledException if the requested service has been disabled.
-        *
-        * @return object The service instance
-        */
-       public function getService( $name ) {
-               if ( $this->destroyed ) {
-                       throw new ContainerDisabledException();
-               }
-
-               if ( isset( $this->disabled[$name] ) ) {
-                       throw new ServiceDisabledException( $name );
-               }
-
-               if ( !isset( $this->services[$name] ) ) {
-                       $this->services[$name] = $this->createService( $name );
-               }
-
-               return $this->services[$name];
-       }
-
-       /**
-        * @param string $name
-        *
-        * @throws InvalidArgumentException if $name is not a known service.
-        * @return object
-        */
-       private function createService( $name ) {
-               if ( isset( $this->serviceInstantiators[$name] ) ) {
-                       $service = ( $this->serviceInstantiators[$name] )(
-                               $this,
-                               ...$this->extraInstantiationParams
-                       );
-
-                       if ( isset( $this->serviceManipulators[$name] ) ) {
-                               foreach ( $this->serviceManipulators[$name] as $callback ) {
-                                       $ret = call_user_func_array(
-                                               $callback,
-                                               array_merge( [ $service, $this ], $this->extraInstantiationParams )
-                                       );
-
-                                       // If the manipulator callback returns an object, that object replaces
-                                       // the original service instance. This allows the manipulator to wrap
-                                       // or fully replace the service.
-                                       if ( $ret !== null ) {
-                                               $service = $ret;
-                                       }
-                               }
-                       }
-
-                       // NOTE: when adding more wiring logic here, make sure importWiring() is kept in sync!
-               } else {
-                       throw new NoSuchServiceException( $name );
-               }
-
-               return $service;
-       }
-
-       /**
-        * @param string $name
-        * @return bool Whether the service is disabled
-        * @since 1.28
-        */
-       public function isServiceDisabled( $name ) {
-               return isset( $this->disabled[$name] );
-       }
-}
diff --git a/includes/services/ServiceDisabledException.php b/includes/services/ServiceDisabledException.php
deleted file mode 100644 (file)
index ae15b7c..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-namespace MediaWiki\Services;
-
-use Exception;
-use RuntimeException;
-
-/**
- * Exception thrown when trying to access a disabled service.
- *
- * 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
- *
- * @since 1.27
- */
-
-/**
- * Exception thrown when trying to access a disabled service.
- */
-class ServiceDisabledException extends RuntimeException {
-
-       /**
-        * @param string $serviceName
-        * @param Exception|null $previous
-        */
-       public function __construct( $serviceName, Exception $previous = null ) {
-               parent::__construct( "Service disabled: $serviceName", 0, $previous );
-       }
-
-}
index 08ff8f0..e31bc06 100644 (file)
@@ -519,7 +519,7 @@ abstract class Skin extends ContextSource {
                $out = $this->getOutput();
                $allCats = $out->getCategoryLinks();
 
-               if ( !count( $allCats ) ) {
+               if ( $allCats === [] ) {
                        return '';
                }
 
index 70b4207..b05c81a 100644 (file)
@@ -431,7 +431,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
         * Attempts to clean up broken items
         */
        private function cleanupWatchlist() {
-               if ( !count( $this->badItems ) ) {
+               if ( $this->badItems === [] ) {
                        return; // nothing to do
                }
 
index 1a04eec..ba16baf 100644 (file)
@@ -62,7 +62,7 @@ class SpecialListGrants extends SpecialPage {
                                        '<span class="mw-listgrants-right-name">' . $permission . '</span>'
                                )->parse();
                        }
-                       if ( !count( $descs ) ) {
+                       if ( $descs === [] ) {
                                $grantCellHtml = '';
                        } else {
                                sort( $descs );
index 0a3a679..573dcb5 100644 (file)
@@ -151,7 +151,7 @@ class SpecialPasswordPolicies extends SpecialPage {
                                '<span class="mw-passwordpolicies-policy-name">' . $gp . '</span>'
                        )->parse();
                }
-               if ( !count( $ret ) ) {
+               if ( $ret === [] ) {
                        return '';
                } else {
                        return '<ul><li>' . implode( "</li>\n<li>", $ret ) . '</li></ul>';
index 60e797e..b566305 100644 (file)
@@ -709,7 +709,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
 
                $categories = array_map( 'trim', explode( '|', $opts['categories'] ) );
 
-               if ( !count( $categories ) ) {
+               if ( $categories === [] ) {
                        return;
                }
 
@@ -744,7 +744,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                }
 
                # Shortcut?
-               if ( !count( $articles ) || !count( $cats ) ) {
+               if ( $articles === [] || $cats === [] ) {
                        return;
                }
 
index d904ad1..ec6c5b9 100644 (file)
@@ -212,13 +212,13 @@ class SpecialSearch extends SpecialPage {
 
                # Extract manually requested namespaces
                $nslist = $this->powerSearch( $request );
-               if ( !count( $nslist ) ) {
+               if ( $nslist === [] ) {
                        # Fallback to user preference
                        $nslist = $this->searchConfig->userNamespaces( $user );
                }
 
                $profile = null;
-               if ( !count( $nslist ) ) {
+               if ( $nslist === [] ) {
                        $profile = 'default';
                }
 
index 585a7cd..9de31da 100644 (file)
@@ -55,7 +55,7 @@ class SpecialSpecialpages extends UnlistedSpecialPage {
                $pages = MediaWikiServices::getInstance()->getSpecialPageFactory()->
                        getUsablePages( $this->getUser() );
 
-               if ( !count( $pages ) ) {
+               if ( $pages === [] ) {
                        # Yeah, that was pointless. Thanks for coming.
                        return false;
                }
index 3ee7cea..4a586b7 100644 (file)
@@ -94,7 +94,7 @@ class SpecialTrackingCategories extends SpecialPage {
                        }
 
                        # Extra message, when no category was found
-                       if ( !count( $allMsgs ) ) {
+                       if ( $allMsgs === [] ) {
                                $allMsgs[] = $this->msg( 'trackingcategories-disabled' )->parse();
                        }
 
index a0b14d2..205ffbf 100644 (file)
@@ -199,7 +199,9 @@ class BlockListPager extends TablePager {
 
                                if ( !$row->ipb_sitewide && $this->restrictions ) {
                                        $list = $this->getRestrictionListHTML( $this->restrictions, $row );
-                                       $properties[] = htmlspecialchars( $msg['blocklist-editing'] ) . $list;
+                                       if ( $list ) {
+                                               $properties[] = htmlspecialchars( $msg['blocklist-editing'] ) . $list;
+                                       }
                                }
 
                                if ( $row->ipb_anon_only ) {
index 65fc4b4..79889ae 100644 (file)
@@ -1579,7 +1579,7 @@ class User implements IDBAccessObject, UserIdentity {
 
                if ( is_array( $data ) ) {
                        if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
-                               if ( !count( $data['user_groups'] ) ) {
+                               if ( $data['user_groups'] === [] ) {
                                        $this->mGroupMemberships = [];
                                } else {
                                        $firstGroup = reset( $data['user_groups'] );
@@ -1645,7 +1645,7 @@ class User implements IDBAccessObject, UserIdentity {
                }
 
                $toPromote = Autopromote::getAutopromoteOnceGroups( $this, $event );
-               if ( !count( $toPromote ) ) {
+               if ( $toPromote === [] ) {
                        return [];
                }
 
index 853601e..c0efd24 100644 (file)
@@ -45,6 +45,7 @@ class InterwikiSearchResultSetWidget implements SearchResultSetWidget {
                $this->output = $specialSearch->getOutput();
                $this->showMultimedia = $showMultimedia;
        }
+
        /**
         * @param string $term User provided search term
         * @param SearchResultSet|SearchResultSet[] $resultSets List of interwiki
index cdec27d..0240f85 100644 (file)
@@ -3938,6 +3938,7 @@ public static $zh2Hant = [
 '余威德' => '余威德',
 '余子明' => '余子明',
 '余思敏' => '余思敏',
+'余杰' => '余杰',
 '佛历' => '佛曆',
 '佛罗棱萨' => '佛羅稜薩',
 '佛钟' => '佛鐘',
@@ -5111,6 +5112,7 @@ public static $zh2Hant = [
 '年历次' => '年歷次',
 '年谷' => '年穀',
 '年里' => '年裡',
+'年里约' => '年里約',
 '年鉴' => '年鑑',
 '并州' => '并州',
 '并日而食' => '并日而食',
@@ -5500,6 +5502,7 @@ public static $zh2Hant = [
 '恒生' => '恒生',
 '恒隆' => '恒隆',
 '恕乏价催' => '恕乏价催',
+'恢复' => '恢復',
 '息交绝游' => '息交絕遊',
 '息谷' => '息穀',
 '悒郁' => '悒鬱',
@@ -7859,6 +7862,7 @@ public static $zh2Hant = [
 '复试' => '複試',
 '复课' => '複課',
 '复议' => '複議',
+'复读机' => '複讀機',
 '复变函数' => '複變函數',
 '复赛' => '複賽',
 '复辅音' => '複輔音',
@@ -11959,7 +11963,6 @@ public static $zh2Hans = [
 '趲' => '趱',
 '跡' => '迹',
 '跥' => '跺',
-'跴' => '踩',
 '踁' => '胫',
 '踐' => '践',
 '踫' => '碰',
@@ -13661,6 +13664,7 @@ public static $zh2Hans = [
 '釐正' => '厘正',
 '釐毫' => '厘毫',
 '釐清' => '厘清',
+'釐米' => '厘米',
 '釐訂' => '厘订',
 '釐革' => '厘革',
 '原著' => '原著',
@@ -14428,6 +14432,8 @@ public static $zh2TW = [
 '克林顿' => '柯林頓',
 '戴卓爾' => '柴契爾',
 '撒切尔' => '柴契爾',
+'格林納丁斯' => '格瑞那丁',
+'格林纳丁斯' => '格瑞那丁',
 '格林納達' => '格瑞那達',
 '格林纳达' => '格瑞那達',
 '桃金娘' => '桃金孃',
@@ -14448,6 +14454,7 @@ public static $zh2TW = [
 '標準杆' => '標準桿',
 '毛里求斯' => '模里西斯',
 '毛里裘斯' => '模里西斯',
+'樸茨茅夫' => '樸茨茅斯',
 '機械人' => '機器人',
 '概率' => '機率',
 '電單車' => '機車',
@@ -14588,6 +14595,7 @@ public static $zh2TW = [
 '昂山素季' => '翁山蘇姬',
 '圣基茨和尼维斯' => '聖克里斯多福及尼維斯',
 '聖吉斯納域斯' => '聖克里斯多福及尼維斯',
+'聖佐治' => '聖喬治',
 '圣文森特和格林纳丁斯' => '聖文森及格瑞那丁',
 '聖文森特和格林納丁斯' => '聖文森及格瑞那丁',
 '圣赫勒拿' => '聖赫倫那',
@@ -14772,6 +14780,8 @@ public static $zh2TW = [
 '链接' => '連結',
 '連結他' => '連結他',
 '进制' => '進位',
+'道琼斯' => '道瓊',
+'道瓊斯' => '道瓊',
 '达·芬奇' => '達·文西',
 '达芬奇' => '達文西',
 '溫納圖萬' => '那杜',
@@ -14855,7 +14865,6 @@ public static $zh2TW = [
 '高清电视' => '高畫質電視',
 '斗着' => '鬥著',
 '魯賓斯·巴里切羅' => '魯本·巴瑞切羅',
-'咪高峰' => '麥克風',
 '迈克尔' => '麥可',
 '麦克尔' => '麥可',
 '迈凯轮' => '麥拿輪',
@@ -15487,8 +15496,6 @@ public static $zh2HK = [
 '味著述' => '味著述',
 '味著錄' => '味著錄',
 '咖哩' => '咖喱',
-'麥克風' => '咪高峰',
-'麦克风' => '咪高峰',
 '咬著' => '咬着',
 '哥特式' => '哥德式',
 '哥斯大黎加' => '哥斯達黎加',
@@ -16363,6 +16370,7 @@ public static $zh2HK = [
 '奥黛丽·赫本' => '柯德莉·夏萍',
 '奧黛麗·赫本' => '柯德莉·夏萍',
 '哥廷根' => '格丁根',
+'格瑞那丁' => '格林納丁斯',
 '格瑞那達' => '格林納達',
 '格莱美奖' => '格林美獎',
 '葛萊美獎' => '格林美獎',
@@ -16397,6 +16405,8 @@ public static $zh2HK = [
 '標志著' => '標志着',
 '寶獅' => '標致',
 '標誌著' => '標誌着',
+'朴茨茅斯' => '樸茨茅夫',
+'樸茨茅斯' => '樸茨茅夫',
 '树林里' => '樹林裏',
 '工具機' => '機床',
 '机器人' => '機械人',
@@ -17069,6 +17079,8 @@ public static $zh2HK = [
 '考著者' => '考著者',
 '考著述' => '考著述',
 '考著錄' => '考著錄',
+'圣乔治' => '聖佐治',
+'聖喬治' => '聖佐治',
 '圣基茨和尼维斯' => '聖吉斯納域斯',
 '聖克里斯多福及尼維斯' => '聖吉斯納域斯',
 '聖文森及格瑞那丁' => '聖文森特和格林納丁斯',
@@ -17568,6 +17580,8 @@ public static $zh2HK = [
 '遍佈著' => '遍佈着',
 '遍布著' => '遍佈着',
 '過著' => '過着',
+'道瓊' => '道瓊斯',
+'道瓊斯' => '道瓊斯',
 '达·芬奇' => '達·文西',
 '达芬奇' => '達文西',
 '達著' => '達着',
@@ -18578,6 +18592,7 @@ public static $zh2CN = [
 '土魯斯' => '图卢兹',
 '吐瓦魯' => '图瓦卢',
 '原子筆' => '圆珠笔',
+'聖佐治' => '圣乔治',
 '汕埠' => '圣佩德罗苏拉',
 '聖露西亞' => '圣卢西亚',
 '聖克里斯多福及尼維斯' => '圣基茨和尼维斯',
@@ -19308,6 +19323,7 @@ public static $zh2CN = [
 '本著者' => '本著者',
 '本著述' => '本著述',
 '本帳' => '本账',
+'樸茨茅夫' => '朴茨茅斯',
 '機械人' => '机器人',
 '工具機' => '机床',
 '殺著' => '杀着',
@@ -19349,6 +19365,7 @@ public static $zh2CN = [
 '查維茲' => '查韦斯',
 '標志著' => '标志着',
 '標誌著' => '标志着',
+'格瑞那丁' => '格林纳丁斯',
 '格瑞那達' => '格林纳达',
 '格林美獎' => '格莱美奖',
 '葛萊美獎' => '格莱美奖',
@@ -19596,6 +19613,7 @@ public static $zh2CN = [
 '畫著稱' => '画著称',
 '畫著者' => '画著者',
 '介面' => '界面',
+'留尼旺' => '留尼汪',
 '留著' => '留着',
 '留著書' => '留着书',
 '留著作' => '留著作',
@@ -20397,6 +20415,8 @@ public static $zh2CN = [
 '遇著述' => '遇著述',
 '遍佈著' => '遍布着',
 '遍布著' => '遍布着',
+'道瓊' => '道琼斯',
+'道瓊斯' => '道琼斯',
 '部份' => '部分',
 '配合著' => '配合着',
 '配合著名' => '配合著名',
@@ -20641,7 +20661,6 @@ public static $zh2CN = [
 '高著述' => '高著述',
 '魚雷' => '鱼雷',
 '鱼雷' => '鱼雷',
-'咪高峰' => '麦克风',
 '黏著' => '黏着',
 '黏著書' => '黏著书',
 '黏著作' => '黏著作',
index d7a7cc8..0971005 100644 (file)
@@ -23,8 +23,8 @@ $namespaceNames = [
        NS_FILE_TALK        => 'फायल_चर्चा',
        NS_MEDIAWIKI        => 'मिडियाविकी',
        NS_MEDIAWIKI_TALK   => 'मिडियाविकी_चर्चा',
-       NS_TEMPLATE         => 'पà¥\8dरारà¥\82प',
-       NS_TEMPLATE_TALK    => 'पà¥\8dरारà¥\82प_चर्चा',
+       NS_TEMPLATE         => 'साà¤\82à¤\9aà¥\8b',
+       NS_TEMPLATE_TALK    => 'साà¤\82à¤\9aà¥\8b_चर्चा',
        NS_HELP             => 'आदार',
        NS_HELP_TALK        => 'आदार_चर्चा',
        NS_CATEGORY         => 'वर्ग',
@@ -38,5 +38,7 @@ $namespaceAliases = [
        'श्रेणी_चर्चा' => NS_CATEGORY_TALK,
        'मिडिया' => NS_MEDIA,
        'उपेगकर्तो' => NS_USER,
-       'उपेगकर्तो_चर्चा' => NS_USER_TALK
+       'उपेगकर्तो_चर्चा' => NS_USER_TALK,
+       'प्रारूप' => NS_TEMPLATE,
+       'प्रारूप_चर्चा' => NS_TEMPLATE_TALK,
 ];
index 31abd35..6c34e0b 100644 (file)
@@ -2650,7 +2650,6 @@ A型肝炎        甲型肝炎
 數碼訊號   数字信号
 數位音樂   数字音乐
 數位化      数字化
-咪高峰      麦克风
 幫浦 泵
 電單車      摩托车
 演化論      进化论
@@ -2710,3 +2709,9 @@ A型肝炎        甲型肝炎
 皮特肯      皮特凯恩
 安地卡      安提瓜
 撒拉威阿拉伯     阿拉伯撒哈拉
+樸茨茅夫   朴茨茅斯
+留尼旺      留尼汪
+道瓊斯      道琼斯
+道瓊 道琼斯
+聖佐治      圣乔治
+格瑞那丁   格林纳丁斯
index 1ad4540..93acb33 100644 (file)
@@ -3023,8 +3023,6 @@ IP地址  IP位址
 數位音樂   數碼音樂
 数字化      數碼化
 數位化      數碼化
-麥克風      咪高峰
-麦克风      咪高峰
 幫浦 泵
 朝鲜战争   韓戰
 万历朝鲜战争     萬曆朝鮮戰爭
@@ -3073,3 +3071,10 @@ IP地址 IP位址
 皮特凯恩   皮特肯
 安地卡      安提瓜
 撒拉威阿拉伯     阿拉伯撒哈拉
+朴茨茅斯   樸茨茅夫
+樸茨茅斯   樸茨茅夫
+道瓊斯      道瓊斯
+道瓊 道瓊斯
+圣乔治      聖佐治
+聖喬治      聖佐治
+格瑞那丁   格林納丁斯
index 5ad8eeb..aacec98 100644 (file)
 釐正 厘正
 毫釐 毫厘
 釐毫 厘毫
+釐米 厘米
 剖釐 剖厘
 一釐 一厘
 昇平 升平
index 28055e1..dd8e5d0 100644 (file)
@@ -768,7 +768,6 @@ IP地址    IP位址
 網絡遊戲   網路遊戲
 电脑网络   電腦網路
 電腦網絡   電腦網路
-咪高峰      麥克風
 電單車      機車
 搜索引擎   搜尋引擎
 福尔马林   福馬林
@@ -815,3 +814,10 @@ IP地址   IP位址
 皮特凯恩   皮特肯
 安提瓜      安地卡
 阿拉伯撒哈拉     撒拉威阿拉伯
+樸茨茅夫   樸茨茅斯
+留尼汪      留尼旺
+道琼斯      道瓊
+道瓊斯      道瓊
+聖佐治      聖喬治
+格林纳丁斯        格瑞那丁
+格林納丁斯        格瑞那丁
index 5bfe18f..a3565b8 100644 (file)
@@ -22,6 +22,7 @@
 陳杰 陳杰
 黃杰 黃杰
 謝杰 謝杰
+余杰 余杰
 博杰普爾   博杰普爾
 寶曆 寶曆
 涂謹申      涂謹申
index 7206924..8d09901 100644 (file)
@@ -690,7 +690,6 @@ U+08D11贑|U+08D63赣|
 U+08D1C贜|U+08D43赃|
 U+08D82趂|U+08D81趁|
 U+08DE5跥|U+08DFA跺|
-U+08DF4跴|U+08E29踩|
 U+08E01踁|U+080EB胫|
 U+08E2B踫|U+078B0碰|
 U+08E30踰|U+0903E逾|
index 327f8f3..24d8a42 100644 (file)
 張三丰
 復始
 往復式
+恢復 #分詞用
 複分析
 複輔音
 複元音
 複合 #因複合詞頻遠高於復合
 複方
 複穗
+複讀機
 撥穀
 扁擬穀盜蟲
 不穀
 孛里海
 布里海
 公里海
+年里約 #里约奧運
 地圖裡
 版圖裡
 配圖裡
index 6ad7917..cedb19d 100644 (file)
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <title>remove</title>
     <g id="remove">
         <path id="trash-can" d="M12 10h-1v6h1v-6zm-2 0h-1v6h1v-6zm4 0h-1v6h1v-6zm0-4v-1h-5v1h-3v3h1v7.966l1 1.031v-.074.077h6.984l.016-.018v.015l1-1.031v-7.966h1v-3h-3zm1 11h-7v-8h7v8zm1-9h-9v-1h9v1z"/>
     </g>
index bcbe871..641bb78 100644 (file)
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="red">
-    <g xmlns:default="http://www.w3.org/2000/svg" id="remove">
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>remove</title><g fill="red">
+    
+    <g id="remove">
         <path id="trash-can" d="M12 10h-1v6h1v-6zm-2 0h-1v6h1v-6zm4 0h-1v6h1v-6zm0-4v-1h-5v1h-3v3h1v7.966l1 1.031v-.074.077h6.984l.016-.018v.015l1-1.031v-7.966h1v-3h-3zm1 11h-7v-8h7v8zm1-9h-9v-1h9v1z"/>
     </g>
 </g></svg>
index ab9a472..1cd40ed 100644 (file)
@@ -1,9 +1,9 @@
 <?php
 
 use MediaWiki\MediaWikiServices;
-use MediaWiki\Services\DestructibleService;
-use MediaWiki\Services\SalvageableService;
-use MediaWiki\Services\ServiceDisabledException;
+use Wikimedia\Services\DestructibleService;
+use Wikimedia\Services\SalvageableService;
+use Wikimedia\Services\ServiceDisabledException;
 
 /**
  * @covers MediaWiki\MediaWikiServices
@@ -219,7 +219,7 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                        'Test',
                        function () use ( &$serviceCounter ) {
                                $serviceCounter++;
-                               $service = $this->createMock( MediaWiki\Services\DestructibleService::class );
+                               $service = $this->createMock( Wikimedia\Services\DestructibleService::class );
                                $service->expects( $this->once() )->method( 'destroy' );
                                return $service;
                        }
@@ -248,7 +248,7 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                $services->defineService(
                        'Test',
                        function () {
-                               $service = $this->createMock( MediaWiki\Services\DestructibleService::class );
+                               $service = $this->createMock( Wikimedia\Services\DestructibleService::class );
                                $service->expects( $this->never() )->method( 'destroy' );
                                return $service;
                        }
index cf5e808..68632f3 100644 (file)
@@ -102,6 +102,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $this->testPageTitle = Title::newFromText( 'UTPage-' . __CLASS__ );
                return $this->testPageTitle;
        }
+
        /**
         * @return WikiPage
         */
diff --git a/tests/phpunit/includes/libs/services/ServiceContainerTest.php b/tests/phpunit/includes/libs/services/ServiceContainerTest.php
new file mode 100644 (file)
index 0000000..6674a15
--- /dev/null
@@ -0,0 +1,496 @@
+<?php
+use Wikimedia\Services\ServiceContainer;
+
+/**
+ * @covers Wikimedia\Services\ServiceContainer
+ */
+class ServiceContainerTest extends PHPUnit\Framework\TestCase {
+
+       use MediaWikiCoversValidator; // TODO this library is supposed to be independent of MediaWiki
+       use PHPUnit4And6Compat;
+
+       private function newServiceContainer( $extraArgs = [] ) {
+               return new ServiceContainer( $extraArgs );
+       }
+
+       public function testGetServiceNames() {
+               $services = $this->newServiceContainer();
+               $names = $services->getServiceNames();
+
+               $this->assertInternalType( 'array', $names );
+               $this->assertEmpty( $names );
+
+               $name = 'TestService92834576';
+               $services->defineService( $name, function () {
+                       return null;
+               } );
+
+               $names = $services->getServiceNames();
+               $this->assertContains( $name, $names );
+       }
+
+       public function testHasService() {
+               $services = $this->newServiceContainer();
+
+               $name = 'TestService92834576';
+               $this->assertFalse( $services->hasService( $name ) );
+
+               $services->defineService( $name, function () {
+                       return null;
+               } );
+
+               $this->assertTrue( $services->hasService( $name ) );
+       }
+
+       public function testGetService() {
+               $services = $this->newServiceContainer( [ 'Foo' ] );
+
+               $theService = new stdClass();
+               $name = 'TestService92834576';
+               $count = 0;
+
+               $services->defineService(
+                       $name,
+                       function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
+                               $count++;
+                               PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+                               PHPUnit_Framework_Assert::assertSame( $extra, 'Foo' );
+                               return $theService;
+                       }
+               );
+
+               $this->assertSame( $theService, $services->getService( $name ) );
+
+               $services->getService( $name );
+               $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
+       }
+
+       public function testGetService_fail_unknown() {
+               $services = $this->newServiceContainer();
+
+               $name = 'TestService92834576';
+
+               $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+               $services->getService( $name );
+       }
+
+       public function testPeekService() {
+               $services = $this->newServiceContainer();
+
+               $services->defineService(
+                       'Foo',
+                       function () {
+                               return new stdClass();
+                       }
+               );
+
+               $services->defineService(
+                       'Bar',
+                       function () {
+                               return new stdClass();
+                       }
+               );
+
+               // trigger instantiation of Foo
+               $services->getService( 'Foo' );
+
+               $this->assertInternalType(
+                       'object',
+                       $services->peekService( 'Foo' ),
+                       'Peek should return the service object if it had been accessed before.'
+               );
+
+               $this->assertNull(
+                       $services->peekService( 'Bar' ),
+                       'Peek should return null if the service was never accessed.'
+               );
+       }
+
+       public function testPeekService_fail_unknown() {
+               $services = $this->newServiceContainer();
+
+               $name = 'TestService92834576';
+
+               $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+               $services->peekService( $name );
+       }
+
+       public function testDefineService() {
+               $services = $this->newServiceContainer();
+
+               $theService = new stdClass();
+               $name = 'TestService92834576';
+
+               $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
+                       PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+                       return $theService;
+               } );
+
+               $this->assertTrue( $services->hasService( $name ) );
+               $this->assertSame( $theService, $services->getService( $name ) );
+       }
+
+       public function testDefineService_fail_duplicate() {
+               $services = $this->newServiceContainer();
+
+               $theService = new stdClass();
+               $name = 'TestService92834576';
+
+               $services->defineService( $name, function () use ( $theService ) {
+                       return $theService;
+               } );
+
+               $this->setExpectedException( Wikimedia\Services\ServiceAlreadyDefinedException::class );
+
+               $services->defineService( $name, function () use ( $theService ) {
+                       return $theService;
+               } );
+       }
+
+       public function testApplyWiring() {
+               $services = $this->newServiceContainer();
+
+               $wiring = [
+                       'Foo' => function () {
+                               return 'Foo!';
+                       },
+                       'Bar' => function () {
+                               return 'Bar!';
+                       },
+               ];
+
+               $services->applyWiring( $wiring );
+
+               $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
+               $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
+       }
+
+       public function testImportWiring() {
+               $services = $this->newServiceContainer();
+
+               $wiring = [
+                       'Foo' => function () {
+                               return 'Foo!';
+                       },
+                       'Bar' => function () {
+                               return 'Bar!';
+                       },
+                       'Car' => function () {
+                               return 'FUBAR!';
+                       },
+               ];
+
+               $services->applyWiring( $wiring );
+
+               $services->addServiceManipulator( 'Foo', function ( $service ) {
+                       return $service . '+X';
+               } );
+
+               $services->addServiceManipulator( 'Car', function ( $service ) {
+                       return $service . '+X';
+               } );
+
+               $newServices = $this->newServiceContainer();
+
+               // create a service with manipulator
+               $newServices->defineService( 'Foo', function () {
+                       return 'Foo!';
+               } );
+
+               $newServices->addServiceManipulator( 'Foo', function ( $service ) {
+                       return $service . '+Y';
+               } );
+
+               // create a service before importing, so we can later check that
+               // existing service instances survive importWiring()
+               $newServices->defineService( 'Car', function () {
+                       return 'Car!';
+               } );
+
+               // force instantiation
+               $newServices->getService( 'Car' );
+
+               // Define another service, so we can later check that extra wiring
+               // is not lost.
+               $newServices->defineService( 'Xar', function () {
+                       return 'Xar!';
+               } );
+
+               // import wiring, but skip `Bar`
+               $newServices->importWiring( $services, [ 'Bar' ] );
+
+               $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
+               $this->assertSame( 'Foo!+Y+X', $newServices->getService( 'Foo' ) );
+
+               // import all wiring, but preserve existing service instance
+               $newServices->importWiring( $services );
+
+               $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
+               $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
+               $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
+               $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
+       }
+
+       public function testLoadWiringFiles() {
+               $services = $this->newServiceContainer();
+
+               $wiringFiles = [
+                       __DIR__ . '/TestWiring1.php',
+                       __DIR__ . '/TestWiring2.php',
+               ];
+
+               $services->loadWiringFiles( $wiringFiles );
+
+               $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
+               $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
+       }
+
+       public function testLoadWiringFiles_fail_duplicate() {
+               $services = $this->newServiceContainer();
+
+               $wiringFiles = [
+                       __DIR__ . '/TestWiring1.php',
+                       __DIR__ . '/./TestWiring1.php',
+               ];
+
+               // loading the same file twice should fail, because
+               $this->setExpectedException( Wikimedia\Services\ServiceAlreadyDefinedException::class );
+
+               $services->loadWiringFiles( $wiringFiles );
+       }
+
+       public function testRedefineService() {
+               $services = $this->newServiceContainer( [ 'Foo' ] );
+
+               $theService1 = new stdClass();
+               $name = 'TestService92834576';
+
+               $services->defineService( $name, function () {
+                       PHPUnit_Framework_Assert::fail(
+                               'The original instantiator function should not get called'
+                       );
+               } );
+
+               // redefine before instantiation
+               $services->redefineService(
+                       $name,
+                       function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
+                               PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+                               PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
+                               return $theService1;
+                       }
+               );
+
+               // force instantiation, check result
+               $this->assertSame( $theService1, $services->getService( $name ) );
+       }
+
+       public function testRedefineService_disabled() {
+               $services = $this->newServiceContainer( [ 'Foo' ] );
+
+               $theService1 = new stdClass();
+               $name = 'TestService92834576';
+
+               $services->defineService( $name, function () {
+                       return 'Foo';
+               } );
+
+               // disable the service. we should be able to redefine it anyway.
+               $services->disableService( $name );
+
+               $services->redefineService( $name, function () use ( $theService1 ) {
+                       return $theService1;
+               } );
+
+               // force instantiation, check result
+               $this->assertSame( $theService1, $services->getService( $name ) );
+       }
+
+       public function testRedefineService_fail_undefined() {
+               $services = $this->newServiceContainer();
+
+               $theService = new stdClass();
+               $name = 'TestService92834576';
+
+               $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+               $services->redefineService( $name, function () use ( $theService ) {
+                       return $theService;
+               } );
+       }
+
+       public function testRedefineService_fail_in_use() {
+               $services = $this->newServiceContainer( [ 'Foo' ] );
+
+               $theService = new stdClass();
+               $name = 'TestService92834576';
+
+               $services->defineService( $name, function () {
+                       return 'Foo';
+               } );
+
+               // create the service, so it can no longer be redefined
+               $services->getService( $name );
+
+               $this->setExpectedException( Wikimedia\Services\CannotReplaceActiveServiceException::class );
+
+               $services->redefineService( $name, function () use ( $theService ) {
+                       return $theService;
+               } );
+       }
+
+       public function testAddServiceManipulator() {
+               $services = $this->newServiceContainer( [ 'Foo' ] );
+
+               $theService1 = new stdClass();
+               $theService2 = new stdClass();
+               $name = 'TestService92834576';
+
+               $services->defineService(
+                       $name,
+                       function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
+                               PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+                               PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
+                               return $theService1;
+                       }
+               );
+
+               $services->addServiceManipulator(
+                       $name,
+                       function (
+                               $theService, $actualLocator, $extra
+                       ) use (
+                               $services, $theService1, $theService2
+                       ) {
+                               PHPUnit_Framework_Assert::assertSame( $theService1, $theService );
+                               PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
+                               PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
+                               return $theService2;
+                       }
+               );
+
+               // force instantiation, check result
+               $this->assertSame( $theService2, $services->getService( $name ) );
+       }
+
+       public function testAddServiceManipulator_fail_undefined() {
+               $services = $this->newServiceContainer();
+
+               $theService = new stdClass();
+               $name = 'TestService92834576';
+
+               $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+               $services->addServiceManipulator( $name, function () use ( $theService ) {
+                       return $theService;
+               } );
+       }
+
+       public function testAddServiceManipulator_fail_in_use() {
+               $services = $this->newServiceContainer( [ 'Foo' ] );
+
+               $theService = new stdClass();
+               $name = 'TestService92834576';
+
+               $services->defineService( $name, function () use ( $theService ) {
+                       return $theService;
+               } );
+
+               // create the service, so it can no longer be redefined
+               $services->getService( $name );
+
+               $this->setExpectedException( Wikimedia\Services\CannotReplaceActiveServiceException::class );
+
+               $services->addServiceManipulator( $name, function () {
+                       return 'Foo';
+               } );
+       }
+
+       public function testDisableService() {
+               $services = $this->newServiceContainer( [ 'Foo' ] );
+
+               $destructible = $this->getMockBuilder( Wikimedia\Services\DestructibleService::class )
+                       ->getMock();
+               $destructible->expects( $this->once() )
+                       ->method( 'destroy' );
+
+               $services->defineService( 'Foo', function () use ( $destructible ) {
+                       return $destructible;
+               } );
+               $services->defineService( 'Bar', function () {
+                       return new stdClass();
+               } );
+               $services->defineService( 'Qux', function () {
+                       return new stdClass();
+               } );
+
+               // instantiate Foo and Bar services
+               $services->getService( 'Foo' );
+               $services->getService( 'Bar' );
+
+               // disable service, should call destroy() once.
+               $services->disableService( 'Foo' );
+
+               // disabled service should still be listed
+               $this->assertContains( 'Foo', $services->getServiceNames() );
+
+               // getting other services should still work
+               $services->getService( 'Bar' );
+
+               // disable non-destructible service, and not-yet-instantiated service
+               $services->disableService( 'Bar' );
+               $services->disableService( 'Qux' );
+
+               $this->assertNull( $services->peekService( 'Bar' ) );
+               $this->assertNull( $services->peekService( 'Qux' ) );
+
+               // disabled service should still be listed
+               $this->assertContains( 'Bar', $services->getServiceNames() );
+               $this->assertContains( 'Qux', $services->getServiceNames() );
+
+               $this->setExpectedException( Wikimedia\Services\ServiceDisabledException::class );
+               $services->getService( 'Qux' );
+       }
+
+       public function testDisableService_fail_undefined() {
+               $services = $this->newServiceContainer();
+
+               $theService = new stdClass();
+               $name = 'TestService92834576';
+
+               $this->setExpectedException( Wikimedia\Services\NoSuchServiceException::class );
+
+               $services->redefineService( $name, function () use ( $theService ) {
+                       return $theService;
+               } );
+       }
+
+       public function testDestroy() {
+               $services = $this->newServiceContainer();
+
+               $destructible = $this->getMockBuilder( Wikimedia\Services\DestructibleService::class )
+                       ->getMock();
+               $destructible->expects( $this->once() )
+                       ->method( 'destroy' );
+
+               $services->defineService( 'Foo', function () use ( $destructible ) {
+                       return $destructible;
+               } );
+
+               $services->defineService( 'Bar', function () {
+                       return new stdClass();
+               } );
+
+               // create the service
+               $services->getService( 'Foo' );
+
+               // destroy the container
+               $services->destroy();
+
+               $this->setExpectedException( Wikimedia\Services\ContainerDisabledException::class );
+               $services->getService( 'Bar' );
+       }
+
+}
diff --git a/tests/phpunit/includes/libs/services/TestWiring1.php b/tests/phpunit/includes/libs/services/TestWiring1.php
new file mode 100644 (file)
index 0000000..b6ff4eb
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Test file for testing ServiceContainer::loadWiringFiles
+ */
+
+return [
+       'Foo' => function () {
+               return 'Foo!';
+       },
+];
diff --git a/tests/phpunit/includes/libs/services/TestWiring2.php b/tests/phpunit/includes/libs/services/TestWiring2.php
new file mode 100644 (file)
index 0000000..dfff64f
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Test file for testing ServiceContainer::loadWiringFiles
+ */
+
+return [
+       'Bar' => function () {
+               return 'Bar!';
+       },
+];
index a0a99cc..ac0ad98 100644 (file)
@@ -9,10 +9,12 @@ class WebPHandlerTest extends MediaWikiTestCase {
                // Allocated file for testing
                $this->tempFileName = tempnam( wfTempDir(), 'WEBP' );
        }
+
        public function tearDown() {
                parent::tearDown();
                unlink( $this->tempFileName );
        }
+
        /**
         * @dataProvider provideTestExtractMetaData
         */
@@ -22,6 +24,7 @@ class WebPHandlerTest extends MediaWikiTestCase {
 
                $this->assertEquals( $expectedResult, WebPHandler::extractMetadata( $this->tempFileName ) );
        }
+
        public function provideTestExtractMetaData() {
                // phpcs:disable Generic.Files.LineLength
                return [
@@ -80,6 +83,7 @@ class WebPHandlerTest extends MediaWikiTestCase {
        public function testWithFileExtractMetaData( $filename, $expectedResult ) {
                $this->assertEquals( $expectedResult, WebPHandler::extractMetadata( $filename ) );
        }
+
        public function provideTestWithFileExtractMetaData() {
                return [
                        [ __DIR__ . '/../../data/media/2_webp_ll.webp',
@@ -108,6 +112,7 @@ class WebPHandlerTest extends MediaWikiTestCase {
                $handler = new WebPHandler();
                $this->assertEquals( $expectedResult, $handler->getImageSize( null, $path ) );
        }
+
        public function provideTestGetImageSize() {
                return [
                        // Public domain files from https://developers.google.com/speed/webp/gallery2
@@ -130,6 +135,7 @@ class WebPHandlerTest extends MediaWikiTestCase {
                $mime = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
                $this->assertEquals( 'image/webp', $mime->guessMimeType( $path, false ) );
        }
+
        public function provideTestGetMimeType() {
                return [
                                // Public domain files from https://developers.google.com/speed/webp/gallery2
index 19774f0..016a9a0 100644 (file)
@@ -118,6 +118,7 @@ class PoolWorkArticleViewTest extends MediaWikiTestCase {
                        }
                ];
        }
+
        /**
         * @dataProvider provideMagicWords
         */
index bb51de0..318dce8 100644 (file)
@@ -31,6 +31,7 @@ class ResourceLoaderLessVarFileModuleTest extends ResourceLoaderTestCase {
                        ]
                ];
        }
+
        /**
         * @dataProvider providerWrapAndEscapeMessage
         * @covers ResourceLoaderLessVarFileModule::wrapAndEscapeMessage
index aa3f820..2ee85b5 100644 (file)
@@ -499,6 +499,7 @@ mw.loader.register( [
                        ] ]
                ];
        }
+
        /**
         * @covers ResourceLoaderStartUpModule::getModuleRegistrations
         * @dataProvider provideRegistrations
index 171f2a6..32afd75 100644 (file)
@@ -837,6 +837,7 @@ mw.example();
                        $response
                );
        }
+
        /**
         * Verify that when building the startup module response,
         * an exception from one module class will not break the entire
diff --git a/tests/phpunit/includes/services/ServiceContainerTest.php b/tests/phpunit/includes/services/ServiceContainerTest.php
deleted file mode 100644 (file)
index aca88aa..0000000
+++ /dev/null
@@ -1,498 +0,0 @@
-<?php
-use MediaWiki\Services\ServiceContainer;
-
-/**
- * @covers MediaWiki\Services\ServiceContainer
- *
- * @group MediaWiki
- */
-class ServiceContainerTest extends PHPUnit\Framework\TestCase {
-
-       use MediaWikiCoversValidator;
-       use PHPUnit4And6Compat;
-
-       private function newServiceContainer( $extraArgs = [] ) {
-               return new ServiceContainer( $extraArgs );
-       }
-
-       public function testGetServiceNames() {
-               $services = $this->newServiceContainer();
-               $names = $services->getServiceNames();
-
-               $this->assertInternalType( 'array', $names );
-               $this->assertEmpty( $names );
-
-               $name = 'TestService92834576';
-               $services->defineService( $name, function () {
-                       return null;
-               } );
-
-               $names = $services->getServiceNames();
-               $this->assertContains( $name, $names );
-       }
-
-       public function testHasService() {
-               $services = $this->newServiceContainer();
-
-               $name = 'TestService92834576';
-               $this->assertFalse( $services->hasService( $name ) );
-
-               $services->defineService( $name, function () {
-                       return null;
-               } );
-
-               $this->assertTrue( $services->hasService( $name ) );
-       }
-
-       public function testGetService() {
-               $services = $this->newServiceContainer( [ 'Foo' ] );
-
-               $theService = new stdClass();
-               $name = 'TestService92834576';
-               $count = 0;
-
-               $services->defineService(
-                       $name,
-                       function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
-                               $count++;
-                               PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
-                               PHPUnit_Framework_Assert::assertSame( $extra, 'Foo' );
-                               return $theService;
-                       }
-               );
-
-               $this->assertSame( $theService, $services->getService( $name ) );
-
-               $services->getService( $name );
-               $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
-       }
-
-       public function testGetService_fail_unknown() {
-               $services = $this->newServiceContainer();
-
-               $name = 'TestService92834576';
-
-               $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
-               $services->getService( $name );
-       }
-
-       public function testPeekService() {
-               $services = $this->newServiceContainer();
-
-               $services->defineService(
-                       'Foo',
-                       function () {
-                               return new stdClass();
-                       }
-               );
-
-               $services->defineService(
-                       'Bar',
-                       function () {
-                               return new stdClass();
-                       }
-               );
-
-               // trigger instantiation of Foo
-               $services->getService( 'Foo' );
-
-               $this->assertInternalType(
-                       'object',
-                       $services->peekService( 'Foo' ),
-                       'Peek should return the service object if it had been accessed before.'
-               );
-
-               $this->assertNull(
-                       $services->peekService( 'Bar' ),
-                       'Peek should return null if the service was never accessed.'
-               );
-       }
-
-       public function testPeekService_fail_unknown() {
-               $services = $this->newServiceContainer();
-
-               $name = 'TestService92834576';
-
-               $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
-               $services->peekService( $name );
-       }
-
-       public function testDefineService() {
-               $services = $this->newServiceContainer();
-
-               $theService = new stdClass();
-               $name = 'TestService92834576';
-
-               $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
-                       PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
-                       return $theService;
-               } );
-
-               $this->assertTrue( $services->hasService( $name ) );
-               $this->assertSame( $theService, $services->getService( $name ) );
-       }
-
-       public function testDefineService_fail_duplicate() {
-               $services = $this->newServiceContainer();
-
-               $theService = new stdClass();
-               $name = 'TestService92834576';
-
-               $services->defineService( $name, function () use ( $theService ) {
-                       return $theService;
-               } );
-
-               $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException::class );
-
-               $services->defineService( $name, function () use ( $theService ) {
-                       return $theService;
-               } );
-       }
-
-       public function testApplyWiring() {
-               $services = $this->newServiceContainer();
-
-               $wiring = [
-                       'Foo' => function () {
-                               return 'Foo!';
-                       },
-                       'Bar' => function () {
-                               return 'Bar!';
-                       },
-               ];
-
-               $services->applyWiring( $wiring );
-
-               $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
-               $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
-       }
-
-       public function testImportWiring() {
-               $services = $this->newServiceContainer();
-
-               $wiring = [
-                       'Foo' => function () {
-                               return 'Foo!';
-                       },
-                       'Bar' => function () {
-                               return 'Bar!';
-                       },
-                       'Car' => function () {
-                               return 'FUBAR!';
-                       },
-               ];
-
-               $services->applyWiring( $wiring );
-
-               $services->addServiceManipulator( 'Foo', function ( $service ) {
-                       return $service . '+X';
-               } );
-
-               $services->addServiceManipulator( 'Car', function ( $service ) {
-                       return $service . '+X';
-               } );
-
-               $newServices = $this->newServiceContainer();
-
-               // create a service with manipulator
-               $newServices->defineService( 'Foo', function () {
-                       return 'Foo!';
-               } );
-
-               $newServices->addServiceManipulator( 'Foo', function ( $service ) {
-                       return $service . '+Y';
-               } );
-
-               // create a service before importing, so we can later check that
-               // existing service instances survive importWiring()
-               $newServices->defineService( 'Car', function () {
-                       return 'Car!';
-               } );
-
-               // force instantiation
-               $newServices->getService( 'Car' );
-
-               // Define another service, so we can later check that extra wiring
-               // is not lost.
-               $newServices->defineService( 'Xar', function () {
-                       return 'Xar!';
-               } );
-
-               // import wiring, but skip `Bar`
-               $newServices->importWiring( $services, [ 'Bar' ] );
-
-               $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
-               $this->assertSame( 'Foo!+Y+X', $newServices->getService( 'Foo' ) );
-
-               // import all wiring, but preserve existing service instance
-               $newServices->importWiring( $services );
-
-               $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
-               $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
-               $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
-               $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
-       }
-
-       public function testLoadWiringFiles() {
-               $services = $this->newServiceContainer();
-
-               $wiringFiles = [
-                       __DIR__ . '/TestWiring1.php',
-                       __DIR__ . '/TestWiring2.php',
-               ];
-
-               $services->loadWiringFiles( $wiringFiles );
-
-               $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
-               $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
-       }
-
-       public function testLoadWiringFiles_fail_duplicate() {
-               $services = $this->newServiceContainer();
-
-               $wiringFiles = [
-                       __DIR__ . '/TestWiring1.php',
-                       __DIR__ . '/./TestWiring1.php',
-               ];
-
-               // loading the same file twice should fail, because
-               $this->setExpectedException( MediaWiki\Services\ServiceAlreadyDefinedException::class );
-
-               $services->loadWiringFiles( $wiringFiles );
-       }
-
-       public function testRedefineService() {
-               $services = $this->newServiceContainer( [ 'Foo' ] );
-
-               $theService1 = new stdClass();
-               $name = 'TestService92834576';
-
-               $services->defineService( $name, function () {
-                       PHPUnit_Framework_Assert::fail(
-                               'The original instantiator function should not get called'
-                       );
-               } );
-
-               // redefine before instantiation
-               $services->redefineService(
-                       $name,
-                       function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
-                               PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
-                               PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
-                               return $theService1;
-                       }
-               );
-
-               // force instantiation, check result
-               $this->assertSame( $theService1, $services->getService( $name ) );
-       }
-
-       public function testRedefineService_disabled() {
-               $services = $this->newServiceContainer( [ 'Foo' ] );
-
-               $theService1 = new stdClass();
-               $name = 'TestService92834576';
-
-               $services->defineService( $name, function () {
-                       return 'Foo';
-               } );
-
-               // disable the service. we should be able to redefine it anyway.
-               $services->disableService( $name );
-
-               $services->redefineService( $name, function () use ( $theService1 ) {
-                       return $theService1;
-               } );
-
-               // force instantiation, check result
-               $this->assertSame( $theService1, $services->getService( $name ) );
-       }
-
-       public function testRedefineService_fail_undefined() {
-               $services = $this->newServiceContainer();
-
-               $theService = new stdClass();
-               $name = 'TestService92834576';
-
-               $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
-               $services->redefineService( $name, function () use ( $theService ) {
-                       return $theService;
-               } );
-       }
-
-       public function testRedefineService_fail_in_use() {
-               $services = $this->newServiceContainer( [ 'Foo' ] );
-
-               $theService = new stdClass();
-               $name = 'TestService92834576';
-
-               $services->defineService( $name, function () {
-                       return 'Foo';
-               } );
-
-               // create the service, so it can no longer be redefined
-               $services->getService( $name );
-
-               $this->setExpectedException( MediaWiki\Services\CannotReplaceActiveServiceException::class );
-
-               $services->redefineService( $name, function () use ( $theService ) {
-                       return $theService;
-               } );
-       }
-
-       public function testAddServiceManipulator() {
-               $services = $this->newServiceContainer( [ 'Foo' ] );
-
-               $theService1 = new stdClass();
-               $theService2 = new stdClass();
-               $name = 'TestService92834576';
-
-               $services->defineService(
-                       $name,
-                       function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
-                               PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
-                               PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
-                               return $theService1;
-                       }
-               );
-
-               $services->addServiceManipulator(
-                       $name,
-                       function (
-                               $theService, $actualLocator, $extra
-                       ) use (
-                               $services, $theService1, $theService2
-                       ) {
-                               PHPUnit_Framework_Assert::assertSame( $theService1, $theService );
-                               PHPUnit_Framework_Assert::assertSame( $services, $actualLocator );
-                               PHPUnit_Framework_Assert::assertSame( 'Foo', $extra );
-                               return $theService2;
-                       }
-               );
-
-               // force instantiation, check result
-               $this->assertSame( $theService2, $services->getService( $name ) );
-       }
-
-       public function testAddServiceManipulator_fail_undefined() {
-               $services = $this->newServiceContainer();
-
-               $theService = new stdClass();
-               $name = 'TestService92834576';
-
-               $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
-               $services->addServiceManipulator( $name, function () use ( $theService ) {
-                       return $theService;
-               } );
-       }
-
-       public function testAddServiceManipulator_fail_in_use() {
-               $services = $this->newServiceContainer( [ 'Foo' ] );
-
-               $theService = new stdClass();
-               $name = 'TestService92834576';
-
-               $services->defineService( $name, function () use ( $theService ) {
-                       return $theService;
-               } );
-
-               // create the service, so it can no longer be redefined
-               $services->getService( $name );
-
-               $this->setExpectedException( MediaWiki\Services\CannotReplaceActiveServiceException::class );
-
-               $services->addServiceManipulator( $name, function () {
-                       return 'Foo';
-               } );
-       }
-
-       public function testDisableService() {
-               $services = $this->newServiceContainer( [ 'Foo' ] );
-
-               $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService::class )
-                       ->getMock();
-               $destructible->expects( $this->once() )
-                       ->method( 'destroy' );
-
-               $services->defineService( 'Foo', function () use ( $destructible ) {
-                       return $destructible;
-               } );
-               $services->defineService( 'Bar', function () {
-                       return new stdClass();
-               } );
-               $services->defineService( 'Qux', function () {
-                       return new stdClass();
-               } );
-
-               // instantiate Foo and Bar services
-               $services->getService( 'Foo' );
-               $services->getService( 'Bar' );
-
-               // disable service, should call destroy() once.
-               $services->disableService( 'Foo' );
-
-               // disabled service should still be listed
-               $this->assertContains( 'Foo', $services->getServiceNames() );
-
-               // getting other services should still work
-               $services->getService( 'Bar' );
-
-               // disable non-destructible service, and not-yet-instantiated service
-               $services->disableService( 'Bar' );
-               $services->disableService( 'Qux' );
-
-               $this->assertNull( $services->peekService( 'Bar' ) );
-               $this->assertNull( $services->peekService( 'Qux' ) );
-
-               // disabled service should still be listed
-               $this->assertContains( 'Bar', $services->getServiceNames() );
-               $this->assertContains( 'Qux', $services->getServiceNames() );
-
-               $this->setExpectedException( MediaWiki\Services\ServiceDisabledException::class );
-               $services->getService( 'Qux' );
-       }
-
-       public function testDisableService_fail_undefined() {
-               $services = $this->newServiceContainer();
-
-               $theService = new stdClass();
-               $name = 'TestService92834576';
-
-               $this->setExpectedException( MediaWiki\Services\NoSuchServiceException::class );
-
-               $services->redefineService( $name, function () use ( $theService ) {
-                       return $theService;
-               } );
-       }
-
-       public function testDestroy() {
-               $services = $this->newServiceContainer();
-
-               $destructible = $this->getMockBuilder( MediaWiki\Services\DestructibleService::class )
-                       ->getMock();
-               $destructible->expects( $this->once() )
-                       ->method( 'destroy' );
-
-               $services->defineService( 'Foo', function () use ( $destructible ) {
-                       return $destructible;
-               } );
-
-               $services->defineService( 'Bar', function () {
-                       return new stdClass();
-               } );
-
-               // create the service
-               $services->getService( 'Foo' );
-
-               // destroy the container
-               $services->destroy();
-
-               $this->setExpectedException( MediaWiki\Services\ContainerDisabledException::class );
-               $services->getService( 'Bar' );
-       }
-
-}
diff --git a/tests/phpunit/includes/services/TestWiring1.php b/tests/phpunit/includes/services/TestWiring1.php
deleted file mode 100644 (file)
index b6ff4eb..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-/**
- * Test file for testing ServiceContainer::loadWiringFiles
- */
-
-return [
-       'Foo' => function () {
-               return 'Foo!';
-       },
-];
diff --git a/tests/phpunit/includes/services/TestWiring2.php b/tests/phpunit/includes/services/TestWiring2.php
deleted file mode 100644 (file)
index dfff64f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-/**
- * Test file for testing ServiceContainer::loadWiringFiles
- */
-
-return [
-       'Bar' => function () {
-               return 'Bar!';
-       },
-];
index e796065..26088a3 100644 (file)
@@ -84,6 +84,7 @@ class UploadStashTest extends MediaWikiTestCase {
                                ] ) ],
                ];
        }
+
        /**
         * @dataProvider provideValidRequests
         */