Test Failed
Pull Request — master (#143)
by Sergei
13:24 queued 11:30
created

DependencyResolver::has()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 3
c 1
b 0
f 1
nc 2
nop 1
dl 0
loc 6
ccs 0
cts 4
cp 0
crap 6
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
A DependencyResolver::setFactoryDefinition() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory;
6
7
use Psr\Container\ContainerExceptionInterface;
8
use Psr\Container\ContainerInterface;
9
use Psr\Container\NotFoundExceptionInterface;
10
use Yiisoft\Definitions\ArrayDefinition;
11
use Yiisoft\Definitions\Contract\DefinitionInterface;
12
use Yiisoft\Definitions\Contract\DependencyResolverInterface;
13
use Yiisoft\Definitions\Contract\ReferenceInterface;
14
use Yiisoft\Definitions\Exception\CircularReferenceException;
15
use Yiisoft\Definitions\Exception\InvalidConfigException;
16
use Yiisoft\Definitions\Exception\NotFoundException;
17
use Yiisoft\Definitions\Exception\NotInstantiableClassException;
18
use Yiisoft\Definitions\Exception\NotInstantiableException;
19
use Yiisoft\Definitions\Infrastructure\Normalizer;
20
21
use function is_object;
22
use function is_string;
23
24
/**
25
 * @internal
26
 */
27
final class DependencyResolver implements DependencyResolverInterface
28
{
29
    private ?ContainerInterface $container;
30
31
    /**
32
     * @var mixed[] Definitions
33
     * @psalm-var array<string, mixed>
34
     */
35
    private array $definitions = [];
36
37
    /**
38
     * @var DefinitionInterface[] object definitions indexed by their types
39
     * @psalm-var array<string, DefinitionInterface>
40
     */
41
    private array $definitionInstances = [];
42
43
    /**
44
     * @var array used to collect IDs instantiated during build to detect circular references
45
     *
46
     * @psalm-var array<string,1>
47
     */
48
    private array $creatingIds = [];
49
50
    public function __construct(?ContainerInterface $container)
51
    {
52 84
        $this->container = $container;
53
    }
54 84
55 84
    /**
56
     * @throws NotFoundExceptionInterface
57
     * @throws ContainerExceptionInterface
58
     *
59
     * @return mixed|object
60
     *
61
     * @psalm-suppress InvalidThrow
62
     */
63
    public function resolve(string $id)
64
    {
65
        if (isset($this->definitions[$id])) {
66
            return $this->getFromFactory($id);
67 18
        }
68
69 18
        if ($this->container !== null && $this->container->has($id)) {
70 10
            return $this->container->get($id);
71
        }
72
73 9
        if (class_exists($id)) {
74 2
            return $this->getFromFactory($id);
75
        }
76
77 7
        throw new NotInstantiableClassException($id);
78 5
    }
79
80
    public function resolveReference(string $id)
81 2
    {
82
        return $this->getFromFactory($id);
83
    }
84
85
    /**
86
     * @param mixed $definition
87
     */
88
    public function setFactoryDefinition(string $id, $definition): void
89
    {
90
        $this->definitions[$id] = $definition;
91
    }
92
93
    /**
94
     * @param mixed $config
95 23
     *
96
     * @throws CircularReferenceException
97 23
     * @throws NotFoundException
98
     * @throws NotInstantiableException
99
     * @throws InvalidConfigException
100 9
     *
101
     * @return mixed
102 9
     */
103
    public function create($config)
104
    {
105
        if (is_string($config)) {
106
            if ($this->canBeCreatedByFactory($config)) {
107
                return $this->getFromFactory($config);
108 49
            }
109
            throw new NotFoundException($config);
110 49
        }
111 49
112
        $definition = $this->createDefinition($config);
113
114
        if ($definition instanceof ArrayDefinition) {
115
            $this->creatingIds[$definition->getClass()] = 1;
116
        }
117
        try {
118
            return $definition->resolve($this);
119
        } finally {
120
            if ($definition instanceof ArrayDefinition) {
121
                unset($this->creatingIds[$definition->getClass()]);
122
            }
123 77
        }
124
    }
125 77
126 60
    /**
127 57
     * @param mixed $config
128
     *
129 3
     * @throws InvalidConfigException
130
     */
131
    private function createDefinition($config): DefinitionInterface
132 19
    {
133
        $definition = Normalizer::normalize($config);
134 18
135 16
        if (
136
            ($definition instanceof ArrayDefinition) &&
137
            isset($this->definitions[$definition->getClass()])
138 18
        ) {
139
            $definition = $this->mergeDefinitions(
140 18
                $this->getDefinition($definition->getClass()),
141 18
                $definition
142
            );
143
        }
144
145
        return $definition;
146
    }
147
148
    /**
149
     * @param string $id
150
     *
151 19
     * @throws CircularReferenceException
152
     * @throws InvalidConfigException
153 19
     * @throws NotFoundException
154
     * @throws NotInstantiableException
155
     *
156 18
     * @return mixed|object
157 18
     */
158
    private function getFromFactory(string $id)
159 4
    {
160 4
        if (isset($this->creatingIds[$id])) {
161
            throw new CircularReferenceException(sprintf(
162
                'Circular reference to "%s" detected while creating: %s.',
163
                $id,
164
                implode(',', array_keys($this->creatingIds))
165 18
            ));
166
        }
167
168
        $this->creatingIds[$id] = 1;
169
        try {
170
            return $this->getDefinition($id)->resolve($this);
171
        } finally {
172
            unset($this->creatingIds[$id]);
173
        }
174
    }
175
176
    /**
177
     * @throws InvalidConfigException
178 58
     */
179
    private function getDefinition(string $id): DefinitionInterface
180 58
    {
181 5
        if (!isset($this->definitionInstances[$id])) {
182 5
            if (isset($this->definitions[$id])) {
183
                if (is_object($this->definitions[$id]) && !($this->definitions[$id] instanceof ReferenceInterface)) {
184 5
                    return Normalizer::normalize(clone $this->definitions[$id], $id);
185
                }
186
                $this->definitionInstances[$id] = Normalizer::normalize($this->definitions[$id], $id);
187
            } else {
188 58
                /** @psalm-var class-string $id */
189
                $this->definitionInstances[$id] = ArrayDefinition::fromPreparedData($id);
190 58
            }
191
        }
192 58
193
        return $this->definitionInstances[$id];
194
    }
195
196
    private function canBeCreatedByFactory(string $id): bool
197
    {
198
        return isset($this->definitions[$id]) || class_exists($id);
199 62
    }
200
201 62
    private function mergeDefinitions(DefinitionInterface $one, ArrayDefinition $two): DefinitionInterface
202 62
    {
203 49
        return $one instanceof ArrayDefinition ? $one->merge($two) : $two;
204 7
    }
205
}
206