Completed
Push — master ( 1feeef...279c4f )
by Matthieu
8s
created

ObjectDefinition::__toString()   A

Complexity

Conditions 1
Paths 1

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 4
Code Lines 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 4
rs 10
1
<?php
2
3
namespace DI\Definition;
4
5
use DI\Definition\Dumper\ObjectDefinitionDumper;
6
use DI\Definition\ObjectDefinition\MethodInjection;
7
use DI\Definition\ObjectDefinition\PropertyInjection;
8
use DI\Scope;
9
use ReflectionClass;
10
11
/**
12
 * Defines how an object can be instantiated.
13
 *
14
 * @author Matthieu Napoli <[email protected]>
15
 */
16
class ObjectDefinition implements Definition, CacheableDefinition, HasSubDefinition
17
{
18
    /**
19
     * Entry name (most of the time, same as $classname).
20
     * @var string
21
     */
22
    private $name;
23
24
    /**
25
     * Class name (if null, then the class name is $name).
26
     * @var string|null
27
     */
28
    private $className;
29
30
    /**
31
     * Constructor parameter injection.
32
     * @var MethodInjection|null
33
     */
34
    private $constructorInjection;
35
36
    /**
37
     * Property injections.
38
     * @var PropertyInjection[]
39
     */
40
    private $propertyInjections = [];
41
42
    /**
43
     * Method calls.
44
     * @var MethodInjection[][]
45
     */
46
    private $methodInjections = [];
47
48
    /**
49
     * @var string|null
50
     */
51
    private $scope;
52
53
    /**
54
     * @var bool|null
55
     */
56
    private $lazy;
57
58
    /**
59
     * Store if the class exists. Storing it (in cache) avoids recomputing this.
60
     *
61
     * @var bool
62
     */
63
    private $classExists;
64
65
    /**
66
     * Store if the class is instantiable. Storing it (in cache) avoids recomputing this.
67
     *
68
     * @var bool
69
     */
70
    private $isInstantiable;
71
72
    /**
73
     * @param string $name Class name
74
     * @param string $className
75
     */
76
    public function __construct($name, $className = null)
77
    {
78
        $this->name = (string) $name;
79
        $this->setClassName($className);
80
    }
81
82
    /**
83
     * @return string Entry name
84
     */
85
    public function getName()
86
    {
87
        return $this->name;
88
    }
89
90
    /**
91
     * @param string|null $className
92
     */
93
    public function setClassName($className)
94
    {
95
        $this->className = $className;
96
97
        $this->updateCache();
98
    }
99
100
    /**
101
     * @return string Class name
102
     */
103
    public function getClassName()
104
    {
105
        if ($this->className !== null) {
106
            return $this->className;
107
        }
108
109
        return $this->name;
110
    }
111
112
    /**
113
     * @return MethodInjection|null
114
     */
115
    public function getConstructorInjection()
116
    {
117
        return $this->constructorInjection;
118
    }
119
120
    /**
121
     * @param MethodInjection $constructorInjection
122
     */
123
    public function setConstructorInjection(MethodInjection $constructorInjection)
124
    {
125
        $this->constructorInjection = $constructorInjection;
126
    }
127
128
    /**
129
     * @return PropertyInjection[] Property injections
130
     */
131
    public function getPropertyInjections()
132
    {
133
        return $this->propertyInjections;
134
    }
135
136
    public function addPropertyInjection(PropertyInjection $propertyInjection)
137
    {
138
        $className = $propertyInjection->getClassName();
139
        if ($className) {
0 ignored issues
show
Bug Best Practice introduced by Matthieu Napoli
The expression $className of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
140
            // Index with the class name to avoid collisions between parent and
141
            // child private properties with the same name
142
            $key = $className . '::' . $propertyInjection->getPropertyName();
143
        } else {
144
            $key = $propertyInjection->getPropertyName();
145
        }
146
147
        $this->propertyInjections[$key] = $propertyInjection;
148
    }
149
150
    /**
151
     * @return MethodInjection[] Method injections
152
     */
153
    public function getMethodInjections()
154
    {
155
        // Return array leafs
156
        $injections = [];
157
        array_walk_recursive($this->methodInjections, function ($injection) use (&$injections) {
158
            $injections[] = $injection;
159
        });
160
161
        return $injections;
162
    }
163
164
    /**
165
     * @param MethodInjection $methodInjection
166
     */
167
    public function addMethodInjection(MethodInjection $methodInjection)
168
    {
169
        $method = $methodInjection->getMethodName();
170
        if (! isset($this->methodInjections[$method])) {
171
            $this->methodInjections[$method] = [];
172
        }
173
        $this->methodInjections[$method][] = $methodInjection;
174
    }
175
176
    /**
177
     * @param string $scope
178
     */
179
    public function setScope($scope)
180
    {
181
        $this->scope = $scope;
182
    }
183
184
    /**
185
     * [email protected]}
186
     */
187
    public function getScope()
188
    {
189
        return $this->scope ?: Scope::SINGLETON;
190
    }
191
192
    /**
193
     * @param bool|null $lazy
194
     */
195
    public function setLazy($lazy)
196
    {
197
        $this->lazy = $lazy;
198
    }
199
200
    /**
201
     * @return bool
202
     */
203
    public function isLazy()
204
    {
205
        if ($this->lazy !== null) {
206
            return $this->lazy;
207
        } else {
208
            // Default value
209
            return false;
210
        }
211
    }
212
213
    /**
214
     * @return bool
215
     */
216
    public function classExists()
217
    {
218
        return $this->classExists;
219
    }
220
221
    /**
222
     * @return bool
223
     */
224
    public function isInstantiable()
225
    {
226
        return $this->isInstantiable;
227
    }
228
229
    /**
230
     * [email protected]}
231
     */
232
    public function getSubDefinitionName()
233
    {
234
        return $this->getClassName();
235
    }
236
237
    /**
238
     * [email protected]}
239
     */
240
    public function setSubDefinition(Definition $definition)
241
    {
242
        if (! $definition instanceof self) {
243
            return;
244
        }
245
246
        // The current prevails
247
        if ($this->className === null) {
248
            $this->setClassName($definition->className);
249
        }
250
        if ($this->scope === null) {
251
            $this->scope = $definition->scope;
252
        }
253
        if ($this->lazy === null) {
254
            $this->lazy = $definition->lazy;
255
        }
256
257
        // Merge constructor injection
258
        $this->mergeConstructorInjection($definition);
259
260
        // Merge property injections
261
        $this->mergePropertyInjections($definition);
262
263
        // Merge method injections
264
        $this->mergeMethodInjections($definition);
265
    }
266
267
    public function __toString()
268
    {
269
        return (new ObjectDefinitionDumper)->dump($this);
270
    }
271
272
    private function mergeConstructorInjection(ObjectDefinition $definition)
273
    {
274
        if ($definition->getConstructorInjection() !== null) {
275
            if ($this->constructorInjection !== null) {
276
                // Merge
277
                $this->constructorInjection->merge($definition->getConstructorInjection());
278
            } else {
279
                // Set
280
                $this->constructorInjection = $definition->getConstructorInjection();
281
            }
282
        }
283
    }
284
285
    private function mergePropertyInjections(ObjectDefinition $definition)
286
    {
287
        foreach ($definition->propertyInjections as $propertyName => $propertyInjection) {
288
            if (! isset($this->propertyInjections[$propertyName])) {
289
                // Add
290
                $this->propertyInjections[$propertyName] = $propertyInjection;
291
            }
292
        }
293
    }
294
295
    private function mergeMethodInjections(ObjectDefinition $definition)
296
    {
297
        foreach ($definition->methodInjections as $methodName => $calls) {
298
            if (array_key_exists($methodName, $this->methodInjections)) {
299
                $this->mergeMethodCalls($calls, $methodName);
300
            } else {
301
                // Add
302
                $this->methodInjections[$methodName] = $calls;
303
            }
304
        }
305
    }
306
307
    private function mergeMethodCalls(array $calls, $methodName)
308
    {
309
        foreach ($calls as $index => $methodInjection) {
310
            // Merge
311
            if (array_key_exists($index, $this->methodInjections[$methodName])) {
312
                // Merge
313
                $this->methodInjections[$methodName][$index]->merge($methodInjection);
314
            } else {
315
                // Add
316
                $this->methodInjections[$methodName][$index] = $methodInjection;
317
            }
318
        }
319
    }
320
321
    private function updateCache()
322
    {
323
        $className = $this->getClassName();
324
325
        $this->classExists = class_exists($className) || interface_exists($className);
326
327
        if (! $this->classExists) {
328
            $this->isInstantiable = false;
329
330
            return;
331
        }
332
333
        $class = new ReflectionClass($className);
334
        $this->isInstantiable = $class->isInstantiable();
335
    }
336
}
337