Passed
Push — sam-rework ( 16881f...5822c3 )
by Alexander
11:19
created

ArrayDefinition::getDependencies()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
namespace yii\di\definitions;
3
4
use Psr\Container\ContainerInterface;
5
use yii\di\contracts\Definition;
6
use yii\di\exceptions\InvalidConfigException;
7
use yii\di\exceptions\NotInstantiableException;
8
use yii\di\resolvers\ClassNameResolver;
9
10
/**
11
 * Builds object by array config.
12
 * @package yii\di
13
 */
14
class ArrayDefinition implements Definition
15
{
16
    private $config;
17
18
    private static $dependencies = [];
19
20
    private const CLASS_KEY = '__class';
21
    private const CONSTRUCT_KEY = '__construct()';
22
23
    /**
24
     * Injector constructor.
25
     * @param array $config
26
     */
27
    public function __construct(array $config)
28
    {
29
        $this->config = $config;
30
    }
31
32
    public function getArray(): array
33
    {
34
        return $this->config;
35
    }
36
37
    /**
38
     * @param string $class
39
     * @return self
40
     */
41
    public static function fromClassName(string $class): self
42
    {
43
        return new static([self::CLASS_KEY => $class]);
44
    }
45
46
    public function resolve(ContainerInterface $container, array $params = [])
47
    {
48
        $config = $this->config;
49
50
        if (empty($config[self::CLASS_KEY])) {
51
            throw new NotInstantiableException(var_export($config, true));
52
        }
53
54
        if (!empty($params)) {
55
            $config[self::CONSTRUCT_KEY] = array_merge($config[self::CONSTRUCT_KEY] ?? [], $params);
56
        }
57
58
        return $this->buildFromArray($container, $config);
59
    }
60
61
    private function buildFromArray(ContainerInterface $container, array $config)
62
    {
63
        if (empty($config[self::CLASS_KEY])) {
64
            throw new NotInstantiableException(var_export($config, true));
65
        }
66
        $class = $config[self::CLASS_KEY];
67
        unset($config[self::CLASS_KEY]);
68
69
        $dependencies = $this->getDependencies($class);
70
71
        if (isset($config[self::CONSTRUCT_KEY])) {
72
            foreach (array_values($config[self::CONSTRUCT_KEY]) as $index => $param) {
73
                if ($param instanceof Definition) {
74
                    $dependencies[$index] = $param;
75
                } else {
76
                    $dependencies[$index] = new ValueDefinition($param);
77
                }
78
            }
79
            unset($config[self::CONSTRUCT_KEY]);
80
        }
81
82
        $resolved = $this->resolveDependencies($container, $dependencies);
83
        $object = new $class(...$resolved);
84
        return $this->configure($container, $object, $config);
85
    }
86
87
    /**
88
     * Resolves dependencies by replacing them with the actual object instances.
89
     * @param Definition[] $dependencies the dependencies
90
     * @return array the resolved dependencies
91
     * @throws InvalidConfigException if a dependency cannot be resolved or if a dependency cannot be fulfilled.
92
     */
93
    private function resolveDependencies(ContainerInterface $container, array $dependencies): array
94
    {
95
        $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...
96
        $result = [];
97
        /** @var Definition $dependency */
98
        foreach ($dependencies as $dependency) {
99
            $result[] = $this->resolveDependency($container, $dependency);
100
        }
101
102
        return $result;
103
    }
104
105
    /**
106
     * This function resolves a dependency recursively, checking for loops.
107
     * TODO add checking for loops
108
     * @param Definition $dependency
109
     * @return mixed
110
     */
111
    private function resolveDependency(ContainerInterface $container, Definition $dependency)
112
    {
113
        while ($dependency instanceof Definition) {
114
            $dependency = $dependency->resolve($container);
115
        }
116
        return $dependency;
117
    }
118
119
    /**
120
     * Returns the dependencies of the specified class.
121
     * @param string $class class name, interface name or alias name
122
     * @return Definition[] the dependencies of the specified class.
123
     * @throws InvalidConfigException
124
     * @throws NotInstantiableException
125
     * @internal
126
     */
127
    private function getDependencies(string $class): array
128
    {
129
        if (!isset($this->dependencies[$class])) {
130
            // For now use hard coded resolver.
131
            $resolver = new ClassNameResolver();
132
133
            self::$dependencies[$class] = $resolver->resolveConstructor($class);
134
        }
135
136
        return self::$dependencies[$class];
137
    }
138
139
    /**
140
     * Configures an object with the given configuration.
141
     * @param ContainerInterface $container
142
     * @param object $object the object to be configured
143
     * @param iterable $config property values and methods to call
144
     * @return object the object itself
145
     */
146
    private function configure(ContainerInterface $container, $object, iterable $config)
147
    {
148
        foreach ($config as $action => $arguments) {
149
            if (substr($action, -2) === '()') {
150
                // method call
151
                \call_user_func_array([$object, substr($action, 0, -2)], $arguments);
152
            } else {
153
                // property
154
                if ($arguments instanceof Definition) {
155
                    $arguments = $arguments->resolve($container);
156
                }
157
                $object->$action = $arguments;
158
            }
159
        }
160
161
        return $object;
162
    }
163
}
164