Resolver   F
last analyzed

Complexity

Total Complexity 76

Size/Duplication

Total Lines 573
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 22
Bugs 1 Features 0
Metric Value
wmc 76
eloc 171
dl 0
loc 573
ccs 192
cts 192
cp 1
rs 2.32
c 22
b 1
f 0

51 Methods

Rating   Name   Duplication   Size   Complexity  
__serialize() 0 3 ?
A hp$0 ➔ __serialize() 0 3 1
A hp$0 ➔ __clone() 0 9 3
__unserialize() 0 3 ?
B __construct() 0 18 8
A hp$0 ➔ __unserialize() 0 3 1
__invoke() 0 3 ?
A hp$0 ➔ __invoke() 0 2 1
__clone() 0 9 ?
resolvable() 0 5 ?
pluginArray() 0 4 ?
C hp$0 ➔ hydrate() 0 39 12
parent() 0 3 ?
provide() 0 24 ?
solve() 0 4 ?
B hp$0 ➔ provide() 0 24 9
resolver() 0 3 ?
A child() 0 3 1
resolve() 0 3 ?
hydrate() 0 39 ?
A filterable() 0 4 1
A hp$0 ➔ provider() 0 3 1
A hp$0 ➔ parent() 0 3 1
variadic() 0 3 ?
A hp$0 ➔ pluginArray() 0 4 3
A hp$0 ➔ param() 0 13 3
C hp$0 ➔ gem() 0 93 8
A hp$0 ➔ plugin() 0 19 5
A hp$0 ➔ scoped() 0 3 3
A hp$0 ➔ variadic() 0 3 3
A hp$0 ➔ solve() 0 4 3
provision() 0 4 ?
A filter() 0 21 5
merge() 0 15 ?
A hp$0 ➔ __call() 0 3 1
A hp$0 ➔ resolve() 0 3 1
A arguments() 0 4 4
param() 0 13 ?
gem() 0 93 ?
A hp$0 ➔ vars() 0 3 1
A hp$0 ➔ resolver() 0 3 1
A hp$0 ➔ provision() 0 4 3
B hp$0 ➔ merge() 0 15 8
provider() 0 3 ?
scoped() 0 3 ?
vars() 0 3 ?
A hp$0 ➔ resolvable() 0 5 3
plugin() 0 19 ?
A bind() 0 3 2
A args() 0 15 5
__call() 0 3 ?

How to fix   Complexity   

Complex Class

Complex classes like Resolver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Resolver, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 *
4
 */
5
6
namespace Mvc5\Resolver;
7
8
use ArrayAccess;
9
use Closure;
10
use Mvc5\ArrayModel;
11
use Mvc5\Config\Configuration;
12
use Mvc5\Config\Model;
13
use Mvc5\Config\Scopable;
14
use Mvc5\Config\Scope;
15
use Mvc5\Container;
16
use Mvc5\Plugin\Gem\Args;
17
use Mvc5\Plugin\Gem\Call;
18
use Mvc5\Plugin\Gem\Calls;
19
use Mvc5\Plugin\Gem\Child;
20
use Mvc5\Plugin\Gem\Config;
21
use Mvc5\Plugin\Gem\Copy;
22
use Mvc5\Plugin\Gem\Expect;
23
use Mvc5\Plugin\Gem\Factory;
24
use Mvc5\Plugin\Gem\FileInclude;
25
use Mvc5\Plugin\Gem\Filter;
26
use Mvc5\Plugin\Gem\Gem;
27
use Mvc5\Plugin\Gem\Invokable;
28
use Mvc5\Plugin\Gem\Invoke;
29
use Mvc5\Plugin\Gem\Link;
30
use Mvc5\Plugin\Gem\Param;
31
use Mvc5\Plugin\Gem\Plug;
32
use Mvc5\Plugin\Gem\Plugin;
33
use Mvc5\Plugin\Gem\Provide;
34
use Mvc5\Plugin\Gem\Scoped;
35
use Mvc5\Plugin\Gem\Shared;
36
use Mvc5\Plugin\Gem\SignalArgs;
37
use Mvc5\Plugin\Gem\Value;
38
use Mvc5\Resolvable;
39
use Throwable;
40
41
use function array_merge;
42
use function array_shift;
43
use function explode;
44
use function is_array;
45
use function is_object;
46
use function is_string;
47
use function key;
48
use function substr;
49
50
use const Mvc5\{ ARGS, CALL_SEPARATOR, CALLS, CONTAINER, EVENTS, INDEX, MAX_RECURSION, NAME, PARAM,
0 ignored issues
show
Bug introduced by
The type Mvc5\CALLS was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\MAX_RECURSION was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\PARAM was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\CALL_SEPARATOR was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\INDEX was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\ARGS was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\EVENTS was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\NAME was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
51
    PROPERTY, SERVICE_RESOLVER, SERVICE_SEPARATOR, SERVICES };
