Rename includes/Services to includes/services for consistency
authorReedy <reedy@wikimedia.org>
Thu, 22 Sep 2016 23:35:52 +0000 (00:35 +0100)
committerBartosz Dziewoński <matma.rex@gmail.com>
Fri, 23 Sep 2016 00:08:15 +0000 (00:08 +0000)
Change-Id: I900fab26a7cf5a339233f55c31168f8c2963bc8c

17 files changed:
autoload.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/services/CannotReplaceActiveServiceException.php [new file with mode: 0644]
includes/services/ContainerDisabledException.php [new file with mode: 0644]
includes/services/DestructibleService.php [new file with mode: 0644]
includes/services/NoSuchServiceException.php [new file with mode: 0644]
includes/services/SalvageableService.php [new file with mode: 0644]
includes/services/ServiceAlreadyDefinedException.php [new file with mode: 0644]
includes/services/ServiceContainer.php [new file with mode: 0644]
includes/services/ServiceDisabledException.php [new file with mode: 0644]

index d985ee0..a352884 100644 (file)
@@ -869,14 +869,14 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Logger\\Spi' => __DIR__ . '/includes/debug/logger/Spi.php',
        'MediaWiki\\MediaWikiServices' => __DIR__ . '/includes/MediaWikiServices.php',
        'MediaWiki\\Search\\ParserOutputSearchDataExtractor' => __DIR__ . '/includes/search/ParserOutputSearchDataExtractor.php',
-       'MediaWiki\\Services\\CannotReplaceActiveServiceException' => __DIR__ . '/includes/Services/CannotReplaceActiveServiceException.php',
-       'MediaWiki\\Services\\ContainerDisabledException' => __DIR__ . '/includes/Services/ContainerDisabledException.php',
-       'MediaWiki\\Services\\DestructibleService' => __DIR__ . '/includes/Services/DestructibleService.php',
-       'MediaWiki\\Services\\NoSuchServiceException' => __DIR__ . '/includes/Services/NoSuchServiceException.php',
-       'MediaWiki\\Services\\SalvageableService' => __DIR__ . '/includes/Services/SalvageableService.php',
-       'MediaWiki\\Services\\ServiceAlreadyDefinedException' => __DIR__ . '/includes/Services/ServiceAlreadyDefinedException.php',
-       'MediaWiki\\Services\\ServiceContainer' => __DIR__ . '/includes/Services/ServiceContainer.php',
-       'MediaWiki\\Services\\ServiceDisabledException' => __DIR__ . '/includes/Services/ServiceDisabledException.php',
+       'MediaWiki\\Services\\CannotReplaceActiveServiceException' => __DIR__ . '/includes/services/CannotReplaceActiveServiceException.php',
+       'MediaWiki\\Services\\ContainerDisabledException' => __DIR__ . '/includes/services/ContainerDisabledException.php',
+       'MediaWiki\\Services\\DestructibleService' => __DIR__ . '/includes/services/DestructibleService.php',
+       'MediaWiki\\Services\\NoSuchServiceException' => __DIR__ . '/includes/services/NoSuchServiceException.php',
+       'MediaWiki\\Services\\SalvageableService' => __DIR__ . '/includes/services/SalvageableService.php',
+       'MediaWiki\\Services\\ServiceAlreadyDefinedException' => __DIR__ . '/includes/services/ServiceAlreadyDefinedException.php',
+       'MediaWiki\\Services\\ServiceContainer' => __DIR__ . '/includes/services/ServiceContainer.php',
+       'MediaWiki\\Services\\ServiceDisabledException' => __DIR__ . '/includes/services/ServiceDisabledException.php',
        'MediaWiki\\Session\\BotPasswordSessionProvider' => __DIR__ . '/includes/session/BotPasswordSessionProvider.php',
        'MediaWiki\\Session\\CookieSessionProvider' => __DIR__ . '/includes/session/CookieSessionProvider.php',
        'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' => __DIR__ . '/includes/session/ImmutableSessionProviderWithCookie.php',
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 bad0ef9..0000000
+++ /dev/null
@@ -1,378 +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 boolean[] disabled status, per service name
-        */
-       private $disabled = [];
-
-       /**
-        * @var array
-        */
-       private $extraInstantiationParams;
-
-       /**
-        * @var boolean
-        */
-       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();
-                       }
-               }
-
-               $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
-               );
-       }
-
-       /**
-        * 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 isset( $this->services[$name] ) ? $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 replaceService().
-        *
-        * @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 causes any previously instantiated instance of the service to be discarded.
-        *
-        * @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 RuntimeException if $name is not a known service.
-        */
-       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] );
-       }
-
-       /**
-        * 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 = call_user_func_array(
-                               $this->serviceInstantiators[$name],
-                               array_merge( [ $this ], $this->extraInstantiationParams )
-                       );
-                       // NOTE: when adding more wiring logic here, make sure copyWiring() 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 );
-       }
-
-}
diff --git a/includes/services/CannotReplaceActiveServiceException.php b/includes/services/CannotReplaceActiveServiceException.php
new file mode 100644 (file)
index 0000000..4993073
--- /dev/null
@@ -0,0 +1,43 @@
+<?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
new file mode 100644 (file)
index 0000000..ede076d
--- /dev/null
@@ -0,0 +1,42 @@
+<?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
new file mode 100644 (file)
index 0000000..6ce9af2
--- /dev/null
@@ -0,0 +1,45 @@
+<?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
new file mode 100644 (file)
index 0000000..36e50d2
--- /dev/null
@@ -0,0 +1,43 @@
+<?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
new file mode 100644 (file)
index 0000000..a613050
--- /dev/null
@@ -0,0 +1,58 @@
+<?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
new file mode 100644 (file)
index 0000000..c6344d3
--- /dev/null
@@ -0,0 +1,45 @@
+<?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
new file mode 100644 (file)
index 0000000..bad0ef9
--- /dev/null
@@ -0,0 +1,378 @@
+<?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 boolean[] disabled status, per service name
+        */
+       private $disabled = [];
+
+       /**
+        * @var array
+        */
+       private $extraInstantiationParams;
+
+       /**
+        * @var boolean
+        */
+       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();
+                       }
+               }
+
+               $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
+               );
+       }
+
+       /**
+        * 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 isset( $this->services[$name] ) ? $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 replaceService().
+        *
+        * @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 causes any previously instantiated instance of the service to be discarded.
+        *
+        * @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 RuntimeException if $name is not a known service.
+        */
+       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] );
+       }
+
+       /**
+        * 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 = call_user_func_array(
+                               $this->serviceInstantiators[$name],
+                               array_merge( [ $this ], $this->extraInstantiationParams )
+                       );
+                       // NOTE: when adding more wiring logic here, make sure copyWiring() 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
new file mode 100644 (file)
index 0000000..ae15b7c
--- /dev/null
@@ -0,0 +1,43 @@
+<?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 );
+       }
+
+}