3 namespace Drupal\Component\Utility;
6 * Resolves the arguments to pass to a callable.
8 class ArgumentsResolver implements ArgumentsResolverInterface {
11 * An associative array of parameter names to scalar candidate values.
18 * An associative array of parameter names to object candidate values.
25 * An array object candidates tried on every parameter regardless of name.
32 * Constructs a new ArgumentsResolver.
34 * @param array $scalars
35 * An associative array of parameter names to scalar candidate values.
36 * @param object[] $objects
37 * An associative array of parameter names to object candidate values.
38 * @param object[] $wildcards
39 * An array object candidates tried on every parameter regardless of its
42 public function __construct(array $scalars, array $objects, array $wildcards) {
43 $this->scalars = $scalars;
44 $this->objects = $objects;
45 $this->wildcards = $wildcards;
51 public function getArguments(callable $callable) {
53 foreach ($this->getReflector($callable)->getParameters() as $parameter) {
54 $arguments[] = $this->getArgument($parameter);
60 * Gets the argument value for a parameter.
62 * @param \ReflectionParameter $parameter
63 * The parameter of a callable to get the value for.
66 * The value of the requested parameter value.
68 * @throws \RuntimeException
69 * Thrown when there is a missing parameter.
71 protected function getArgument(\ReflectionParameter $parameter) {
72 $parameter_type_hint = $parameter->getClass();
73 $parameter_name = $parameter->getName();
75 // If the argument exists and is NULL, return it, regardless of
76 // parameter type hint.
77 if (!isset($this->objects[$parameter_name]) && array_key_exists($parameter_name, $this->objects)) {
81 if ($parameter_type_hint) {
82 // If the argument exists and complies with the type hint, return it.
83 if (isset($this->objects[$parameter_name]) && is_object($this->objects[$parameter_name]) && $parameter_type_hint->isInstance($this->objects[$parameter_name])) {
84 return $this->objects[$parameter_name];
86 // Otherwise, resolve wildcard arguments by type matching.
87 foreach ($this->wildcards as $wildcard) {
88 if ($parameter_type_hint->isInstance($wildcard)) {
93 elseif (isset($this->scalars[$parameter_name])) {
94 return $this->scalars[$parameter_name];
97 // If the callable provides a default value, use it.
98 if ($parameter->isDefaultValueAvailable()) {
99 return $parameter->getDefaultValue();
102 // Can't resolve it: call a method that throws an exception or can be
103 // overridden to do something else.
104 return $this->handleUnresolvedArgument($parameter);
108 * Gets a reflector for the access check callable.
110 * The access checker may be either a procedural function (in which case the
111 * callable is the function name) or a method (in which case the callable is
112 * an array of the object and method name).
114 * @param callable $callable
115 * The callable (either a function or a method).
117 * @return \ReflectionFunctionAbstract
118 * The ReflectionMethod or ReflectionFunction to introspect the callable.
120 protected function getReflector(callable $callable) {
121 return is_array($callable) ? new \ReflectionMethod($callable[0], $callable[1]) : new \ReflectionFunction($callable);
125 * Handles unresolved arguments for getArgument().
127 * Subclasses that override this method may return a default value
128 * instead of throwing an exception.
130 * @throws \RuntimeException
131 * Thrown when there is a missing parameter.
133 protected function handleUnresolvedArgument(\ReflectionParameter $parameter) {
134 $class = $parameter->getDeclaringClass();
135 $function = $parameter->getDeclaringFunction();
136 if ($class && !$function->isClosure()) {
137 $function_name = $class->getName() . '::' . $function->getName();
140 $function_name = $function->getName();
142 throw new \RuntimeException(sprintf('Callable "%s" requires a value for the "$%s" argument.', $function_name, $parameter->getName()));