Drupal Tutorial: Services

Drupal 8+ is built on the Symfony PHP framework which provides a "service container" for managing and injecting dependencies within an application.

What is a Service Container?

A service container is a PHP class that manages the creation and retrieval of objects within an application. It acts as a central store of classes and their dependencies, and provides a way to easily retrieve and use these services throughout an application. The service container is designed to decouple application components and make them more modular and reusable.

The service container works by defining services which are PHP objects that are instantiated and managed by the container. Each service has a unique identifier and the container stores a map of these identifiers to the actual objects. The container also manages the dependencies between services, and automatically injects the required dependencies when a service is requested.

When the container is compiled, it is optimised for performance and can be cached to reduce the overhead of building the container on each request. The cached container is also implemented as a PHP class, which is then used to create the container at runtime.

Symfony and Drupal provide several caching mechanisms to cache the compiled container class, depending on the environment and requirements of the application. Some of the caching mechanisms include:

  • A PHP accelerator cache such as APC, OpCache, or XCache can be used to cache the compiled container class in shared memory.
  • A cache adapter: Symfony supports several caching adapters that can be used to cache the compiled container class in a variety of data stores, such as Redis or Memcached.

In addition to caching the compiled container class, Symfony also provides other mechanisms to optimise the container, such as the ability to remove unused services and parameters.

Using the Service Container (Services) in Drupal

Drupal provides a number of core services, such as the database service, cache service, and entity manager service and these services can be used within modules and themes using a single line of code:

\Drupal::service('example_service_name');

It is worth taking a look at the core.services.yml file found in the /core folder to get an idea of how services are defined.

Example module demonstrating a simple Drupal service

The code below demonstrates how to create a simple service that can set and get class properties within the service container and should provide an understanding of how this could be used within a custom Drupal module or theme.

The basic structure for this example module is as follows:

  • example_module 
    • src
      • SettingsService.php
  • example_module.info.yml
  • example_module.services.yml

The example_module.info.yml file has the following contents:

name: 'Settings service'
description: 'Provides a simple settings service to store and retrieve a settings property.'
core_version_requirement: ^9.3 || ^10
type: module

Within the example_module.services.yml file we define the service as follows:

services:
  example_module.settings_service:
    class: Drupal\example_module\SettingsService

This code defines a service named "example_module.settings_service", which is an instance of the "SettingsService" class. Once the rest of our code is in place we can call this service using a single line of code:

\Drupal::service('settings_service');

Within the SettingsService.php file we define the namespace and the class properties and methods. Note: we are not using any dependency injection in this service to keep the code simple.

<?php
namespace Drupal\example_module;
/**
* Formatter settings service.
*/
class SettingsService {
 /**
  * The settings array generated from hooks.
  *
  * @var array
  */
 public static array $settings;
 /**
  * Get the settings.
  */
 public function getSettings(): array {
   return self::$settings;
 }
 /**
  * Set the settings.
  */
 public function setSettings($settings = []) {
   self::$settings = $settings;
   return $settings;
 }
}

Once this code has been added we can enable the module, making sure to clear any caches you may have in place.

Now that we have some class properties and methods defined we can call our service again but this time it can be instructed to both set and get some arbitrary settings.

/** @var \Drupal\example_module\SettingsService $settings_service */
   $settings_service = \Drupal::service('example_module.settings_service');
   // Set the static settings property.
   $settings_service->setSettings(['test'=>'testing']);
   // Get the static settings property.
   $my_settings = $settings_service->getSettings();
   // Do stuff with these settings.

Using this type of service means the settings property stored within this service can be retrieved by other themes or modules in different parts of the application. This makes it super easy to provide reusable code that will be called multiple times by the application which is cached and served quickly to end users.

Conclusion

The service container is a powerful tool for managing dependencies within Drupal as it provides a way to decouple application components, and makes them more modular and reusable. By using the service container, code is more maintainable and easier to extend with additional features and functionality, which in turn makes the application easier to test and deploy.