Passed
Push — PSR-11-2 ( e86d47...dfb1a3 )
by Nikolaos
05:09
created

Blueprint   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Test Coverage

Coverage 97.3%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 24
eloc 65
c 1
b 0
f 0
dl 0
loc 252
ccs 72
cts 74
cp 0.973
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 45 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 50
    public function __construct(
68
        string $className,
69
        array $parameters = [],
70
        array $setters = [],
71
        array $mutations = []
72
    ) {
73 50
        $this->className  = $className;
74 50
        $this->parameters = $parameters;
75 50
        $this->setters    = $setters;
76 50
        $this->mutations  = $mutations;
77 50
    }
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 28
    public function __invoke(ReflectionClass $reflectedClass): object
90
    {
91 28
        $parameters = $this->parameters;
92 28
        foreach ($parameters as $index => $parameter) {
93 22
            if ($parameter instanceof UnresolvedParameter) {
94 4
                throw new MissingParameter(
95
                    sprintf(
96 4
                        "Parameter missing: %s::\$%s",
97 4
                        $this->className,
98 4
                        $parameter->getName()
99
                    )
100
                );
101
            }
102
103 18
            $parameters[$index] = $this->resolveLazy($parameter);
104
        }
105
106 23
        $object = $reflectedClass->newInstanceArgs($parameters);
107 23
        foreach ($this->setters as $method => $value) {
108 4
            if (!method_exists($this->className, $method)) {
109 1
                throw new SetterMethodNotFound(
110
                    sprintf(
111 1
                        "Setter method not found: %s::%s()",
112 1
                        $this->className,
113
                        $method
114
                    )
115
                );
116
            }
117
118 3
            $value = $this->resolveLazy($value);
119 3
            $object->$method($value);
120
        }
121
122
        /** @var MutationInterface $mutation */
123 22
        foreach ($this->mutations as $mutation) {
124 2
            $mutation = $this->resolveLazy($mutation);
125
126 2
            if ($mutation instanceof MutationInterface === false) {
127 1
                throw Exception::mutationDoesNotImplementInterface($mutation);
128
            }
129
130 1
            $object = $mutation($object);
131
        }
132
133 21
        return $object;
134
    }
135
136
    /**
137
     * @return string
138
     */
139 26
    public function getClassName(): string
140
    {
141 26
        return $this->className;
142
    }
143
144
    /**
145
     * @return array
146
     */
147 38
    public function getMutations(): array
148
    {
149 38
        return $this->mutations;
150
    }
151
152
    /**
153
     * @return array
154
     */
155 38
    public function getSetters(): array
156
    {
157 38
        return $this->setters;
158
    }
159
160
    /**
161
     * @return array
162
     */
163 41
    public function getParameters(): array
164
    {
165 41
        return $this->parameters;
166
    }
167
168
    /**
169
     * Merges all parameters and invokes the lazy ones.
170
     *
171
     * @param Blueprint $blueprint The overrides during merging
172
     *
173
     * @return Blueprint The merged blueprint
174
     */
175 25
    public function merge(Blueprint $blueprint): Blueprint
176
    {
177 25
        return new Blueprint(
178 25
            $this->className,
179 25
            $this->mergeParameters($blueprint),
180 25
            $this->mergeSetters($blueprint),
181 25
            $this->mergeMutations($blueprint)
182
        );
183
    }
184
185
    /**
186
     * @param array $parameters
187
     *
188
     * @return Blueprint
189
     */
190 25
    public function replaceParameters(array $parameters): Blueprint
191
    {
192 25
        $clone             = clone $this;
193 25
        $clone->parameters = $parameters;
194
195 25
        return $clone;
196
    }
197
198
    /**
199
     * @param array $parameters
200
     *
201
     * @return Blueprint
202
     */
203 15
    public function withParams(array $parameters): Blueprint
204
    {
205 15
        $clone             = clone $this;
206 15
        $clone->parameters = array_merge($this->parameters, $parameters);
207
208 15
        return $clone;
209
    }
210
211
    /**
212
     *
213
     * Merges the setters with overrides; also invokes Lazy values.
214
     *
215
     * @param Blueprint $blueprint A blueprint containing additional mutations.
216
     *
217
     * @return array The merged mutations
218
     */
219 25
    private function mergeMutations(Blueprint $blueprint): array
220
    {
221 25
        return array_merge($this->mutations, $blueprint->mutations);
222
    }
223
224
    /**
225
     *
226
     * Merges the parameters with overrides; also invokes Lazy values.
227
     *
228
     * @param Blueprint $blueprint  A blueprint containing override parameters;
229
     *                              the key may be the name *or* the numeric
230
     *                              position of the constructor parameter,
231
     *                              and the value is the parameter value to use.
232
     *
233
     * @return array The merged parameters
234
     *
235
     */
236 25
    private function mergeParameters(Blueprint $blueprint): array
237
    {
238 25
        if (!$blueprint->parameters) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $blueprint->parameters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
239
            // no parameters to merge, micro-optimize the loop
240 20
            return $this->parameters;
241
        }
242
243 9
        $parameters = $this->parameters;
244
245 9
        $position = 0;
246 9
        foreach ($parameters as $key => $value) {
247
            // positional overrides take precedence over named overrides
248 9
            if (array_key_exists($position, $blueprint->parameters)) {
249
                // positional override
250 1
                $value = $blueprint->parameters[$position];
251 9
            } elseif (array_key_exists($key, $blueprint->parameters)) {
252
                // named override
253 8
                $value = $blueprint->parameters[$key];
254
            }
255
256
            // retain the merged value
257 9
            $parameters[$key] = $value;
258
259
            // next position
260 9
            $position += 1;
261
        }
262
263 9
        return $parameters;
264
    }
265
266
    /**
267
     *
268
     * Merges the setters with overrides; also invokes Lazy values.
269
     *
270
     * @param Blueprint $blueprint A blueprint containing override setters.
271
     *
272
     * @return array The merged setters
273
     */
274 25
    private function mergeSetters(Blueprint $blueprint): array
275
    {
276 25
        return array_merge($this->setters, $blueprint->setters);
277
    }
278
279
    /**
280
     * @param mixed $object
281
     *
282
     * @return mixed
283
     */
284 19
    private function resolveLazy($object)
285
    {
286 19
        if ($object instanceof LazyInterface) {
287 6
            $object = $object();
288
        }
289
290 18
        return $object;
291
    }
292
}
293