Passed
Push — PSR-11-2 ( dfb1a3...d7a3e8 )
by Nikolaos
04:33
created

Blueprint   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 245
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 24
eloc 61
c 1
b 0
f 0
dl 0
loc 245
ccs 71
cts 71
cp 1
rs 10

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A mergeParameters() 0 28 5
A mergeMutations() 0 3 1
B __invoke() 0 39 7
A mergeSetters() 0 3 1
A merge() 0 7 1
A resolveLazy() 0 7 2
A getParameters() 0 3 1
A getMutations() 0 3 1
A replaceParameters() 0 6 1
A getClassName() 0 3 1
A getSetters() 0 3 1
A withParams() 0 6 1
1
<?php
2
3
/**
4
 * This file is part of the Phalcon Framework.
5
 *
6
 * (c) Phalcon Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.txt
9
 * file that was distributed with this source code.
10
 *
11
 * Implementation of this file has been influenced by AuraPHP
12
 *
13
 * @link    https://github.com/auraphp/Aura.Di
14
 * @license https://github.com/auraphp/Aura.Di/blob/4.x/LICENSE
15
 */
16
17
declare(strict_types=1);
18
19
namespace Phalcon\Container\Resolver;
20
21
use Phalcon\Container\Exception;
22
use Phalcon\Container\Exception\MissingParameter;
23
use Phalcon\Container\Exception\MutationDoesNotImplementInterface;
24
use Phalcon\Container\Exception\SetterMethodNotFound;
25
use Phalcon\Container\Injection\LazyInterface;
26
use Phalcon\Container\Injection\MutationInterface;
27
use ReflectionClass;
28
29
use function array_merge;
30
31
/**
32
 * Class Blueprint
33
 *
34
 * @property string $className
35
 * @property array  $mutations
36
 * @property array  $parameters
37
 * @property array  $setters
38
 */
