Completed
Push — PSR-11-2 ( a5ad88...7f5041 )
by Nikolaos
03:59
created

Blueprint::__invoke()   B

Complexity

Conditions 7
Paths 27

Size

Total Lines 39
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 21
c 1
b 0
f 0
dl 0
loc 39
ccs 22
cts 22
cp 1
rs 8.6506
cc 7
nc 27
nop 1
crap 7
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 string $className;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_FUNCTION or T_CONST
Loading history...
45
46
    /**
47
     * @var array
48
     */
49
    private array $mutations;
50
51
    /**
52
     * @var array
53
     */
54
    private array $parameters;
55
56
    /**
57
     * @var array
58
     */
59
    private array $setters;
60
61
    /**
62
     * @param string $className
63
     * @param array  $parameters
64
     * @param array  $setters
65
     * @param array  $mutations
66
     */
67 74
    public function __construct(
68
        string $className,
69
        array $parameters = [],
70
        array $setters = [],
71
        array $mutations = []
72
    ) {
73 74
        $this->className  = $className;
74 74
        $this->parameters = $parameters;
75 74
        $this->setters    = $setters;
76 74
        $this->mutations  = $mutations;
77 74
    }
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 50
    public function __invoke(ReflectionClass $reflectedClass): object
90
    {
91 50
        $parameters = $this->parameters;
92 50
        foreach ($parameters as $index => $parameter) {
93 38
            if ($parameter instanceof UnresolvedParameter) {
94 5
                Exception::missingParameter(
95 5
                    $this->className,
96 5
                    $parameter->getName()
97
                );
98
            }
99
100 33
            $parameters[$index] = $this->resolveLazy($parameter);
101
        }
102
103 44
        $object = $reflectedClass->newInstanceArgs($parameters);
104 44
        foreach ($this->setters as $method => $value) {
105 8
            if (!method_exists($this->className, $method)) {
106 2
                Exception::setterMethodNotFound(
107 2
                    $this->className,
108 2
                    $method
109
                );
110
            }
111
112 6
            $value = $this->resolveLazy($value);
113 6
            $object->$method($value);
114
        }
115
116
        /** @var MutationInterface $mutation */
117 42
        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 40
        return $object;
128
    }
129
130
    /**
131
     * @return string
132
     */
133 47
    public function getClassName(): string
134
    {
135 47
        return $this->className;
136
    }
137
138
    /**
139
     * @return array
140
     */
141 60
    public function getMutations(): array
142
    {
143 60
        return $this->mutations;
144
    }
145
146
    /**
147
     * @return array
148
     */
149 60
    public function getSetters(): array
150
    {
151 60
        return $this->setters;
152
    }
153
154
    /**
155
     * @return array
156
     */
157 63
    public function getParameters(): array
158
    {
159 63
        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 46
    public function merge(Blueprint $blueprint): Blueprint
170
    {
171 46
        return new Blueprint(
172 46
            $this->className,
173 46
            $this->mergeParameters($blueprint),
174 46
            $this->mergeSetters($blueprint),
175 46
            $this->mergeMutations($blueprint)
176
        );
177
    }
178
179
    /**
180
     * @param array $parameters
181
     *
182
     * @return Blueprint
183
     */
184 46
    public function replaceParameters(array $parameters): Blueprint
185
    {
186 46
        $clone             = clone $this;
187 46
        $clone->parameters = $parameters;
188
189 46
        return $clone;
190
    }
191
192
    /**
193
     * @param array $parameters
194
     *
195
     * @return Blueprint
196
     */
197 23
    public function withParams(array $parameters): Blueprint
198
    {
199 23
        $clone             = clone $this;
200 23
        $clone->parameters = array_merge($this->parameters, $parameters);
201
202 23
        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 46
    private function mergeMutations(Blueprint $blueprint): array
214
    {
215 46
        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 46
    private function mergeParameters(Blueprint $blueprint): array
231
    {
232 46
        if (empty($blueprint->parameters)) {
233
            // no parameters to merge, micro-optimize the loop
234 38
            return $this->parameters;
235
        }
236
237 14
        $parameters = $this->parameters;
238
239 14
        $position = 0;
240 14
        foreach ($parameters as $key => $value) {
241
            // positional overrides take precedence over named overrides
242 14
            if (array_key_exists($position, $blueprint->parameters)) {
243
                // positional override
244 2
                $value = $blueprint->parameters[$position];
245 13
            } elseif (array_key_exists($key, $blueprint->parameters)) {
246
                // named override
247 12
                $value = $blueprint->parameters[$key];
248
            }
249
250
            // retain the merged value
251 14
            $parameters[$key] = $value;
252
253
            // next position
254 14
            $position += 1;
255
        }
256
257 14
        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 46
    private function mergeSetters(Blueprint $blueprint): array
268
    {
269 46
        return array_merge($this->setters, $blueprint->setters);
270
    }
271
272
    /**
273
     * @param mixed $object
274
     *
275
     * @return mixed
276
     */
277 38
    private function resolveLazy($object)
278
    {
279 38
        if ($object instanceof LazyInterface) {
280 14
            $object = $object();
281
        }
282
283 37
        return $object;
284
    }
285
}
286