5 * @link http://php-di.org/
6 * @copyright Matthieu Napoli (http://mnapoli.fr/)
7 * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
10 namespace DI\Definition\Helper;
12 use DI\Definition\ObjectDefinition;
13 use DI\Definition\ObjectDefinition\MethodInjection;
14 use DI\Definition\ObjectDefinition\PropertyInjection;
15 use DI\Definition\Exception\DefinitionException;
18 * Helps defining how to create an instance of a class.
20 * @author Matthieu Napoli <matthieu@mnapoli.fr>
22 class ObjectDefinitionHelper implements DefinitionHelper
40 * Array of constructor parameters.
43 private $constructor = [];
46 * Array of properties and their value.
49 private $properties = [];
52 * Array of methods and their parameters.
55 private $methods = [];
58 * Helper for defining an object.
60 * @param string|null $className Class name of the object.
61 * If null, the name of the entry (in the container) will be used as class name.
63 public function __construct($className = null)
65 $this->className = $className;
69 * Define the entry as lazy.
71 * A lazy entry is created only when it is used, a proxy is injected instead.
73 * @return ObjectDefinitionHelper
75 public function lazy()
82 * Defines the scope of the entry.
84 * @param string $scope
86 * @return ObjectDefinitionHelper
88 public function scope($scope)
90 $this->scope = $scope;
95 * Defines the arguments to use to call the constructor.
97 * This method takes a variable number of arguments, example:
98 * ->constructor($param1, $param2, $param3)
100 * @param mixed ... Parameters to use for calling the constructor of the class.
102 * @return ObjectDefinitionHelper
104 public function constructor()
106 $this->constructor = func_get_args();
111 * Defines a value for a specific argument of the constructor.
113 * This method is usually used together with annotations or autowiring, when a parameter
114 * is not (or cannot be) type-hinted. Using this method instead of constructor() allows to
115 * avoid defining all the parameters (letting them being resolved using annotations or autowiring)
116 * and only define one.
118 * @param string $parameter Parameter for which the value will be given.
119 * @param mixed $value Value to give to this parameter.
121 * @return ObjectDefinitionHelper
123 public function constructorParameter($parameter, $value)
125 $this->constructor[$parameter] = $value;
130 * Defines a value to inject in a property of the object.
132 * @param string $property Entry in which to inject the value.
133 * @param mixed $value Value to inject in the property.
135 * @return ObjectDefinitionHelper
137 public function property($property, $value)
139 $this->properties[$property] = $value;
144 * Defines a method to call and the arguments to use.
146 * This method takes a variable number of arguments after the method name, example:
148 * ->method('myMethod', $param1, $param2)
150 * Can be used multiple times to declare multiple calls.
152 * @param string $method Name of the method to call.
153 * @param mixed ... Parameters to use for calling the method.
155 * @return ObjectDefinitionHelper
157 public function method($method)
159 $args = func_get_args();
162 if (! isset($this->methods[$method])) {
163 $this->methods[$method] = [];
166 $this->methods[$method][] = $args;
172 * Defines a method to call and a value for a specific argument.
174 * This method is usually used together with annotations or autowiring, when a parameter
175 * is not (or cannot be) type-hinted. Using this method instead of method() allows to
176 * avoid defining all the parameters (letting them being resolved using annotations or
177 * autowiring) and only define one.
179 * If multiple calls to the method have been configured already (e.g. in a previous definition)
180 * then this method only overrides the parameter for the *first* call.
182 * @param string $method Name of the method to call.
183 * @param string $parameter Name or index of the parameter for which the value will be given.
184 * @param mixed $value Value to give to this parameter.
186 * @return ObjectDefinitionHelper
188 public function methodParameter($method, $parameter, $value)
190 // Special case for the constructor
191 if ($method === '__construct') {
192 $this->constructor[$parameter] = $value;
196 if (! isset($this->methods[$method])) {
197 $this->methods[$method] = [0 => []];
200 $this->methods[$method][0][$parameter] = $value;
208 public function getDefinition($entryName)
210 $definition = new ObjectDefinition($entryName, $this->className);
212 if ($this->lazy !== null) {
213 $definition->setLazy($this->lazy);
215 if ($this->scope !== null) {
216 $definition->setScope($this->scope);
219 if (! empty($this->constructor)) {
220 $parameters = $this->fixParameters($definition, '__construct', $this->constructor);
221 $constructorInjection = MethodInjection::constructor($parameters);
222 $definition->setConstructorInjection($constructorInjection);
225 if (! empty($this->properties)) {
226 foreach ($this->properties as $property => $value) {
227 $definition->addPropertyInjection(
228 new PropertyInjection($property, $value)
233 if (! empty($this->methods)) {
234 foreach ($this->methods as $method => $calls) {
235 foreach ($calls as $parameters) {
236 $parameters = $this->fixParameters($definition, $method, $parameters);
237 $methodInjection = new MethodInjection($method, $parameters);
238 $definition->addMethodInjection($methodInjection);
247 * Fixes parameters indexed by the parameter name -> reindex by position.
249 * This is necessary so that merging definitions between sources is possible.
251 * @param ObjectDefinition $definition
252 * @param string $method
253 * @param array $parameters
254 * @throws DefinitionException
257 private function fixParameters(ObjectDefinition $definition, $method, $parameters)
259 $fixedParameters = [];
261 foreach ($parameters as $index => $parameter) {
262 // Parameter indexed by the parameter name, we reindex it with its position
263 if (is_string($index)) {
264 $callable = [$definition->getClassName(), $method];
266 $reflectionParameter = new \ReflectionParameter($callable, $index);
267 } catch (\ReflectionException $e) {
268 throw DefinitionException::create($definition, "Parameter with name '$index' could not be found");
271 $index = $reflectionParameter->getPosition();
274 $fixedParameters[$index] = $parameter;
277 return $fixedParameters;