-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathArgumentsResolver.php
152 lines (137 loc) · 4.7 KB
/
ArgumentsResolver.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<?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) {
if (is_array($callable)) {
return new \ReflectionMethod($callable[0], $callable[1]);
}
if (is_string($callable) && str_contains($callable, "::")) {
return \ReflectionMethod::createFromMethodName($callable);
}
return 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()));
}
}