Passed
Pull Request — master (#85)
by Sergei
02:17
created

Factory::getDefinition()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 6
c 2
b 0
f 0
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 10
cc 3
nc 3
nop 1
crap 3
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 27
    public function __construct(
48
        ContainerInterface $container = null,
49
        array $definitions = [],
50
        bool $validate = true
51
    ) {
52 27
        $this->container = $container;
53 27
        $this->validate = $validate;
54 27
        $this->setMultiple($definitions);
55 27
    }
56
57 17
    public function create($config, array $constructorArguments = [])
58
    {
59 17
        if ($this->validate) {
60 17
            DefinitionValidator::validate($config);
61
        }
62
63 17
        $definition = Normalizer::normalize($config);
64 17
        if ($definition instanceof ArrayDefinition) {
65 11
            if (!empty($constructorArguments)) {
66 5
                $definition->setConstructorArguments($constructorArguments);
67
            }
68 11
            if ($this->has($definition->getClass())) {
69 2
                $definition = $this->merge(
70 2
                    $this->getDefinition($definition->getClass()),
71
                    $definition
72
                );
73
            }
74
        }
75
76 17
        if ($definition instanceof ArrayDefinition) {
77 11
            return $definition->resolve($this->container ?? $this);
78
        }
79
80 6
        return $definition->resolve($this);
81
    }
82
83 2
    private function merge(DefinitionInterface $one, ArrayDefinition $two): DefinitionInterface
84
    {
85 2
        return $one instanceof ArrayDefinition ? $one->merge($two) : $two;
86
    }
87
88
    /**
89
     * @param string $id
90
     *
91
     * @throws NotInstantiableException
92
     *
93
     * @return mixed|object
94
     */
95 16
    public function get($id)
96
    {
97
        try {
98 16
            $definition = $this->getDefinition($id);
99
        } catch (InvalidConfigException $e) {
100
            throw new NotInstantiableException($id);
101
        }
102
103 16
        if ($definition instanceof ArrayDefinition) {
104 15
            return $definition->resolve($this->container ?? $this);
105
        }
106
107 5
        return $definition->resolve($this);
108
    }
109
110
    /**
111
     * @throws InvalidConfigException
112
     */
113 18
    public function getDefinition(string $id): DefinitionInterface
114
    {
115 18
        if (!isset($this->definitionInstances[$id])) {
116 18
            if (isset($this->definitions[$id])) {
117 11
                $this->definitionInstances[$id] = Normalizer::normalize($this->definitions[$id], $id);
118
            } else {
119
                /** @psalm-var class-string $id */
120 11
                $this->definitionInstances[$id] = ArrayDefinition::fromPreparedData($id);
121
            }
122
        }
123
124 18
        return $this->definitionInstances[$id];
125
    }
126
127
    /**
128
     * Sets a definition to the factory.
129
     *
130
     * @param mixed $definition
131
     *
132
     * @throws InvalidConfigException
133
     */
134 12
    public function set(string $id, $definition): void
135
    {
136 12
        if ($this->validate) {
137 12
            DefinitionValidator::validate($definition);
138
        }
139
140 12
        $this->definitions[$id] = $definition;
141 12
    }
142
143
    /**
144
     * Sets multiple definitions at once.
145
     *
146
     * @param array $definitions definitions indexed by their ids
147
     *
148
     * @psalm-param array<string, mixed> $definitions
149
     *
150
     * @throws InvalidConfigException
151
     */
152 27
    public function setMultiple(array $definitions): void
153
    {
154
        /** @var mixed $definition */
155 27
        foreach ($definitions as $id => $definition) {
156 11
            $this->set($id, $definition);
157
        }
158 27
    }
159
160
    /**
161
     * Returns a value indicating whether the container has the definition of the specified name.
162
     *
163
     * @param string $id class name, interface name or alias name
164
     *
165
     * @return bool whether the container is able to provide instance of class specified.
166
     *
167
     * @see set()
168
     */
169 11
    public function has($id): bool
170
    {
171 11
        return isset($this->definitions[$id]);
172
    }
173
}
174