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.
Comments
non working examplemy_module
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