Change record status: 
Project: 
Introduced in branch: 
8.x
Description: 

The use of $GLOBALS is replaced with a Symfony Dependency Injection container to help save variable scope, and reduce the risk of global PHP object conflicts.

Below is an example of getting a service from the container, in this case the 'request' service, which is available as long as the code is running within the scope of an http request.

Drupal 7:

$path = $_GET['q']

Drupal 8:

$request = Drupal::service('request');
$path = $request->attributes->get('_system_path');

To register new services in the container, modules may now provide a modulename.services.yml file in the same directory where the module file is.

For example mymodule.services.yml:

services:
  my_module.some_service:
    class: Drupal\mymodule\SomeClassHere
    arguments: [@service_container]

This YAML file is documented in detail in the Service Container chapter of the Symfony book.

Drupal provides the core/core.services.yml file which registers various services needed for basic functionality.

Another example is the language_manager service which replaced the global $language.

Changing registered services

To modify an existing service, provide a "camelized" (my_module => MyModule) class with the ServiceProvider suffix and extend from \Drupal\Core\DependencyInjection\ServiceProviderBase:

namespace Drupal\my_module;

use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;

class MyModuleServiceProvider extends ServiceProviderBase {

  /**
   * {@inheritdoc}
   */
  public function alter(ContainerBuilder $container) {
    // Overrides language_manager class to test domain language negotiation.
    $definition = $container->getDefinition('language_manager');
    $definition->setClass('Drupal\language_test\LanguageTestManager');
  }

}

Event listeners

This mechanism is also used to register event listeners. Your module should provide a class that implements the EventSubscriberInterface:

namespace Drupal\my_module;

use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class MyModuleClass implements EventSubscriberInterface {

  /**
   * A simple kernel listener method.
   */
  public function onKernelRequestTest(GetResponseEvent $event) {
    drupal_set_message(t('My event subscriber fired!'));
  }

  /**
   * Registers methods as kernel listeners.
   *
   * @return array
   *   An array of event listener definitions.
   */
  static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = array('onKernelRequestTest', 100);
    return $events;
  }
}

And then you register it to the container and tag it as event_subscriber:

services:
  my_module.some_service:
    class: Drupal\mymodule\MyModuleClass
    tags:
      - { name: event_subscriber }

Drupal helper class for procedural code that needs services

Generally, code in Drupal should accept its dependencies via either constructor injection or setter method injection. However, there are cases, particularly in legacy procedural code, where that is infeasible. The Drupal class acts as a unified global accessor to arbitrary services within the system in order to ease the transition from procedural code to injected OO code.

The container is built by the kernel and passed in to this class which stores it statically. The container always contains the services from core/core.services.yml, the service YAML files of enabled modules and any other YAML files defined in $GLOBALS['conf']['container_yamls'].

This class exists only to support legacy code that cannot be dependency injected. If your code needs it, consider refactoring it to be object oriented, if possible. When this is not possible, for instance in the case of hook implementations, and your code is more than a few non-reusable lines, it is recommended to instantiate an object implementing the actual logic.

function hook_do_stuff() {
  // Move the actual implementation to a class and instantiate it.
  $instance = new StuffDoingClass(Drupal::lock());
  $instance->doStuff();

  // Or, even better, rely on the service container to avoid hard coding a
  // specific interface implementation, so that the actual logic can be
  // swapped. This might not always make sense, but in general it is a good
  // practice.
  Drupal::service('stuff.doing')->doStuff();
}

interface StuffDoingInterface {
  public function doStuff();
}

class StuffDoingClass implements StuffDoingInterface {
  protected $lockBackend;

  public function __construct(LockBackendInterface $lockBackend) {
    $this->lockBackend = $lockBackend;
  }

  public function doStuff() {
    $lock = $this->lockBackend->acquire('stuff_lock');
    // ...
  }
}

For more details, consult the Symfony documentation linked above.

Impacts: 
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done

Comments

podarok’s picture

non working example
my_module <=> mymodule

even with the same name everywere
PHP Fatal error: Call to a member function get() on a non-object in /core/includes/theme.maintenance.inc on line 59

---------------
Andrii Podanenko
CEO, ITCare