You are here

protected function TaggedHandlersPass::processServiceCollectorPass in Drupal 10

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php \Drupal\Core\DependencyInjection\Compiler\TaggedHandlersPass::processServiceCollectorPass()
  2. 9 core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php \Drupal\Core\DependencyInjection\Compiler\TaggedHandlersPass::processServiceCollectorPass()

Processes a service collector service pass.

Parameters

array $pass: The service collector pass data.

string $consumer_id: The consumer service ID.

\Symfony\Component\DependencyInjection\ContainerBuilder $container: The service container.

1 call to TaggedHandlersPass::processServiceCollectorPass()
TaggedHandlersPass::process in core/lib/Drupal/Core/DependencyInjection/Compiler/TaggedHandlersPass.php
Finds services tagged with 'service_collector' or 'service_id_collector', then finds all corresponding tagged services.

File

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

Class

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

Namespace

Drupal\Core\DependencyInjection\Compiler

Code

protected function processServiceCollectorPass(array $pass, $consumer_id, ContainerBuilder $container) {
  $tag = $pass['tag'] ?? $consumer_id;
  $method_name = $pass['call'] ?? 'addHandler';
  $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) {
    $class = Reflection::getParameterClassName($param);
    if ($class !== NULL) {
      $interface = $class;
    }
    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.", [
      $consumer_id,
      $consumer
        ->getClass(),
      $method_name,
    ]));
  }

  // Find all tagged handlers.
  $handlers = [];
  $extra_arguments = [];
  foreach ($this->tagCache[$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] = $attributes[0]['priority'] ?? 0;

    // Keep track of other tagged handlers arguments.
    foreach ($extra_params as $name => $pos) {
      $extra_arguments[$id][$pos] = $attributes[0][$name] ?? $params[$pos]
        ->getDefaultValue();
    }
  }
  if ($required && empty($handlers)) {
    throw new LogicException(sprintf("At least one service tagged with '%s' is required.", $tag));
  }

  // 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 = [];
    $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);
  }
}