public function TaggedHandlersPass::process

public TaggedHandlersPass::process(ContainerBuilder $container)

Finds services tagged with 'service_collector', then finds all corresponding tagged services and adds a method call for each to the consuming/collecting service definition.

Supported 'service_collector' tag attributes:

  • tag: The tag name used by handler services to collect. Defaults to the service ID of the consumer.
  • call: The method name to call on the consumer service. Defaults to 'addHandler'. The called method receives two arguments:
    • The handler instance as first argument.
    • Optionally the handler's priority as second argument, if the method accepts a second parameter and its name is "priority". In any case, all handlers registered at compile time are sorted already.
  • required: Boolean indicating if at least one handler service is required. Defaults to FALSE.

Example (YAML):

tags:
  - { name: service_collector, tag: breadcrumb_builder, call: addBuilder }

Supported handler tag attributes:

  • priority: An integer denoting the priority of the handler. Defaults to 0.

Example (YAML):

tags:
  - { name: breadcrumb_builder, priority: 100 }

Throws

\Symfony\Component\DependencyInjection\Exception\LogicException If the method of a consumer service to be called does not type-hint an interface.

\Symfony\Component\DependencyInjection\Exception\LogicException If a tagged handler does not implement the required interface.

\Symfony\Component\DependencyInjection\Exception\LogicException If at least one tagged service is required but none are found.

Overrides CompilerPassInterface::process

File

core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php, line 77

Class

TaggedHandlersPass
Collects services to add/inject them into a consumer service.

Namespace

Drupal\Core\DependencyInjection\Compiler

Code

public function process(ContainerBuilder $container) {
  foreach ($container->findTaggedServiceIds('service_collector') as $consumer_id => $passes) {
    foreach ($passes as $pass) {
      $tag = isset($pass['tag']) ? $pass['tag'] : $consumer_id;
      $method_name = isset($pass['call']) ? $pass['call'] : 'addHandler';
      $required = isset($pass['required']) ? $pass['required'] : FALSE;

      // Determine parameters.
      $consumer = $container->getDefinition($consumer_id);
      $method = new \ReflectionMethod($consumer->getClass(), $method_name);
      $params = $method->getParameters();

      $interface_pos = 0;
      $id_pos = NULL;
      $priority_pos = NULL;
      $extra_params = [];
      foreach ($params as $pos => $param) {
        if ($param->getClass()) {
          $interface = $param->getClass();
        }
        elseif ($param->getName() === 'id') {
          $id_pos = $pos;
        }
        elseif ($param->getName() === 'priority') {
          $priority_pos = $pos;
        }
        else {
          $extra_params[$param->getName()] = $pos;
        }
      }
      // Determine the ID.

      if (!isset($interface)) {
        throw new LogicException(vsprintf("Service consumer '%s' class method %s::%s() has to type-hint an interface.", array(
          $consumer_id,
          $consumer->getClass(),
          $method_name,
        )));
      }
      $interface = $interface->getName();

      // Find all tagged handlers.
      $handlers = array();
      $extra_arguments = array();
      foreach ($container->findTaggedServiceIds($tag) as $id => $attributes) {
        // Validate the interface.
        $handler = $container->getDefinition($id);
        if (!is_subclass_of($handler->getClass(), $interface)) {
          throw new LogicException("Service '$id' for consumer '$consumer_id' does not implement $interface.");
        }
        $handlers[$id] = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
        // Keep track of other tagged handlers arguments.
        foreach ($extra_params as $name => $pos) {
          $extra_arguments[$id][$pos] = isset($attributes[0][$name]) ? $attributes[0][$name] : $params[$pos]->getDefaultValue();
        }
      }
      if (empty($handlers)) {
        if ($required) {
          throw new LogicException(sprintf("At least one service tagged with '%s' is required.", $tag));
        }
        continue;
      }
      // Sort all handlers by priority.
      arsort($handlers, SORT_NUMERIC);

      // Add a method call for each handler to the consumer service
      // definition.
      foreach ($handlers as $id => $priority) {
        $arguments = array();
        $arguments[$interface_pos] = new Reference($id);
        if (isset($priority_pos)) {
          $arguments[$priority_pos] = $priority;
        }
        if (isset($id_pos)) {
          $arguments[$id_pos] = $id;
        }
        // Add in extra arguments.
        if (isset($extra_arguments[$id])) {
          // Place extra arguments in their right positions.
          $arguments += $extra_arguments[$id];
        }
        // Sort the arguments by position.
        ksort($arguments);
        $consumer->addMethodCall($method_name, $arguments);
      }
    }
  }
}

© 2001–2016 by the original authors
Licensed under the GNU General Public License, version 2 and later.
Drupal is a registered trademark of Dries Buytaert.
https://api.drupal.org/api/drupal/core!lib!Drupal!Core!DependencyInjection!Compiler!TaggedHandlersPass.php/function/TaggedHandlersPass::process/8.1.x