You are here

ArgumentsResolver.php in Drupal 10

File

core/lib/Drupal/Component/Utility/ArgumentsResolver.php
View source
<?php

namespace Drupal\Component\Utility;


/**
 * Resolves the arguments to pass to a callable.
 */
class ArgumentsResolver implements ArgumentsResolverInterface {

  /**
   * An associative array of parameter names to scalar candidate values.
   *
   * @var array
   */
  protected $scalars;

  /**
   * An associative array of parameter names to object candidate values.
   *
   * @var array
   */
  protected $objects;

  /**
   * An array object candidates tried on every parameter regardless of name.
   *
   * @var array
   */
  protected $wildcards;

  /**
   * Constructs a new ArgumentsResolver.
   *
   * @param array $scalars
   *   An associative array of parameter names to scalar candidate values.
   * @param object[] $objects
   *   An associative array of parameter names to object candidate values.
   * @param object[] $wildcards
   *   An array object candidates tried on every parameter regardless of its
   *   name.
   */
  public function __construct(array $scalars, array $objects, array $wildcards) {
    $this->scalars = $scalars;
    $this->objects = $objects;
    $this->wildcards = $wildcards;
  }

  /**
   * {@inheritdoc}
   */
  public function getArguments(callable $callable) {
    $arguments = [];
    foreach ($this
      ->getReflector($callable)
      ->getParameters() as $parameter) {
      $arguments[] = $this
        ->getArgument($parameter);
    }
    return $arguments;
  }

  /**
   * Gets the argument value for a parameter.
   *
   * @param \ReflectionParameter $parameter
   *   The parameter of a callable to get the value for.
   *
   * @return mixed
   *   The value of the requested parameter value.
   *
   * @throws \RuntimeException
   *   Thrown when there is a missing parameter.
   */
  protected function getArgument(\ReflectionParameter $parameter) {
    $parameter_type_hint = Reflection::getParameterClassName($parameter);
    $parameter_name = $parameter
      ->getName();

    // If the argument exists and is NULL, return it, regardless of
    // parameter type hint.
    if (!isset($this->objects[$parameter_name]) && array_key_exists($parameter_name, $this->objects)) {
      return NULL;
    }
    if ($parameter_type_hint) {
      $parameter_type_hint = new \ReflectionClass($parameter_type_hint);

      // If the argument exists and complies with the type hint, return it.
      if (isset($this->objects[$parameter_name]) && is_object($this->objects[$parameter_name]) && $parameter_type_hint
        ->isInstance($this->objects[$parameter_name])) {
        return $this->objects[$parameter_name];
      }

      // Otherwise, resolve wildcard arguments by type matching.
      foreach ($this->wildcards as $wildcard) {
        if ($parameter_type_hint
          ->isInstance($wildcard)) {
          return $wildcard;
        }
      }
    }
    elseif (isset($this->scalars[$parameter_name])) {
      return $this->scalars[$parameter_name];
    }

    // If the callable provides a default value, use it.
    if ($parameter
      ->isDefaultValueAvailable()) {
      return $parameter
        ->getDefaultValue();
    }

    // Can't resolve it: call a method that throws an exception or can be
    // overridden to do something else.
    return $this
      ->handleUnresolvedArgument($parameter);
  }

  /**
   * Gets a reflector for the access check callable.
   *
   * The access checker may be either a procedural function (in which case the
   * callable is the function name) or a method (in which case the callable is
   * an array of the object and method name).
   *
   * @param callable $callable
   *   The callable (either a function or a method).
   *
   * @return \ReflectionFunctionAbstract
   *   The ReflectionMethod or ReflectionFunction to introspect the callable.
   */
  protected function getReflector(callable $callable) {
    return is_array($callable) ? new \ReflectionMethod($callable[0], $callable[1]) : new \ReflectionFunction($callable);
  }

  /**
   * Handles unresolved arguments for getArgument().
   *
   * Subclasses that override this method may return a default value
   * instead of throwing an exception.
   *
   * @throws \RuntimeException
   *   Thrown when there is a missing parameter.
   */
  protected function handleUnresolvedArgument(\ReflectionParameter $parameter) {
    $class = $parameter
      ->getDeclaringClass();
    $function = $parameter
      ->getDeclaringFunction();
    if ($class && !$function
      ->isClosure()) {
      $function_name = $class
        ->getName() . '::' . $function
        ->getName();
    }
    else {
      $function_name = $function
        ->getName();
    }
    throw new \RuntimeException(sprintf('Callable "%s" requires a value for the "$%s" argument.', $function_name, $parameter
      ->getName()));
  }

}

Classes

Namesort descending Description
ArgumentsResolver Resolves the arguments to pass to a callable.