Completed
Push — master ( 8ab030...ee21ce )
by Andrii
14:10
created

ArrayBuilder::resolveDependencies()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace yii\di\definitions;
4
5
use Psr\Container\ContainerInterface;
6
use yii\di\contracts\Definition;
7
use yii\di\exceptions\NotInstantiableException;
8
use yii\di\resolvers\ClassNameResolver;
9
10
/**
11
 * Builds object by ArrayDefinition.
12
 */
13
class ArrayBuilder
14
{
15
    private static $dependencies = [];
16
17
    public function build(ContainerInterface $container, ArrayDefinition $def)
18
    {
19
        $class = $def->getClass();
20
        $dependencies = $this->getDependencies($class);
21
22
        if (!empty($def->getParams())) {
23
            foreach (array_values($def->getParams()) as $index => $param) {
24
                if ($param instanceof Definition) {
25
                    $dependencies[$index] = $param;
26
                } else {
27
                    $dependencies[$index] = new ValueDefinition($param);
28
                }
29
            }
30
        }
31
32
        $resolved = $this->resolveDependencies($container, $dependencies);
33
        $object = new $class(...$resolved);
34
        return $this->configure($container, $object, $def->getConfig());
35
    }
36
37
    /**
38
     * Resolves dependencies by replacing them with the actual object instances.
39
     * @param ContainerInterface $container
40
     * @param Definition[] $dependencies the dependencies
41
     * @return array the resolved dependencies
42
     */
43
    private function resolveDependencies(ContainerInterface $container, array $dependencies): array
44
    {
45
        $container = $container->parentContainer ?? $container;
0 ignored issues
show
Bug introduced by
Accessing parentContainer on the interface Psr\Container\ContainerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
46
        $result = [];
47
        /** @var Definition $dependency */
48
        foreach ($dependencies as $dependency) {
49
            $result[] = $this->resolveDependency($container, $dependency);
50
        }
51
52
        return $result;
53
    }
54
55
    /**
56
     * This function resolves a dependency recursively, checking for loops.
57
     * TODO add checking for loops
58
     * @param ContainerInterface $container
59
     * @param Definition $dependency
60
     * @return mixed
61
     */
62
    private function resolveDependency(ContainerInterface $container, Definition $dependency)
63
    {
64
        while ($dependency instanceof Definition) {
65
            $dependency = $dependency->resolve($container);
66
        }
67
        return $dependency;
68
    }
69
70
    /**
71
     * Returns the dependencies of the specified class.
72
     * @param string $class class name, interface name or alias name
73
     * @return Definition[] the dependencies of the specified class.
74
     * @throws NotInstantiableException
75
     * @internal
76
     */
77
    private function getDependencies(string $class): array
78
    {
79
        if (!isset(self::$dependencies[$class])) {
80
            self::$dependencies[$class] = $this->getResolver()->resolveConstructor($class);
81
        }
82
83
        return self::$dependencies[$class];
84
    }
85
86
    private static $resolver;
87
88
    private function getResolver()
89
    {
90
        if (static::$resolver === null) {
0 ignored issues
show
Bug introduced by
Since $resolver is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $resolver to at least protected.
Loading history...
91
            // For now use hard coded resolver.
92
            static::$resolver = new ClassNameResolver();
93
        }
94
95
        return static::$resolver;
96
    }
97
98
    /**
99
     * Configures an object with the given configuration.
100
     * @param ContainerInterface $container
101
     * @param object $object the object to be configured
102
     * @param iterable $config property values and methods to call
103
     * @return object the object itself
104
     */
105
    private function configure(ContainerInterface $container, $object, iterable $config)
106
    {
107
        foreach ($config as $action => $arguments) {
108
            if (substr($action, -2) === '()') {
109
                // method call
110
                \call_user_func_array([$object, substr($action, 0, -2)], $arguments);
111
            } else {
112
                // property
113
                if ($arguments instanceof Definition) {
114
                    $arguments = $arguments->resolve($container);
115
                }
116
                $object->$action = $arguments;
117
            }
118
        }
119
120
        return $object;
121
    }
122
}
123