63b0ba975ab32fc4621d50c919a37c803486f79c
[GitHub/WoltLab/WCF.git] /
1 <?php
2 /**
3 * PHP-DI
4 *
5 * @link http://mnapoli.github.com/PHP-DI/
6 * @copyright Matthieu Napoli (http://mnapoli.fr/)
7 * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
8 */
9
10 namespace DI\Definition\Resolver;
11
12 use DI\Definition\ObjectDefinition;
13 use DI\Definition\Exception\DefinitionException;
14 use DI\Definition\Helper\DefinitionHelper;
15 use DI\Definition\ObjectDefinition\MethodInjection;
16
17 /**
18 * Resolves parameters for a function call.
19 *
20 * @since 4.2
21 * @author Matthieu Napoli <matthieu@mnapoli.fr>
22 */
23 class ParameterResolver
24 {
25 /**
26 * @var DefinitionResolver
27 */
28 private $definitionResolver;
29
30 /**
31 * @param DefinitionResolver $definitionResolver Will be used to resolve nested definitions.
32 */
33 public function __construct(DefinitionResolver $definitionResolver)
34 {
35 $this->definitionResolver = $definitionResolver;
36 }
37
38 /**
39 * @param MethodInjection $definition
40 * @param \ReflectionFunctionAbstract $functionReflection
41 * @param array $parameters
42 *
43 * @throws DefinitionException A parameter has no value defined or guessable.
44 * @return array Parameters to use to call the function.
45 */
46 public function resolveParameters(
47 MethodInjection $definition = null,
48 \ReflectionFunctionAbstract $functionReflection = null,
49 array $parameters = []
50 ) {
51 $args = [];
52
53 if (! $functionReflection) {
54 return $args;
55 }
56
57 $definitionParameters = $definition ? $definition->getParameters() : array();
58
59 foreach ($functionReflection->getParameters() as $index => $parameter) {
60 if (array_key_exists($parameter->getName(), $parameters)) {
61 // Look in the $parameters array
62 $value = &$parameters[$parameter->getName()];
63 } elseif (array_key_exists($index, $definitionParameters)) {
64 // Look in the definition
65 $value = &$definitionParameters[$index];
66 } else {
67 // If the parameter is optional and wasn't specified, we take its default value
68 if ($parameter->isOptional()) {
69 $args[] = $this->getParameterDefaultValue($parameter, $functionReflection);
70 continue;
71 }
72
73 throw new DefinitionException(sprintf(
74 "The parameter '%s' of %s has no value defined or guessable",
75 $parameter->getName(),
76 $this->getFunctionName($functionReflection)
77 ));
78 }
79
80 if ($value instanceof DefinitionHelper) {
81 $nestedDefinition = $value->getDefinition('');
82
83 // If the container cannot produce the entry, we can use the default parameter value
84 if ($parameter->isOptional() && !$this->definitionResolver->isResolvable($nestedDefinition)) {
85 $value = $this->getParameterDefaultValue($parameter, $functionReflection);
86 } else {
87 $value = $this->definitionResolver->resolve($nestedDefinition);
88 }
89 }
90
91 $args[] = &$value;
92 }
93
94 return $args;
95 }
96
97 /**
98 * Returns the default value of a function parameter.
99 *
100 * @param \ReflectionParameter $parameter
101 * @param \ReflectionFunctionAbstract $function
102 *
103 * @throws DefinitionException Can't get default values from PHP internal classes and functions
104 * @return mixed
105 */
106 private function getParameterDefaultValue(
107 \ReflectionParameter $parameter,
108 \ReflectionFunctionAbstract $function
109 ) {
110 try {
111 return $parameter->getDefaultValue();
112 } catch (\ReflectionException $e) {
113 throw new DefinitionException(sprintf(
114 "The parameter '%s' of %s has no type defined or guessable. It has a default value, "
115 . "but the default value can't be read through Reflection because it is a PHP internal class.",
116 $parameter->getName(),
117 $this->getFunctionName($function)
118 ));
119 }
120 }
121
122 private function getFunctionName(\ReflectionFunctionAbstract $reflectionFunction)
123 {
124 if ($reflectionFunction instanceof \ReflectionMethod) {
125 return sprintf(
126 '%s::%s',
127 $reflectionFunction->getDeclaringClass()->getName(),
128 $reflectionFunction->getName()
129 );
130 } elseif ($reflectionFunction->isClosure()) {
131 return sprintf(
132 'closure defined in %s at line %d',
133 $reflectionFunction->getFileName(),
134 $reflectionFunction->getStartLine()
135 );
136 }
137
138 return $reflectionFunction->getName();
139 }
140 }