Test Failed
Pull Request — master (#93)
by Alexander
04:33 queued 02:13
created

Factory::setDefaultDefinitions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 7
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
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 32
    public function __construct(
48
        ContainerInterface $container = null,
49
        array $definitions = [],
50
        bool $validate = true
51
    ) {
52 32
        $this->container = $container;
53 32
        $this->validate = $validate;
54 32
        $this->setDefaultDefinitions();
55 32
        $this->setMultiple($definitions);
56
    }
57 22
58
    public function create($config)
59 22
    {
60 22
        if ($this->validate) {
61
            DefinitionValidator::validate($config);
62
        }
63 22
64 22
        $definition = Normalizer::normalize($config);
65 16
        if (
66 9
            ($definition instanceof ArrayDefinition) &&
67
            $this->has($definition->getClass())
68 16
        ) {
69 4
            $definition = $this->merge(
70 4
                $this->getDefinition($definition->getClass()),
71
                $definition
72
            );
73
        }
74
75
        if ($definition instanceof ArrayDefinition) {
76 22
            return $definition->resolve($this->container ?? $this);
77 16
        }
78
79
        return $definition->resolve($this);
80 6
    }
81
82
    private function merge(DefinitionInterface $one, ArrayDefinition $two): DefinitionInterface
83 4
    {
84
        return $one instanceof ArrayDefinition ? $one->merge($two) : $two;
85 4
    }
86
87
    /**
88
     * @param string $id
89
     *
90
     * @throws NotInstantiableException
91
     *
92
     * @return mixed|object
93
     */
94
    public function get($id)
95 16
    {
96
        try {
97
            $definition = $this->getDefinition($id);
98 16
        } catch (InvalidConfigException $e) {
99
            throw new NotInstantiableException($id);
100
        }
101
102
        if ($definition instanceof ArrayDefinition) {
103 16
            return $definition->resolve($this->container ?? $this);
104 15
        }
105
106
        return $definition->resolve($this);
107 5
    }
108
109
    /**
110
     * @throws InvalidConfigException
111
     */
112
    public function getDefinition(string $id): DefinitionInterface
113 20
    {
114
        if (!isset($this->definitionInstances[$id])) {
115 20
            if (isset($this->definitions[$id])) {
116 20
                $this->definitionInstances[$id] = Normalizer::normalize($this->definitions[$id], $id);
117 13
            } else {
118
                /** @psalm-var class-string $id */
119
                $this->definitionInstances[$id] = ArrayDefinition::fromPreparedData($id);
120 11
            }
121
        }
122
123
        return $this->definitionInstances[$id];
124 20
    }
125
126
    /**
127
     * Sets a definition to the factory.
128
     *
129
     * @param mixed $definition
130
     *
131
     * @throws InvalidConfigException
132
     */
133
    public function set(string $id, $definition): void
134 14
    {
135
        if ($this->validate) {
136 14
            DefinitionValidator::validate($definition, $id);
137 14
        }
138
139
        $this->definitions[$id] = $definition;
140 14
    }
141 14
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
    public function setMultiple(array $definitions): void
152 32
    {
153
        /** @var mixed $definition */
154
        foreach ($definitions as $id => $definition) {
155 32
            $this->set($id, $definition);
156 13
        }
157
    }
158 32
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
    public function has($id): bool
169 16
    {
170
        return isset($this->definitions[$id]);
171 16
    }
172
173
    private function setDefaultDefinitions(): void
174
    {
175
        /** @var ContainerInterface */
176
        $container = $this->container ?? $this;
177
178
        $this->setMultiple([
179
            ContainerInterface::class => $container,
180
        ]);
181
    }
182
}
183