Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace DI\Definition;
6
 
7
use DI\Definition\Dumper\ObjectDefinitionDumper;
8
use DI\Definition\ObjectDefinition\MethodInjection;
9
use DI\Definition\ObjectDefinition\PropertyInjection;
10
use DI\Definition\Source\DefinitionArray;
11
use ReflectionClass;
12
 
13
/**
14
 * Defines how an object can be instantiated.
15
 *
16
 * @author Matthieu Napoli <matthieu@mnapoli.fr>
17
 */
18
class ObjectDefinition implements Definition
19
{
20
    /**
21
     * Entry name (most of the time, same as $classname).
22
     */
23
    private string $name;
24
 
25
    /**
26
     * Class name (if null, then the class name is $name).
27
     */
28
    protected ?string $className = null;
29
 
30
    protected ?MethodInjection $constructorInjection = null;
31
 
32
    protected array $propertyInjections = [];
33
 
34
    /**
35
     * Method calls.
36
     * @var MethodInjection[][]
37
     */
38
    protected array $methodInjections = [];
39
 
40
    protected ?bool $lazy = null;
41
 
42
    /**
43
     * Store if the class exists. Storing it (in cache) avoids recomputing this.
44
     */
45
    private bool $classExists;
46
 
47
    /**
48
     * Store if the class is instantiable. Storing it (in cache) avoids recomputing this.
49
     */
50
    private bool $isInstantiable;
51
 
52
    /**
53
     * @param string $name Entry name
54
     */
55
    public function __construct(string $name, string $className = null)
56
    {
57
        $this->name = $name;
58
        $this->setClassName($className);
59
    }
60
 
61
    public function getName() : string
62
    {
63
        return $this->name;
64
    }
65
 
66
    public function setName(string $name) : void
67
    {
68
        $this->name = $name;
69
    }
70
 
71
    public function setClassName(?string $className) : void
72
    {
73
        $this->className = $className;
74
 
75
        $this->updateCache();
76
    }
77
 
78
    public function getClassName() : string
79
    {
80
        return $this->className ?? $this->name;
81
    }
82
 
83
    public function getConstructorInjection() : ?MethodInjection
84
    {
85
        return $this->constructorInjection;
86
    }
87
 
88
    public function setConstructorInjection(MethodInjection $constructorInjection) : void
89
    {
90
        $this->constructorInjection = $constructorInjection;
91
    }
92
 
93
    public function completeConstructorInjection(MethodInjection $injection) : void
94
    {
95
        if ($this->constructorInjection !== null) {
96
            // Merge
97
            $this->constructorInjection->merge($injection);
98
        } else {
99
            // Set
100
            $this->constructorInjection = $injection;
101
        }
102
    }
103
 
104
    /**
105
     * @return PropertyInjection[] Property injections
106
     */
107
    public function getPropertyInjections() : array
108
    {
109
        return $this->propertyInjections;
110
    }
111
 
112
    public function addPropertyInjection(PropertyInjection $propertyInjection) : void
113
    {
114
        $className = $propertyInjection->getClassName();
115
        if ($className) {
116
            // Index with the class name to avoid collisions between parent and
117
            // child private properties with the same name
118
            $key = $className . '::' . $propertyInjection->getPropertyName();
119
        } else {
120
            $key = $propertyInjection->getPropertyName();
121
        }
122
 
123
        $this->propertyInjections[$key] = $propertyInjection;
124
    }
125
 
126
    /**
127
     * @return MethodInjection[] Method injections
128
     */
129
    public function getMethodInjections() : array
130
    {
131
        // Return array leafs
132
        $injections = [];
133
        array_walk_recursive($this->methodInjections, function ($injection) use (&$injections) {
134
            $injections[] = $injection;
135
        });
136
 
137
        return $injections;
138
    }
139
 
140
    public function addMethodInjection(MethodInjection $methodInjection) : void
141
    {
142
        $method = $methodInjection->getMethodName();
143
        if (! isset($this->methodInjections[$method])) {
144
            $this->methodInjections[$method] = [];
145
        }
146
        $this->methodInjections[$method][] = $methodInjection;
147
    }
148
 
149
    public function completeFirstMethodInjection(MethodInjection $injection) : void
150
    {
151
        $method = $injection->getMethodName();
152
 
153
        if (isset($this->methodInjections[$method][0])) {
154
            // Merge
155
            $this->methodInjections[$method][0]->merge($injection);
156
        } else {
157
            // Set
158
            $this->addMethodInjection($injection);
159
        }
160
    }
161
 
162
    public function setLazy(bool $lazy = null) : void
163
    {
164
        $this->lazy = $lazy;
165
    }
166
 
167
    public function isLazy() : bool
168
    {
169
        if ($this->lazy !== null) {
170
            return $this->lazy;
171
        }
172
 
173
        // Default value
174
        return false;
175
    }
176
 
177
    public function classExists() : bool
178
    {
179
        return $this->classExists;
180
    }
181
 
182
    public function isInstantiable() : bool
183
    {
184
        return $this->isInstantiable;
185
    }
186
 
187
    public function replaceNestedDefinitions(callable $replacer) : void
188
    {
189
        array_walk($this->propertyInjections, function (PropertyInjection $propertyInjection) use ($replacer) {
190
            $propertyInjection->replaceNestedDefinition($replacer);
191
        });
192
 
193
        $this->constructorInjection?->replaceNestedDefinitions($replacer);
194
 
195
        array_walk($this->methodInjections, function ($injectionArray) use ($replacer) {
196
            array_walk($injectionArray, function (MethodInjection $methodInjection) use ($replacer) {
197
                $methodInjection->replaceNestedDefinitions($replacer);
198
            });
199
        });
200
    }
201
 
202
    /**
203
     * Replaces all the wildcards in the string with the given replacements.
204
     *
205
     * @param string[] $replacements
206
     */
207
    public function replaceWildcards(array $replacements) : void
208
    {
209
        $className = $this->getClassName();
210
 
211
        foreach ($replacements as $replacement) {
212
            $pos = strpos($className, DefinitionArray::WILDCARD);
213
            if ($pos !== false) {
214
                $className = substr_replace($className, $replacement, $pos, 1);
215
            }
216
        }
217
 
218
        $this->setClassName($className);
219
    }
220
 
221
    public function __toString() : string
222
    {
223
        return (new ObjectDefinitionDumper)->dump($this);
224
    }
225
 
226
    private function updateCache() : void
227
    {
228
        $className = $this->getClassName();
229
 
230
        $this->classExists = class_exists($className) || interface_exists($className);
231
 
232
        if (! $this->classExists) {
233
            $this->isInstantiable = false;
234
 
235
            return;
236
        }
237
 
238
        /** @var class-string $className */
239
        $class = new ReflectionClass($className);
240
        $this->isInstantiable = $class->isInstantiable();
241
    }
242
}