Passed
Pull Request — master (#142)
by Sergei
06:52
created

DependencyResolver::has()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory\ServiceLocator;
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
use Yiisoft\Injector\Injector;
21
22
use function is_object;
23
24
final class DependencyResolver implements DependencyResolverInterface
25
{
26
    private ?ContainerInterface $container;
27
    private ?Injector $injector = null;
28
29
    /**
30
     * @var mixed[] Definitions
31
     * @psalm-var array<string, mixed>
32
     */
33
    private array $definitions = [];
34
35
    /**
36
     * @var DefinitionInterface[] object definitions indexed by their types
37
     * @psalm-var array<string, DefinitionInterface>
38
     */
39
    private array $definitionInstances = [];
40
41
    /**
42
     * @var array used to collect IDs instantiated during build to detect circular references
43
     *
44
     * @psalm-var array<string,1>
45
     */
46
    private array $creatingIds = [];
47
48
    public function __construct(?ContainerInterface $container)
49
    {
50
        $this->container = $container;
51
    }
52
53
    /**
54
     * @param string $id
55
     *
56
     * @throws CircularReferenceException
57
     * @throws InvalidConfigException
58
     * @throws NotFoundException
59
     * @throws NotInstantiableException
60
     *
61
     * @return mixed|object
62
     */
63
    public function getFromServiceLocator(string $id)
64
    {
65
        if (isset($this->creatingIds[$id])) {
66
            throw new CircularReferenceException(sprintf(
67
                'Circular reference to "%s" detected while creating: %s.',
68
                $id,
69
                implode(',', array_keys($this->creatingIds))
70
            ));
71
        }
72
73
        $this->creatingIds[$id] = 1;
74
        try {
75
            return $this->getDefinition($id)->resolve($this);
76
        } finally {
77
            unset($this->creatingIds[$id]);
78
        }
79
    }
80
81
    /**
82
     * @param string $id
83
     *
84
     * @throws NotFoundExceptionInterface
85
     * @throws ContainerExceptionInterface
86
     *
87
     * @return mixed|object
88
     *
89
     * @psalm-suppress InvalidThrow
90
     */
91
    public function get($id)
92
    {
93
        if ($this->container !== null && $this->container->has($id)) {
94
            return $this->container->get($id);
95
        }
96
97
        throw new NotInstantiableClassException($id);
98
    }
99
100
    /**
101
     * @param string $id
102
     */
103
    public function has($id): bool
104
    {
105
        return isset($this->definitions[$id]);
106
    }
107
108
    public function resolveReference(string $id)
109
    {
110
        return $this->getFromServiceLocator($id);
111
    }
112
113
    public function invoke(callable $callable)
114
    {
115
        return $this->getInjector()->invoke($callable);
116
    }
117
118
    /**
119
     * @param mixed $definition
120
     */
121
    public function set(string $id, $definition): void
122
    {
123
        $this->definitions[$id] = $definition;
124
    }
125
126
    /**
127
     * @throws InvalidConfigException
128
     */
129
    private function getDefinition(string $id): DefinitionInterface
130
    {
131
        if (!isset($this->definitionInstances[$id])) {
132
            if (isset($this->definitions[$id])) {
133
                if (is_object($this->definitions[$id]) && !($this->definitions[$id] instanceof ReferenceInterface)) {
134
                    return Normalizer::normalize(clone $this->definitions[$id], $id);
135
                }
136
                $this->definitionInstances[$id] = Normalizer::normalize($this->definitions[$id], $id);
137
            } else {
138
                /** @psalm-var class-string $id */
139
                $this->definitionInstances[$id] = ArrayDefinition::fromPreparedData($id);
140
            }
141
        }
142
143
        return $this->definitionInstances[$id];
144
    }
145
146
    private function getInjector(): Injector
147
    {
148
        return $this->injector ??= new Injector($this);
149
    }
150
}
151