0 ignored issues
show
Bug introduced by
The type Mvc5\SERVICE_RESOLVER was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\SERVICE_SEPARATOR was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\SERVICES was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Mvc5\PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
52
53
trait Resolver
54
{
55
    /**
56
     *
57
     */
58
    use Base;
59
    use Build;
60
    use Generator;
61
    use Scope;
62
    use Service;
63
64
    /**
65
     * @var callable
66
     */
67
    protected $provider;
68
69
    /**
70
     * @param array|ArrayAccess|null $config
71
     * @param callable|null $provider
72
     * @param bool|object|null $scope
73
     * @param bool $strict
74
     * @throws Throwable
75
     */
76 306
    function __construct($config = null, callable $provider = null, $scope = null, bool $strict = false)
77
    {
78 306
        $this->config = $config instanceof Model ? $config : new ArrayModel((array) $config);
79
80 306
        $this->container = ! isset($config[CONTAINER]) ? new Container :
81 28
            ($config[CONTAINER] instanceof Configuration ? $config[CONTAINER] : new Container((array) $config[CONTAINER]));
82
83 306
        $this->events = ! isset($config[EVENTS]) ? new ArrayModel :
84 21
            ($config[EVENTS] instanceof ArrayAccess ? $config[EVENTS] : new ArrayModel((array) $config[EVENTS]));
85
86 306
        $this->services = ! isset($config[SERVICES]) ? new ArrayModel :
87 123
            ($config[SERVICES] instanceof Model ? $config[SERVICES] : new ArrayModel((array) $config[SERVICES]));
88
89 306
        $this->provider = $provider;
90
91 306
        $this->strict = $strict;
92
93 306
        $this->scope = $this->resolve($scope) ?? false;
94 306
    }
95
96
    /**
97
     * @param array|mixed $args
98
     * @return array|mixed
99
     * @throws Throwable
100
     */
101 110
    protected function args($args)
102
    {
103 110
        if (!$args) {
104 72
            return $args;
105
        }
106
107 74
        if (!is_array($args)) {
108 1
            return $this->resolve($args);
109
        }
110
111 73
        foreach($args as $index => $value) {
112 73
            $value instanceof Resolvable && $args[$index] = $this->resolve($value);
113
        }
114
115 73
        return $args;
116
    }
117
118
    /**
119
     * @param array $child
120
     * @param array $parent
121
     * @return array
122
     */
123 47
    protected function arguments(array $child, array $parent) : array
124
    {
125 47
        return !$parent ? $child : (
126 47
            !$child ? $parent : (is_string(key($child)) ? $child + $parent : [...$child, ...$parent])
127
        );
128
    }
129
130
    /**
131
     * @param Closure $callback
132
     * @param object $object
133
     * @param bool $scoped
134
     * @return Closure
135
     */
136 16
    protected function bind(Closure $callback, $object, bool $scoped) : Closure
137
    {
138 16
        return Closure::bind($callback, $object, $scoped ? $object : null);
139
    }
140
141
    /**
142
     * @param Child $child
143
     * @param array $args
144
     * @return mixed
145
     * @throws Throwable
146
     */
147 3
    protected function child(Child $child, array $args = [])
148
    {
149 3
        return $this->provide($this->merge($this->parent($child->parent()), $child), $args);
150
    }
151
152
    /**
153
     * @param mixed $value
154
     * @param iterable $filters
155
     * @param array $args
156
     * @param string|null $param
157
     * @return mixed
158
     * @throws Throwable
159
     */
160 5
    protected function filter($value, iterable $filters = [], array $args = [], string $param = null)
161
    {
162 5
        $result = $value;
163
164 5
        foreach($filters as $filter) {
165 5
            $value = $this->invoke(
166 5
                $this->callable($filter), $param ? [$param => $result] + $args : [$result, ...$args]
167
            );
168
169 5
            if (false === $value) {
170 1
                return $result;
171
            }
172
173 5
            if (null === $value) {
174 1
                return null;
175
            }
176
177 5
            $result = $value;
178
        }
179
180 3
        return $result;
181
    }
182
183
    /**
184
     * @param Filter $filter
185
     * @param array $args
186
     * @return mixed
187
     * @throws Throwable
188
     */
189 5
    protected function filterable(Filter $filter, array $args = [])
190
    {
191 5
        return $this->filter(
192 5
            $this->resolve($filter->config()), $this->resolve($filter->plugin()), $args, $filter->param()
0 ignored issues
show
Bug introduced by
It seems like $this->resolve($filter->plugin()) can also be of type mixed; however, parameter $filters of Mvc5\Resolver\Resolver::filter() does only seem to accept iterable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

192
            $this->resolve($filter->config()), /** @scrutinizer ignore-type */ $this->resolve($filter->plugin()), $args, $filter->param()
Loading history...
193
        );
194
    }
195
196
    /**
197
     * @param Gem $gem
198
     * @param array $args
199
     * @return callable|mixed
200
     * @throws Throwable
201
     */
202 132
    protected function gem(Gem $gem, array $args = [])
203
    {
204 132
        if ($gem instanceof Factory) {
205 1
            return $this->invoke($this->child($gem, $args));
0 ignored issues
show
Bug introduced by
$this->child($gem, $args) of type object is incompatible with the type callable expected by parameter $callable of Mvc5\Resolver\Resolver::invoke(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

205
            return $this->invoke(/** @scrutinizer ignore-type */ $this->child($gem, $args));
Loading history...
206
        }
207
208 131
        if ($gem instanceof Calls) {
209 1
            return $this->hydrate($gem, $this->resolve($gem->name(), $args));
210
        }
211
212 131
        if ($gem instanceof Child) {
213 2
            return $this->child($gem, $args);
214
        }
215
216 129
        if ($gem instanceof Plugin) {
217 58
            return $this->provide($gem, $args);
218
        }
219
220 102
        if ($gem instanceof Shared) {
221 9
            return $this->shared($gem->name(), $gem->config(), $args);
222
        }
223
224 99
        if ($gem instanceof Param) {
225 20
            return $this->resolve($this->param($gem->name()), $args);
226
        }
227
228 98
        if ($gem instanceof Call) {
229 26
            return $this->call($this->resolve($gem->config()), $this->vars($args, $gem->args()));
230
        }
231
232 86
        if ($gem instanceof Args) {
233 8
            return $this->args($gem->config());
234
        }
235
236 82
        if ($gem instanceof Config) {
237 5
            return $this->config;
238
        }
239
240 77
        if ($gem instanceof Link) {
241 24
            return $this;
242
        }
243
244 59
        if ($gem instanceof Filter) {
245 5
            return $this->filterable($gem, $this->vars($args, $gem->args()));
246
        }
247
248 54
        if ($gem instanceof Plug) {
249 4
            return $this->configured($gem->name());
250
        }
251
252 51
        if ($gem instanceof Invoke) {
253 14
            return fn(...$argv) => $this->resolve($this->call(
254 14
                $this->resolve($gem->config()), $this->vars($this->variadic($argv), $gem->args())
255 14
            ));
256
        }
257
258 45
        if ($gem instanceof Invokable) {
259 7
            return fn(...$argv) => $this->resolve($gem->config(), $this->vars($this->variadic($argv), $gem->args()));
260
        }
261
262 38
        if ($gem instanceof FileInclude) {
263
            return (new class() {
264
                function __invoke($file) {
265 1
                    return include $file;
266
                }
267 1
            })($this->resolve($gem->config()));
268
        }
269
270 37
        if ($gem instanceof Copy) {
271 1
            return clone $this->resolve($gem->config(), $args);
272
        }
273
274 36
        if ($gem instanceof Value) {
275 16
            return $gem->config();
276
        }
277
278 21
        if ($gem instanceof Scoped) {
279 12
            return $this->scoped($gem->closure(), $gem->scoped());
280
        }
281
282 9
        if ($gem instanceof Provide) {
283 3
            return ($this->provider() ?? new Unresolvable)($gem->config(), $this->vars($args, $gem->args()));
284
        }
285
286 6
        if ($gem instanceof Expect) {
287
            try {
288 5
                return $this->resolve($gem->plugin(), $args);
289 3
            } catch(Throwable $exception) {
290 3
                return $this->resolve($gem->exception(), $gem->args($exception, $args));
291
            }
292
        }
293
294 1
        return Unresolvable::plugin($gem);
295
    }
296
297
    /**
298
     * @param Plugin $plugin
299
     * @param object $service
300
     * @return object
301
     * @throws Throwable
302
     */
303 57
    protected function hydrate(Plugin $plugin, $service)
304
    {
305 57
        foreach($plugin->calls() as $method => $args) {
306 16
            if (is_string($method)) {
307 6
                if (INDEX == $method[0]) {
308 2
                    $service[substr($method, 1)] = $this->resolve($args);
309 2
                    continue;
310
                }
311
312 4
                if (PROPERTY == $method[0]) {
313 2
                    $service->{substr($method, 1)} = $this->resolve($args);
314 2
                    continue;
315
                }
316
317 2
                $service->$method($this->resolve($args));
318 2
                continue;
319
            }
320
321 10
            if (is_array($args)) {
322 9
                $method = array_shift($args);
323 9
                $param  = $plugin->param();
324
325 9
                if (is_string($method) && PROPERTY == $method[0]) {
326 3
                    $param  = substr($method, 1);
327 3
                    $method = array_shift($args);
328
                }
329
330 9
                $this->invoke(
331 9
                    is_string($method) ? [$service, $method] : $this->callable($method),
332 9
                    ($param && (!$args || is_string(key($args))) ? [$param => $service] : []) + $this->args($args)
333
                );
334
335 9
                continue;
336
            }
337
338 1
            $this->resolve($args);
339
        }
340
341 57
        return $service;
342
    }
343
344
    /**
345
     * @param Plugin $parent
346
     * @param Plugin $child
347
     * @param string|null $name
348
     * @param array $config
349
     * @return Plugin
350
     * @throws Throwable
351
     */
352 10
    protected function merge(Plugin $parent, Plugin $child, string $name = null, array $config = []) : Plugin
353
    {
354 10
        !$parent->name() &&
355 1
            $config[NAME] = $name ?? $this->resolve($child->name());
356
357 10
        $child->args() &&
358 5
            $config[ARGS] = is_string(key($child->args())) ? $child->args() + $parent->args() : $child->args();
359
360 10
        $child->calls() &&
361 4
            $config[CALLS] = $child->merge() ? array_merge($parent->calls(), $child->calls()) : $child->calls();
362
363 10
        $child->param() &&
364 7
            $config[PARAM] = $child->param();
365
366 10
        return $config ? $parent->with($config) : $parent;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $config ? $parent->with($config) : $parent could return the type Mvc5\Config\Model which includes types incompatible with the type-hinted return Mvc5\Plugin\Gem\Plugin. Consider adding an additional type-check to rule them out.
Loading history...
367
    }
368
369
    /**
370
     * @param array|string $name
371
     * @return mixed
372
     */
373 24
    function param($name)
374
    {
375 24
        if (is_string($name)) {
376 24
            return param($this->config, $name);
377
        }
378
379 4
        $matched = [];
380
381 4
        foreach($name as $key) {
382 4
            $matched[$key] = $this->config[$key] ?? null;
383
        }
384
385 4
        return $matched;
386
    }
387
388
    /**
389
     * @param string $parent
390
     * @return Plugin
391
     * @throws Throwable
392
     */
393 3
    protected function parent(string $parent) : Plugin
394
    {
395 3
        return $this->configured($this->resolve($parent));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->configured($this->resolve($parent)) could return the type null which is incompatible with the type-hinted return Mvc5\Plugin\Gem\Plugin. Consider adding an additional type-check to rule them out.
Loading history...
396
    }
397
398
    /**
399
     * @param string|mixed $plugin
400
     * @param array $args
401
     * @param callable|null $callback
402
     * @param string|null $previous
403
     * @return mixed
404
     * @throws Throwable
405
     */
406 221
    function plugin($plugin, array $args = [], callable $callback = null, string $previous = null)
407
    {
408 221
        if (!$plugin) {
409 2
            return $plugin;
410
        }
411
412 220
        if (is_string($plugin)) {
413 145
            return $this->build(explode(SERVICE_SEPARATOR, $plugin), $args, $callback);
414
        }
415
416 170
        if (is_array($plugin)) {
417 28
            return $this->pluginArray(array_shift($plugin), $args + $this->args($plugin), $callback, $previous);
418
        }
419
420 144
        if ($plugin instanceof Closure) {
421 22
            return $this->invoke($this->scoped($plugin), $args);
422
        }
423
424 129
        return $this->resolve($plugin, $args);
425
    }
426
427
    /**
428
     * @param string|mixed $plugin
429
     * @param array $args
430
     * @param callable|null $callback
431
     * @param string|null $previous
432
     * @return mixed
433
     * @throws Throwable
434
     */
435 28
    protected function pluginArray($plugin, array $args = [], callable $callback = null, string $previous = null)
436
    {
437 28
        return $previous && $previous === $plugin ?
438 28
            $this->callback($plugin, true, $args, $callback) : $this->plugin($plugin, $args, $callback);
439
    }
440
441
    /**
442
     * @param Plugin $plugin
443
     * @param array $args
444
     * @return callable|object|null
445
     * @throws Throwable
446
     */
447 61
    protected function provide(Plugin $plugin, array $args = [])
448
    {
449 61
        $name   = $this->resolve($plugin->name());
450 61
        $parent = $this->configured($name);
451
452 61
        $args && is_string(key($args)) && $plugin->args() && $args += $this->args($plugin->args());
0 ignored issues
show
Bug Best Practice introduced by
The expression $args 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...
453
454 61
        !$args && $args = $this->args($plugin->args());
455
456 61
        if (!$parent) {
457 37
            return $this->hydrate($plugin, $this->combine(explode(SERVICE_SEPARATOR, $name), $args));
0 ignored issues
show
Bug introduced by
It seems like $this->combine(explode(M...PARATOR, $name), $args) can also be of type mixed; however, parameter $service of Mvc5\Resolver\Resolver::hydrate() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

457
            return $this->hydrate($plugin, /** @scrutinizer ignore-type */ $this->combine(explode(SERVICE_SEPARATOR, $name), $args));
Loading history...
458
        }
459
460 35
        if (!$parent instanceof Plugin) {
461 27
            return $this->hydrate(
462 27
                $plugin, $name === $parent ? $this->make($name, $args) : $this->provision($this->resolve($parent), $args)
463
            );
464
        }
465
466 8
        if ($name === $parent->name()) {
467 1
            return $this->hydrate($plugin, $this->make($name, $args));
468
        }
469
470 7
        return $this->provide($this->merge($parent, $plugin, $name), $args);
471
    }
472
473
    /**
474
     * @return callable|null
475
     */
476 71
    protected function provider() : ?callable
477
    {
478 71
        return $this->provider;
479
    }
480
481
    /**
482
     * @param $plugin
483
     * @param array $args
484
     * @return mixed
485
     * @throws \ReflectionException
486
     * @throws Throwable
487
     */
488 26
    protected function provision($plugin, array $args)
489
    {
490 26
        return $plugin instanceof Closure && (new \ReflectionFunction($plugin))->getClosureThis() ?
491 26
            $this->invoke($plugin, $args) : $this->plugin($plugin, $args);
492
    }
493
494
    /**
495
     * @param Resolvable|mixed $plugin
496
     * @param array $args
497
     * @param callable|null $callback
498
     * @param int $c
499
     * @return mixed
500
     * @throws Throwable
501
     */
502 306
    protected function resolvable($plugin, array $args = [], callable $callback = null, int $c = 0)
503
    {
504 306
        return !$plugin instanceof Resolvable ? $plugin : (
505 133
            $c > MAX_RECURSION ? Unresolvable::plugin($plugin) :
506 306
                $this->resolvable($this->solve($plugin, $args, $callback), $args, $callback, ++$c)
507
        );
508
    }
509
510
    /**
511
     * @param Resolvable|mixed $plugin
512
     * @param array $args
513
     * @return mixed
514
     * @throws Throwable
515
     */
516 306
    protected function resolve($plugin, array $args = [])
517
    {
518 306
        return $this->resolvable($plugin, $args);
519
    }
520
521
    /**
522
     * @param string|mixed $plugin
523
     * @param array $args
524
     * @return mixed
525
     * @throws Throwable
526
     */
527 1
    protected function resolver($plugin, array $args = [])
528
    {
529 1
        return $this->call($this->provider() ?? SERVICE_RESOLVER, [$plugin, $args]);
530
    }
531
532
    /**
533
     * @param Closure $callback
534
     * @param bool $scoped
535
     * @return Closure
536
     */
537 33
    protected function scoped(Closure $callback, bool $scoped = false) : Closure
538
    {
539 33
        return $this->scope ? $this->bind($callback, $this->scope === true ? $this : $this->scope, $scoped) : $callback;
540
    }
541
542
    /**
543
     * @param Gem|mixed $plugin
544
     * @param array $args
545
     * @param callable|null $callback
546
     * @return callable|mixed
547
     * @throws Throwable
548
     */
549 133
    protected function solve($plugin, array $args = [], callable $callback = null)
550
    {
551 133
        return $plugin instanceof Gem ? $this->gem($plugin, $args) : (
552 131
            $callback ? $callback($plugin, $args) : $this->resolver($plugin, $args)
553
        );
554
    }
555
556
    /**
557
     * @param array $args
558
     * @return array
559
     */
560 31
    protected function variadic(array $args) : array
561
    {
562 31
        return $args && $args[0] instanceof SignalArgs ? $args[0]->args() : $args;
0 ignored issues
show
Bug Best Practice introduced by
The expression $args 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...
563
    }
564
565
    /**
566
     * @param array $child
567
     * @param array $parent
568
     * @return array
569
     * @throws Throwable
570
     */
571 47
    protected function vars(array $child = [], array $parent = []) : array
572
    {
573 47
        return $this->arguments($child, $this->args($parent));
574
    }
575
576
    /**
577
     * @param mixed $plugin
578
     * @param array $args
579
     * @return mixed
580
     * @throws Throwable
581
     */
582 2
    function __call($plugin, array $args = [])
583
    {
584 2
        return $this->call($plugin, $args);
585
    }
586
587
    /**
588
     *
589
     */
590 12
    function __clone()
591
    {
592 12
        $this->config = clone $this->config;
593 12
        $this->container = clone $this->container;
594 12
        $this->events = clone $this->events;
595 12
        $this->services = clone $this->services;
596
597 12
        is_object($this->scope) &&
598 2
            $this->scope = $this->scope instanceof Scopable ? $this->scope->withScope($this) : clone $this->scope;
599 12
    }
600
601
    /**
602
     * @param mixed $plugin
603
     * @param array $args
604
     * @return mixed
605
     * @throws Throwable
606
     */
607
    function __invoke($plugin, array $args = [])
608
    {
609
        return $this->plugin($plugin, $args, $this->provider() ?? function(){});
610
    }
611
612
    /**
613
     * @return array
614
     */
615 4
    function __serialize() : array
616
    {
617 4
        return [$this->config, $this->events, $this->services, new Container, $this->provider, $this->scope, $this->strict];
618
    }
619
620
    /**
621
     * @param array $data
622
     */
623 4
    function __unserialize(array $data) : void
624
    {
625 4
        list($this->config, $this->events, $this->services, $this->container, $this->provider, $this->scope, $this->strict) = $data;
626 4
    }
627
}
628
629
/**
630
 * @param array|ArrayAccess $config
631
 * @param string $name
632
 * @return mixed
633
 */
634
function param($config, string $name)
635
{
636 24
    $name = explode(CALL_SEPARATOR, $name);
637 24
    $value = $config[array_shift($name)];
638
639 24
    foreach($name as $n) {
640 22
        $value = $value[$n];
641
    }
642
643 24
    return $value;
644
}
645