Passed
Pull Request — master (#78)
by Dmitriy
07:47
created

Factory::merge()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 2
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 6
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 14
    public function __construct(ContainerInterface $container = null, array $definitions = [])
38
    {
39 14
        $this->container = $container;
40 14
        $this->setMultiple($definitions);
41 14
    }
42
43 7
    public function create($config, array $params = [])
44
    {
45 7
        $definition = Normalizer::normalize($config, null, $params);
46 7
        if ($definition instanceof ArrayDefinition && $this->has($definition->getClass())) {
47
            $definition = $this->merge($this->getDefinition($definition->getClass()), $definition);
48
        }
49
50 7
        if ($definition instanceof ArrayDefinition) {
51 1
            return $definition->resolve($this->container ?? $this);
52
        }
53
54 6
        return $definition->resolve($this);
55
    }
56
57
    private function merge(DefinitionInterface $one, ArrayDefinition $two): DefinitionInterface
58
    {
59
        return $one instanceof ArrayDefinition ? $one->merge($two) : $two;
60
    }
61
62
    /**
63
     * @param string $id
64
     *
65
     * @throws NotInstantiableException
66
     *
67
     * @return mixed|object
68
     */
69 11
    public function get($id)
70
    {
71
        try {
72 11
            $definition = $this->getDefinition($id);
73 5
        } catch (InvalidConfigException $e) {
74 5
            throw new NotInstantiableException($id);
75
        }
76
77 8
        if ($definition instanceof ArrayDefinition) {
0 ignored issues
show
introduced by
$definition is always a sub-type of Yiisoft\Factory\Definitions\ArrayDefinition.
Loading history...
78 8
            return $definition->resolve($this->container ?? $this);
79
        }
80
81 1
        return $definition->resolve($this);
82
    }
83
84
    /**
85
     * @param mixed $id
86
     *
87
     * @throws InvalidConfigException
88
     */
89 11
    public function getDefinition($id): DefinitionInterface
90
    {
91 11
        if (is_string($id)) {
92
            // prevent infinite loop when Reference definition points to string but not to a class
93
            /** @psalm-suppress ArgumentTypeCoercion */
94 11
            return $this->definitions[$id] ?? new ArrayDefinition([ArrayDefinition::CLASS_NAME => $id]);
95
        }
96
97
        return Normalizer::normalize($id);
98
    }
99
100
    /**
101
     * Sets a definition to the factory.
102
     *
103
     * @param mixed $definition
104
     *
105
     * @throws InvalidConfigException
106
     *
107
     * @see `Normalizer::normalize()`
108
     */
109 4
    public function set(string $id, $definition): void
110
    {
111 4
        $this->definitions[$id] = Normalizer::normalize($definition, $id);
112 4
    }
113
114
    /**
115
     * Sets multiple definitions at once.
116
     *
117
     * @param array $definitions definitions indexed by their ids
118
     *
119
     * @psalm-param array<string, mixed> $definitions
120
     *
121
     * @throws InvalidConfigException
122
     */
123 14
    public function setMultiple(array $definitions): void
124
    {
125
        /** @var mixed $definition */
126 14
        foreach ($definitions as $id => $definition) {
127 4
            $this->set($id, $definition);
128
        }
129 14
    }
130
131
    /**
132
     * Returns a value indicating whether the container has the definition of the specified name.
133
     *
134
     * @param string $id class name, interface name or alias name
135
     *
136
     * @return bool whether the container is able to provide instance of class specified.
137
     *
138
     * @see set()
139
     */
140 1
    public function has($id): bool
141
    {
142 1
        return isset($this->definitions[$id]);
143
    }
144
}
145