Completed
Push — master ( 8ecded...ff0374 )
by Matthieu
11s
created

CreateDefinitionHelper::fixParameters()   B

Complexity

Conditions 4
Paths 4

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 22
Code Lines 12

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 4
nop 3
dl 0
loc 22
rs 8.9197
c 0
b 0
f 0
1
<?php
2
3
namespace DI\Definition\Helper;
4
5
use DI\Definition\Exception\InvalidDefinition;
6
use DI\Definition\ObjectDefinition;
7
use DI\Definition\ObjectDefinition\MethodInjection;
8
use DI\Definition\ObjectDefinition\PropertyInjection;
9
10
/**
11
 * Helps defining how to create an instance of a class.
12
 *
13
 * @author Matthieu Napoli <[email protected]>
14
 */
15
class CreateDefinitionHelper implements DefinitionHelper
16
{
17
    const DEFINITION_CLASS = ObjectDefinition::class;
18
19
    /**
20
     * @var string|null
21
     */
22
    private $className;
23
24
    /**
25
     * @var bool|null
26
     */
27
    private $lazy;
28
29
    /**
30
     * @var string|null
31
     */
32
    private $scope;
33
34
    /**
35
     * Array of constructor parameters.
36
     * @var array
37
     */
38
    protected $constructor = [];
39
40
    /**
41
     * Array of properties and their value.
42
     * @var array
43
     */
44
    private $properties = [];
45
46
    /**
47
     * Array of methods and their parameters.
48
     * @var array
49
     */
50
    protected $methods = [];
51
52
    /**
53
     * Helper for defining an object.
54
     *
55
     * @param string|null $className Class name of the object.
56
     *                               If null, the name of the entry (in the container) will be used as class name.
57
     */
58
    public function __construct($className = null)
59
    {
60
        $this->className = $className;
61
    }
62
63
    /**
64
     * Define the entry as lazy.
65
     *
66
     * A lazy entry is created only when it is used, a proxy is injected instead.
67
     *
68
     * @return CreateDefinitionHelper
69
     */
70
    public function lazy()
71
    {
72
        $this->lazy = true;
73
74
        return $this;
75
    }
76
77
    /**
78
     * Defines the scope of the entry.
79
     *
80
     * @param string $scope
81
     *
82
     * @return CreateDefinitionHelper
83
     */
84
    public function scope($scope)
85
    {
86
        $this->scope = $scope;
87
88
        return $this;
89
    }
90
91
    /**
92
     * Defines the arguments to use to call the constructor.
93
     *
94
     * This method takes a variable number of arguments, example:
95
     *     ->constructor($param1, $param2, $param3)
96
     *
97
     * @param mixed ... Parameters to use for calling the constructor of the class.
98
     *
99
     * @return CreateDefinitionHelper
100
     */
101
    public function constructor()
102
    {
103
        $this->constructor = func_get_args();
104
105
        return $this;
106
    }
107
108
    /**
109
     * Defines a value to inject in a property of the object.
110
     *
111
     * @param string $property Entry in which to inject the value.
112
     * @param mixed  $value    Value to inject in the property.
113
     *
114
     * @return CreateDefinitionHelper
115
     */
116
    public function property($property, $value)
117
    {
118
        $this->properties[$property] = $value;
119
120
        return $this;
121
    }
122
123
    /**
124
     * Defines a method to call and the arguments to use.
125
     *
126
     * This method takes a variable number of arguments after the method name, example:
127
     *
128
     *     ->method('myMethod', $param1, $param2)
129
     *
130
     * Can be used multiple times to declare multiple calls.
131
     *
132
     * @param string $method Name of the method to call.
133
     * @param mixed  ...     Parameters to use for calling the method.
134
     *
135
     * @return CreateDefinitionHelper
136
     */
137
    public function method($method)
138
    {
139
        $args = func_get_args();
140
        array_shift($args);
141
142
        if (! isset($this->methods[$method])) {
143
            $this->methods[$method] = [];
144
        }
145
146
        $this->methods[$method][] = $args;
147
148
        return $this;
149
    }
150
151
    /**
152
     * [email protected]}
153
     */
154
    public function getDefinition($entryName)
155
    {
156
        $class = $this::DEFINITION_CLASS;
157
        /** @var ObjectDefinition $definition */
158
        $definition = new $class($entryName, $this->className);
159
160
        if ($this->lazy !== null) {
161
            $definition->setLazy($this->lazy);
162
        }
163
        if ($this->scope !== null) {
164
            $definition->setScope($this->scope);
165
        }
166
167
        if (! empty($this->constructor)) {
168
            $parameters = $this->fixParameters($definition, '__construct', $this->constructor);
169
            $constructorInjection = MethodInjection::constructor($parameters);
170
            $definition->setConstructorInjection($constructorInjection);
171
        }
172
173
        if (! empty($this->properties)) {
174
            foreach ($this->properties as $property => $value) {
175
                $definition->addPropertyInjection(
176
                    new PropertyInjection($property, $value)
177
                );
178
            }
179
        }
180
181
        if (! empty($this->methods)) {
182
            foreach ($this->methods as $method => $calls) {
183
                foreach ($calls as $parameters) {
184
                    $parameters = $this->fixParameters($definition, $method, $parameters);
185
                    $methodInjection = new MethodInjection($method, $parameters);
186
                    $definition->addMethodInjection($methodInjection);
187
                }
188
            }
189
        }
190
191
        return $definition;
192
    }
193
194
    /**
195
     * Fixes parameters indexed by the parameter name -> reindex by position.
196
     *
197
     * This is necessary so that merging definitions between sources is possible.
198
     *
199
     * @param ObjectDefinition $definition
200
     * @param string          $method
201
     * @param array           $parameters
202
     * @throws InvalidDefinition
203
     * @return array
204
     */
205
    private function fixParameters(ObjectDefinition $definition, $method, $parameters)
206
    {
207
        $fixedParameters = [];
208
209
        foreach ($parameters as $index => $parameter) {
210
            // Parameter indexed by the parameter name, we reindex it with its position
211
            if (is_string($index)) {
212
                $callable = [$definition->getClassName(), $method];
213
                try {
214
                    $reflectionParameter = new \ReflectionParameter($callable, $index);
215
                } catch (\ReflectionException $e) {
216
                    throw InvalidDefinition::create($definition, sprintf("Parameter with name '%s' could not be found. %s.", $index, $e->getMessage()));
217
                }
218
219
                $index = $reflectionParameter->getPosition();
220
            }
221
222
            $fixedParameters[$index] = $parameter;
223
        }
224
225
        return $fixedParameters;
226
    }
227
}
228