Passed
Pull Request — master (#93)
by Sergei
04:47 queued 02:36
created

Factory::setDefaultDefinitions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory;
6
7
use Psr\Container\ContainerInterface;
8
use Yiisoft\Factory\Definition\ArrayDefinition;
9
use Yiisoft\Factory\Definition\DefinitionInterface;
10
use Yiisoft\Factory\Definition\Normalizer;
11
use Yiisoft\Factory\Definition\DefinitionValidator;
12
use Yiisoft\Factory\Exception\InvalidConfigException;
13
use Yiisoft\Factory\Exception\NotInstantiableException;
14
15
class Factory implements FactoryInterface
16
{
17
    /**
18
     * @var ContainerInterface|null Parent container.
19
     */
20
    private ?ContainerInterface $container = null;
21
22
    /**
23
     * @var mixed[] Definitions
24
     * @psalm-var array<string, mixed>
25
     */
26
    private array $definitions = [];
27
28
    /**
29
     * @var DefinitionInterface[] object definitions indexed by their types
30
     * @psalm-var array<string, DefinitionInterface>
31
     */
32
    private array $definitionInstances = [];
33
34
    /**
35
     * @var bool $validate Validate definitions when set
36
     */
37
    private bool $validate;
38
39
    /**
40
     * Factory constructor.
41
     *
42
     * @psalm-param array<string, mixed> $definitions
43
     *
44
     * @throws InvalidConfigException
45
     * @throws NotInstantiableException
46
     */
47 38
    public function __construct(
48
        ContainerInterface $container = null,
49
        array $definitions = [],
50
        bool $validate = true
51
    ) {
52 38
        $this->container = $container;
53 38
        $this->validate = $validate;
54 38
        $this->setDefaultDefinitions();
55 38
        $this->setMultiple($definitions);
56 38
    }
57
58 26
    public function create($config)
59
    {
60 26
        if ($this->validate) {
61 26
            DefinitionValidator::validate($config);
62
        }
63
64 25
        $definition = Normalizer::normalize($config);
65
        if (
66 25
            ($definition instanceof ArrayDefinition) &&
67 25
            $this->has($definition->getClass())
68
        ) {
69 4
            $definition = $this->merge(
70 4
                $this->getDefinition($definition->getClass()),
71
                $definition
72
            );
73
        }
74
75 25
        if ($definition instanceof ArrayDefinition) {
76 18
            return $definition->resolve($this->container ?? $this);
77
        }
78
79 7
        return $definition->resolve($this);
80
    }
81
82 4
    private function merge(DefinitionInterface $one, ArrayDefinition $two): DefinitionInterface
83
    {
84 4
        return $one instanceof ArrayDefinition ? $one->merge($two) : $two;
85
    }
86
87
    /**
88
     * @param string $id
89
     *
90
     * @throws NotInstantiableException
91
     *
92
     * @return mixed|object
93
     */
94 17
    public function get($id)
95
    {
96
        try {
97 17
            $definition = $this->getDefinition($id);
98 1
        } catch (InvalidConfigException $e) {
99 1
            throw new NotInstantiableException($id);
100
        }
101
102 16
        if ($definition instanceof ArrayDefinition) {
103 15
            return $definition->resolve($this->container ?? $this);
104
        }
105
106 5
        return $definition->resolve($this);
107
    }
108
109
    /**
110
     * @throws InvalidConfigException
111
     */
112 21
    public function getDefinition(string $id): DefinitionInterface
113
    {
114 21
        if (!isset($this->definitionInstances[$id])) {
115 21
            if (isset($this->definitions[$id])) {
116 14
                $this->definitionInstances[$id] = Normalizer::normalize($this->definitions[$id], $id);
117
            } else {
118
                /** @psalm-var class-string $id */
119 11
                $this->definitionInstances[$id] = ArrayDefinition::fromPreparedData($id);
120
            }
121
        }
122
123 20
        return $this->definitionInstances[$id];
124
    }
125
126
    /**
127
     * Sets a definition to the factory.
128
     *
129
     * @param mixed $definition
130
     *
131
     * @throws InvalidConfigException
132
     */
133 38
    public function set(string $id, $definition): void
134
    {
135 38
        if ($this->validate) {
136 37
            DefinitionValidator::validate($definition, $id);
137
        }
138
139 38
        $this->definitions[$id] = $definition;
140 38
    }
141
142
    /**
143
     * Sets multiple definitions at once.
144
     *
145
     * @param array $definitions definitions indexed by their ids
146
     *
147
     * @psalm-param array<string, mixed> $definitions
148
     *
149
     * @throws InvalidConfigException
150
     */
151 38
    public function setMultiple(array $definitions): void
152
    {
153
        /** @var mixed $definition */
154 38
        foreach ($definitions as $id => $definition) {
155 38
            $this->set($id, $definition);
156
        }
157 38
    }
158
159
    /**
160
     * Returns a value indicating whether the container has the definition of the specified name.
161
     *
162
     * @param string $id class name, interface name or alias name
163
     *
164
     * @return bool whether the container is able to provide instance of class specified.
165
     *
166
     * @see set()
167
     */
168 18
    public function has($id): bool
169
    {
170 18
        return isset($this->definitions[$id]);
171
    }
172
173 38
    private function setDefaultDefinitions(): void
174
    {
175
        /** @var ContainerInterface */
176 38
        $container = $this->container ?? $this;
177
178 38
        $this->setMultiple([
179 38
            ContainerInterface::class => $container,
180
        ]);
181 38
    }
182
}
183