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

Factory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 3
c 2
b 0
f 0
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 3
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 29
    public function __construct(
48
        ContainerInterface $container = null,
49
        array $definitions = [],
50
        bool $validate = true
51
    ) {
52 29
        $this->container = $container;
53 29
        $this->validate = $validate;
54 29
        $this->setMultiple($definitions);
55 29
    }
56
57 19
    public function create($config, array $constructorArguments = [])
58
    {
59 19
        if ($this->validate) {
60 19
            DefinitionValidator::validate($config);
61
        }
62
63 19
        $definition = Normalizer::normalize($config);
64 19
        if ($definition instanceof ArrayDefinition) {
65 13
            if (!empty($constructorArguments)) {
66 7
                $definition->mergeConstructorArguments($constructorArguments);
67
            }
68 13
            if ($this->has($definition->getClass())) {
69 4
                $definition = $this->merge(
70 4
                    $this->getDefinition($definition->getClass()),
71
                    $definition
72
                );
73
            }
74
        }
75
76 19
        if ($definition instanceof ArrayDefinition) {
77 13
            return $definition->resolve($this->container ?? $this);
78
        }
79
80 6
        return $definition->resolve($this);
81
    }
82
83 4
    private function merge(DefinitionInterface $one, ArrayDefinition $two): DefinitionInterface
84
    {
85 4
        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 20
    public function getDefinition(string $id): DefinitionInterface
114
    {
115 20
        if (!isset($this->definitionInstances[$id])) {
116 20
            if (isset($this->definitions[$id])) {
117 13
                $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 20
        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 14
    public function set(string $id, $definition): void
135
    {
136 14
        if ($this->validate) {
137 14
            DefinitionValidator::validate($definition, $id);
138
        }
139
140 14
        $this->definitions[$id] = $definition;
141 14
    }
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 29
    public function setMultiple(array $definitions): void
153
    {
154
        /** @var mixed $definition */
155 29
        foreach ($definitions as $id => $definition) {
156 13
            $this->set($id, $definition);
157
        }
158 29
    }
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 13
    public function has($id): bool
170
    {
171 13
        return isset($this->definitions[$id]);
172
    }
173
}
174