diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index c72cafc..ef1a785 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -34,6 +34,8 @@ * * Modules wishing to register services to the container should use * modulename.services.yml in their respective directories. + * + * @ingroup container */ class CoreServiceProvider implements ServiceProviderInterface { diff --git a/core/lib/Drupal/Core/DependencyInjection/ServiceProviderInterface.php b/core/lib/Drupal/Core/DependencyInjection/ServiceProviderInterface.php index 98d1f02..f8cb491 100644 --- a/core/lib/Drupal/Core/DependencyInjection/ServiceProviderInterface.php +++ b/core/lib/Drupal/Core/DependencyInjection/ServiceProviderInterface.php @@ -4,6 +4,8 @@ /** * Interface that all service providers must implement. + * + * @ingroup container */ interface ServiceProviderInterface { diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php index 8af5387..f138295 100644 --- a/core/modules/system/core.api.php +++ b/core/modules/system/core.api.php @@ -509,6 +509,8 @@ * factory_service: cache_factory * arguments: [nameofbin] * @endcode + * See the @link container Services topic @endlink for more on defining + * services. * * @section delete Deletion * @@ -736,14 +738,36 @@ * class: Drupal\Core\Path\AliasManager * arguments: ['@path.crud', '@path.alias_whitelist', '@language_manager'] * @endcode - * The first line gives the unique machine name of the service (typically - * prefixed by the module name if provided by a module), and the class line - * gives the default class that provides the service. For some services, the - * arguments line lists the machine names of other services that are - * dependencies (preceded by '@'); objects for each of these services are - * instantiated from the container and passed to the class constructor when the - * service class is instantiated. Other arguments can also be passed in; see - * the section at https://drupal.org/node/2133171 for more detailed information. + * Some services use factory classes; a typical service definition is: + * @code + * cache.entity: + * class: Drupal\Core\Cache\CacheBackendInterface + * tags: + * - { name: cache.bin } + * factory_method: get + * factory_service: cache_factory + * arguments: [entity] + * @endcode + * + * The first line of a service definition gives the unique machine name of the + * service. This is often prefixed by the module name if provided by a module; + * however, by convention some service names are prefixed by a group name + * instead, such as cache.* for cache bins and plugin.manager.* for plugin + * managers. + * + * The class line either gives the default class that provides the service, or + * if the service uses a factory class, the interface for the service. If the + * class depends on other services, the arguments line lists the machine + * names of the dependencies (preceded by '@'); objects for each of these + * services are instantiated from the container and passed to the class + * constructor when the service class is instantiated. Other arguments can also + * be passed in; see the section at https://drupal.org/node/2133171 for more + * detailed information. + * + * Services using factories can be defined as shown in the above example, if the + * factory is itself a service. The factory can also be a class; details of how + * to use service factories can be found in the section at + * https://drupal.org/node/2133171. * * @section sec_container Accessing a service through the container * As noted above, if you need to use a service in your code, you should always @@ -752,20 +776,21 @@ * several ways to make sure this happens: * - For service-providing classes, see other sections of this documentation * describing how to pass services as arguments to the constructor. - * - Plugin classes, controllers, and similar classes have create() methods, - * whose first argument is - * @code - * \Symfony\Component\DependencyInjection\ContainerInterface $container - * @endcode - * In the create() method, call + * - Plugin classes, controllers, and similar classes have create() or + * createInstance() methods that are used to create an instance of the class. + * These methods come from different interfaces, and have different + * arguments, but they all include an argument $container of type + * \Symfony\Component\DependencyInjection\ContainerInterface. + * If you are defining one of these classes, in the create() or + * createInstance() method, call * @code $container->get('myservice.name') @endcode to instantiate a service. * The results of these calls are generally passed to the class constructor * and saved as member variables in the class. * - For functions and class methods that do not have access to either of - * the above methods of dependency injection, you can access the container - * via a call to the global \Drupal class. This class has special methods for - * accessing commonly-used services, or you can call a generic method to - * access any service. Examples: + * the above methods of dependency injection, you can use service location to + * access services, via a call to the global \Drupal class. This class has + * special methods for accessing commonly-used services, or you can call a + * generic method to access any service. Examples: * @code * // Retrieve the entity.manager service object (special method exists). * $manager = \Drupal->entityManager(); @@ -773,6 +798,16 @@ * $foobar = \Drupal->service('foo.bar'); * @endcode * + * As a note, you should always use dependency injection (via service arguments + * or create()/createInstance() methods) if possible to instantiate services, + * rather than service location (via the \Drupal class), because: + * - Dependency injection facilitates writing unit tests, since the container + * argument can be mocked and the create() method can be bypassed by using + * the class constructor. If you use the \Drupal class, unit tests are much + * harder to write and your code has more dependencies. + * - Having the service interfaces on the class constructor and member variables + * is useful for IDE auto-complete and self-documentation. + * * @section sec_define Defining a service * If your module needs to define a new service, here are the steps: * - Choose a unique machine name for your service. Typically, this should @@ -789,6 +824,29 @@ * syntax; it will start with your machine name, refer to your default class, * and list the services that need to be passed into your constructor. * + * Services can also be defined dynamically, as in the + * \Drupal\Core\CoreServiceProvider class, but this is less common for modules. + * + * @section sec_tags Service tags + * Some services have tags, which are defined in the service definition. Tags + * are used to define a group of related services, or to specify some aspect of + * how the service behaves. Typically, if you tag a service, your service class + * must also implement a corresponding interface. Some common examples: + * - access_check: Indicates a route access checking service; see the + * @link menu Menu and routing system topic @endlink for more information. + * - cache.bin: Indicates a cache bin service; see the + * @link cache Cache topic @endlink for more information. + * - event_subscriber: Indicates an event subscriber service for routing; see + * the @link menu Menu and routing system topic @endlink for more information. + * - needs_destruction: Indicates that a destruct() method needs to be called + * at the end of a request to finalize operations, if this service was + * instantiated. + * + * Creating a tag for a service does not do anything on its own, but tags + * can be discovered or queried in a compiler pass when the container is built, + * and a corresponding action can be taken. See + * \Drupal\Core\CoreServiceProvider::register() for an example. + * * @section sec_injection Overriding the default service class * Modules can override the default classes used for services. Here are the * steps: @@ -801,8 +859,6 @@ * \Drupal\Core\DependencyInjection\ServiceModifierInterface, which is * typically done by extending * \Drupal\Core\DependencyInjection\ServiceProviderBase. - * - Include a use statement for the class - * \Drupal\Core\DependencyInjection\ContainerBuilder * - The class needs to contain one method: alter(). This method does the * actual work of telling Drupal to use your class instead of the default. * Here's an example: @@ -813,6 +869,8 @@ * $definition->setClass('Drupal\my_module\MyLanguageManager'); * } * @endcode + * Note that $container here is an instance of + * \Drupal\Core\DependencyInjection\ContainerBuilder. * * @see https://drupal.org/node/2133171 * @see core.services.yml