Passed
Push — master ( d1af07...6b728f )
by Alexander
11:52
created

Factory::merge()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory;
6
7
use Psr\Container\ContainerInterface;
8
use Yiisoft\Factory\Definitions\ArrayDefinition;
9
use Yiisoft\Factory\Definitions\DefinitionInterface;
10
use Yiisoft\Factory\Definitions\Normalizer;
11
use Yiisoft\Factory\Exceptions\InvalidConfigException;
12
use Yiisoft\Factory\Exceptions\NotInstantiableException;
13
14
use function is_string;
15
16
class Factory implements FactoryInterface
17
{
18
    /**
19
     * @var ContainerInterface|null Parent container.
20
     */
21
    private ?ContainerInterface $container = null;
22
23
    /**
24
     * @var DefinitionInterface[] object definitions indexed by their types
25
     * @psalm-var array<string, DefinitionInterface>
26
     */
27
    private array $definitions = [];
28
29
    /**
30
     * Factory constructor.
31
     *
32
     * @psalm-param array<string, mixed> $definitions
33
     *
34
     * @throws InvalidConfigException
35
     * @throws NotInstantiableException
36
     */
37 44
    public function __construct(ContainerInterface $container = null, array $definitions = [])
38
    {
39 44
        $this->container = $container;
40 44
        $this->setMultiple($definitions);
41 44
    }
42
43 33
    public function create($config, array $params = [])
44
    {
45 33
        $definition = Normalizer::normalize($config, null, $params);
46 33
        if ($definition instanceof ArrayDefinition && $this->has($definition->getClass())) {
47 4
            $definition = $this->merge($this->getDefinition($definition->getClass()), $definition);
48
        }
49
50 33
        if ($definition instanceof ArrayDefinition) {
51 14
            return $definition->resolve($this->container ?? $this);
52
        }
53
54 19
        return $definition->resolve($this);
55
    }
56
57 4
    private function merge(DefinitionInterface $one, ArrayDefinition $two): DefinitionInterface
58
    {
59 4
        return $one instanceof ArrayDefinition ? $one->merge($two) : $two;
60
    }
61
62
    /**
63
     * @param mixed $id
64
     *
65
     * @return mixed|object
66
     */
67 32
    public function get($id)
68
    {
69 32
        $definition = $this->getDefinition($id);
70 32
        if ($definition instanceof ArrayDefinition) {
71 30
            return $definition->resolve($this->container ?? $this);
72
        }
73
74 10
        return $definition->resolve($this);
75
    }
76
77
    /**
78
     * @param mixed $id
79
     *
80
     * @throws InvalidConfigException
81
     */
82 36
    public function getDefinition($id): DefinitionInterface
83
    {
84 36
        if (is_string($id)) {
85
            // prevent infinite loop when Reference definition points to string but not to a class
86
            /** @psalm-suppress ArgumentTypeCoercion */
87 36
            return $this->definitions[$id] ?? ArrayDefinition::fromArray($id);
88
        }
89
90
        return Normalizer::normalize($id);
91
    }
92
93
    /**
94
     * Sets a definition to the factory.
95
     *
96
     * @param mixed $definition
97
     *
98
     * @throws InvalidConfigException
99
     *
100
     * @see `Normalizer::normalize()`
101
     */
102 21
    public function set(string $id, $definition): void
103
    {
104 21
        $this->definitions[$id] = Normalizer::normalize($definition, $id);
105 21
    }
106
107
    /**
108
     * Sets multiple definitions at once.
109
     *
110
     * @param array $definitions definitions indexed by their ids
111
     *
112
     * @psalm-param array<string, mixed> $definitions
113
     *
114
     * @throws InvalidConfigException
115
     */
116 44
    public function setMultiple(array $definitions): void
117
    {
118
        /** @var mixed $definition */
119 44
        foreach ($definitions as $id => $definition) {
120 19
            $this->set($id, $definition);
121
        }
122 44
    }
123
124
    /**
125
     * Returns a value indicating whether the container has the definition of the specified name.
126
     *
127
     * @param string $id class name, interface name or alias name
128
     *
129
     * @return bool whether the container is able to provide instance of class specified.
130
     *
131
     * @see set()
132
     */
133 14
    public function has($id): bool
134
    {
135 14
        return isset($this->definitions[$id]);
136
    }
137
}
138