39
final class Blueprint
40
{
41
    /**
42
     * @var string
43
     */
44
    private $className;
45
46
    /**
47
     * @var array
48
     */
49
    private $mutations;
50
51
    /**
52
     * @var array
53
     */
54
    private $parameters;
55
56
    /**
57
     * @var array
58
     */
59
    private $setters;
60
61
    /**
62
     * @param string $className
63
     * @param array  $parameters
64
     * @param array  $setters
65
     * @param array  $mutations
66
     */
67 58
    public function __construct(
68
        string $className,
69
        array $parameters = [],
70
        array $setters = [],
71
        array $mutations = []
72
    ) {
73 58
        $this->className  = $className;
74 58
        $this->parameters = $parameters;
75 58
        $this->setters    = $setters;
76 58
        $this->mutations  = $mutations;
77 58
    }
78
79
    /**
80
     * Instantiates a new object based on the current blueprint.
81
     *
82
     * @param ReflectionClass $reflectedClass
83
     *
84
     * @return object
85
     * @throws MissingParameter
86
     * @throws MutationDoesNotImplementInterface
87
     * @throws SetterMethodNotFound
88
     */
89 35
    public function __invoke(ReflectionClass $reflectedClass): object
90
    {
91 35
        $parameters = $this->parameters;
92 35
        foreach ($parameters as $index => $parameter) {
93 25
            if ($parameter instanceof UnresolvedParameter) {
94 4
                Exception::missingParameter(
95 4
                    $this->className,
96 4
                    $parameter->getName()
97
                );
98
            }
99
100 21
            $parameters[$index] = $this->resolveLazy($parameter);
101
        }
102
103 30
        $object = $reflectedClass->newInstanceArgs($parameters);
104 30
        foreach ($this->setters as $method => $value) {
105 4
            if (!method_exists($this->className, $method)) {
106 1
                Exception::setterMethodNotFound(
107 1
                    $this->className,
108 1
                    $method
109
                );
110
            }
111
112 3
            $value = $this->resolveLazy($value);
113 3
            $object->$method($value);
114
        }
115
116
        /** @var MutationInterface $mutation */
117 29
        foreach ($this->mutations as $mutation) {
118 6
            $mutation = $this->resolveLazy($mutation);
119
120 6
            if ($mutation instanceof MutationInterface === false) {
121 2
                Exception::mutationDoesNotImplementInterface($mutation);
122
            }
123
124 4
            $object = $mutation($object);
125
        }
126
127 27
        return $object;
128
    }
129
130
    /**
131
     * @return string
132
     */
133 32
    public function getClassName(): string
134
    {
135 32
        return $this->className;
136
    }
137
138
    /**
139
     * @return array
140
     */
141 45
    public function getMutations(): array
142
    {
143 45
        return $this->mutations;
144
    }
145
146
    /**
147
     * @return array
148
     */
149 45
    public function getSetters(): array
150
    {
151 45
        return $this->setters;
152
    }
153
154
    /**
155
     * @return array
156
     */
157 48
    public function getParameters(): array
158
    {
159 48
        return $this->parameters;
160
    }
161
162
    /**
163
     * Merges all parameters and invokes the lazy ones.
164
     *
165
     * @param Blueprint $blueprint The overrides during merging
166
     *
167
     * @return Blueprint The merged blueprint
168
     */
169 31
    public function merge(Blueprint $blueprint): Blueprint
170
    {
171 31
        return new Blueprint(
172 31
            $this->className,
173 31
            $this->mergeParameters($blueprint),
174 31
            $this->mergeSetters($blueprint),
175 31
            $this->mergeMutations($blueprint)
176
        );
177
    }
178
179
    /**
180
     * @param array $parameters
181
     *
182
     * @return Blueprint
183
     */
184 31
    public function replaceParameters(array $parameters): Blueprint
185
    {
186 31
        $clone             = clone $this;
187 31
        $clone->parameters = $parameters;
188
189 31
        return $clone;
190
    }
191
192
    /**
193
     * @param array $parameters
194
     *
195
     * @return Blueprint
196
     */
197 17
    public function withParams(array $parameters): Blueprint
198
    {
199 17
        $clone             = clone $this;
200 17
        $clone->parameters = array_merge($this->parameters, $parameters);
201
202 17
        return $clone;
203
    }
204
205
    /**
206
     *
207
     * Merges the setters with overrides; also invokes Lazy values.
208
     *
209
     * @param Blueprint $blueprint A blueprint containing additional mutations.
210
     *
211
     * @return array The merged mutations
212
     */
213 31
    private function mergeMutations(Blueprint $blueprint): array
214
    {
215 31
        return array_merge($this->mutations, $blueprint->mutations);
216
    }
217
218
    /**
219
     *
220
     * Merges the parameters with overrides; also invokes Lazy values.
221
     *
222
     * @param Blueprint $blueprint  A blueprint containing override parameters;
223
     *                              the key may be the name *or* the numeric
224
     *                              position of the constructor parameter,
225
     *                              and the value is the parameter value to use.
226
     *
227
     * @return array The merged parameters
228
     *
229
     */
230 31
    private function mergeParameters(Blueprint $blueprint): array
231
    {
232 31
        if (empty($blueprint->parameters)) {
233
            // no parameters to merge, micro-optimize the loop
234 25
            return $this->parameters;
235
        }
236
237 10
        $parameters = $this->parameters;
238
239 10
        $position = 0;
240 10
        foreach ($parameters as $key => $value) {
241
            // positional overrides take precedence over named overrides
242 10
            if (array_key_exists($position, $blueprint->parameters)) {
243
                // positional override
244 1
                $value = $blueprint->parameters[$position];
245 10
            } elseif (array_key_exists($key, $blueprint->parameters)) {
246
                // named override
247 9
                $value = $blueprint->parameters[$key];
248
            }
249
250
            // retain the merged value
251 10
            $parameters[$key] = $value;
252
253
            // next position
254 10
            $position += 1;
255
        }
256
257 10
        return $parameters;
258
    }
259
260
    /**
261
     * Merges the setters with overrides; also invokes Lazy values.
262
     *
263
     * @param Blueprint $blueprint A blueprint containing override setters.
264
     *
265
     * @return array The merged setters
266
     */
267 31
    private function mergeSetters(Blueprint $blueprint): array
268
    {
269 31
        return array_merge($this->setters, $blueprint->setters);
270
    }
271
272
    /**
273
     * @param mixed $object
274
     *
275
     * @return mixed
276
     */
277 25
    private function resolveLazy($object)
278
    {
279 25
        if ($object instanceof LazyInterface) {
280 9
            $object = $object();
281
        }
282
283 24
        return $object;
284
    }
285
}
286