Passed
Pull Request — master (#100)
by Sergei
02:04
created

FactoryContainer::setFactoryDefinition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 1
c 1
b 0
f 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 2
crap 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\Factory\Definition\ArrayDefinition;
11
use Yiisoft\Factory\Definition\DefinitionInterface;
12
use Yiisoft\Factory\Definition\Normalizer;
13
use Yiisoft\Factory\Exception\InvalidConfigException;
14
use Yiisoft\Factory\Exception\NotFoundException;
15
use Yiisoft\Factory\Exception\NotInstantiableException;
16
17
use function is_object;
18
use function is_string;
19
20
/**
21
 * @internal
22
 */
23
final class FactoryContainer implements ContainerInterface
24
{
25
    private Factory $factory;
26
    private ?ContainerInterface $container;
27
28
    /**
29
     * @var mixed[] Definitions
30
     * @psalm-var array<string, mixed>
31
     */
32
    private array $definitions = [];
33
34
    /**
35
     * @var DefinitionInterface[] object definitions indexed by their types
36
     * @psalm-var array<string, DefinitionInterface>
37
     */
38
    private array $definitionInstances = [];
39
40 73
    public function __construct(Factory $factory, ?ContainerInterface $container)
41
    {
42 73
        $this->factory = $factory;
43 73
        $this->container = $container;
44 73
    }
45
46
    /**
47
     * @param string $id
48
     *
49
     * @throws NotFoundExceptionInterface
50
     * @throws ContainerExceptionInterface
51
     *
52
     * @return mixed|object
53
     *
54
     * @psalm-suppress InvalidThrow
55
     */
56 18
    public function get($id)
57
    {
58 18
        if ($this->container !== null) {
59 9
            return $this->container->get($id);
60
        }
61 9
        return $this->getFromFactory($id);
62
    }
63
64
    /**
65
     * @param string $id
66
     */
67 2
    public function has($id): bool
68
    {
69 2
        if ($this->container !== null) {
70 1
            return $this->container->has($id);
71
        }
72 1
        return $this->canBeCreatedByFactory($id);
73
    }
74
75
    /**
76
     * @param string $id
77
     *
78
     * @throws InvalidConfigException
79
     * @throws NotFoundException
80
     * @throws NotInstantiableException
81
     *
82
     * @return mixed|object
83
     */
84 4
    public function getFromFactoryIfHasDefinition($id)
85
    {
86 4
        if (isset($this->definitions[$id])) {
87 2
            return $this->getFromFactory($id);
88
        }
89 3
        return $this->get($id);
90
    }
91
92
    /**
93
     * @param mixed $definition
94
     */
95 73
    public function setFactoryDefinition(string $id, $definition): void
96
    {
97 73
        $this->definitions[$id] = $definition;
98 73
    }
99
100 47
    public function isUsedFactory(): bool
101
    {
102 47
        return $this->container === null;
103
    }
104
105
    /**
106
     * @param mixed $config
107
     *
108
     * @throws InvalidConfigException
109
     */
110 36
    public function createDefinition($config): DefinitionInterface
111
    {
112 36
        if (is_string($config) && isset($this->definitions[$config])) {
113 12
            return Normalizer::normalize(
114 12
                is_object($this->definitions[$config])
115 3
                    ? clone $this->definitions[$config]
116 12
                    : $this->definitions[$config]
117
            );
118
        }
119
120 24
        $definition = Normalizer::normalize($config);
121
122
        if (
123 23
            ($definition instanceof ArrayDefinition) &&
124 23
            isset($this->definitions[$definition->getClass()])
125
        ) {
126 4
            $definition = $this->mergeDefinitions(
127 4
                $this->getDefinition($definition->getClass()),
128
                $definition
129
            );
130
        }
131
132 23
        return $definition;
133
    }
134
135
    /**
136
     * @param string $id
137
     *
138
     * @throws InvalidConfigException
139
     * @throws NotFoundException
140
     * @throws NotInstantiableException
141
     *
142
     * @return mixed|object
143
     */
144 10
    private function getFromFactory($id)
145
    {
146 10
        return $this->getDefinition($id)->resolve($this);
147
    }
148
149
    /**
150
     * @throws InvalidConfigException
151
     */
152 14
    private function getDefinition(string $id): DefinitionInterface
153
    {
154 14
        if (!isset($this->definitionInstances[$id])) {
155 14
            if (isset($this->definitions[$id])) {
156 8
                $this->definitionInstances[$id] = Normalizer::normalize($this->definitions[$id], $id);
157
            } else {
158
                /** @psalm-var class-string $id */
159 9
                $this->definitionInstances[$id] = ArrayDefinition::fromPreparedData($id);
160
            }
161
        }
162
163 14
        return $this->definitionInstances[$id];
164
    }
165
166 1
    private function canBeCreatedByFactory($id): bool
167
    {
168 1
        return isset($this->definitions[$id]) || class_exists($id);
169
    }
170
171 4
    private function mergeDefinitions(DefinitionInterface $one, ArrayDefinition $two): DefinitionInterface
172
    {
173 4
        return $one instanceof ArrayDefinition ? $one->merge($two) : $two;
174
    }
175
}
176