Passed
Push — master ( 316314...68ec29 )
by Alexander
02:27
created

ArrayDefinition   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Test Coverage

Coverage 94.55%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 20
eloc 48
c 1
b 0
f 0
dl 0
loc 149
ccs 52
cts 55
cp 0.9455
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getClass() 0 3 1
A __construct() 0 10 2
A setConstructorArguments() 0 14 2
A getMethodsAndProperties() 0 3 1
A mergeArguments() 0 9 2
A resolve() 0 3 1
A setClass() 0 18 4
A getMeta() 0 3 1
A getConstructorArguments() 0 3 1
A merge() 0 17 3
A getType() 0 3 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory\Definition;
6
7
use Psr\Container\ContainerInterface;
8
use Yiisoft\Factory\Exception\InvalidConfigException;
9
10
use Yiisoft\Factory\Exception\NotInstantiableException;
11
12
use function array_key_exists;
13
use function get_class;
14
use function gettype;
15
use function is_array;
16
use function is_object;
17
use function is_string;
18
19
/**
20
 * Builds object by array config
21
 */
22
class ArrayDefinition implements DefinitionInterface
23
{
24
    public const CLASS_NAME = 'class';
25
    public const CONSTRUCTOR = '__construct()';
26
27
    /**
28
     * @psalm-var class-string
29
     */
30
    private string $class;
31
    private array $constructorArguments;
32
33
    /**
34
     * @psalm-var array<string, mixed|array>
35
     */
36
    private array $methodsAndProperties;
37
38
    /**
39
     * @psalm-var array<string, mixed>
40
     */
41
    private array $meta = [];
42
43
    /**
44
     * @param array $config Container entry config.
45
     * @param bool $checkDefinition Check definition flag.
46
     *
47
     * @throws InvalidConfigException
48
     */
49 49
    public function __construct(array $config, bool $checkDefinition = true)
50
    {
51 49
        if ($checkDefinition) {
52 49
            [$config,] = Normalizer::parse($config, []);
53
        }
54 46
        $this->setClass($config);
55 43
        unset($config[self::CLASS_NAME]);
56 43
        $this->setConstructorArguments($config);
57 42
        unset($config[self::CONSTRUCTOR]);
58 42
        $this->methodsAndProperties = $config;
59 42
    }
60
61
    /**
62
     * @throws InvalidConfigException
63
     */
64 46
    private function setClass(array $config): void
65
    {
66 46
        if (!array_key_exists(self::CLASS_NAME, $config)) {
67 1
            throw new InvalidConfigException('Invalid definition: no class name specified.');
68
        }
69
70
        /** @var mixed */
71 45
        $class = $config[self::CLASS_NAME];
72
73 45
        if (!is_string($class)) {
74 1
            throw new InvalidConfigException(sprintf('Invalid definition: invalid class name "%s".', (string)$class));
75
        }
76
77 44
        if ($class === '') {
78 1
            throw new InvalidConfigException('Invalid definition: empty class name.');
79
        }
80
81 43
        $this->class = $class;
82 43
    }
83
84
    /**
85
     * @throws InvalidConfigException
86
     */
87 43
    private function setConstructorArguments(array $config): void
88
    {
89 43
        $arguments = $config[self::CONSTRUCTOR] ?? [];
90
91 43
        if (!is_array($arguments)) {
92 1
            throw new InvalidConfigException(
93 1
                sprintf(
94 1
                    'Invalid definition: incorrect constructor arguments. Expected array, got %s.',
95 1
                    $this->getType($arguments)
96
                )
97
            );
98
        }
99
100 42
        $this->constructorArguments = $arguments;
101 42
    }
102
103
    /**
104
     * @psalm-return class-string
105
     */
106 42
    public function getClass(): string
107
    {
108 42
        return $this->class;
109
    }
110
111 39
    public function getConstructorArguments(): array
112
    {
113 39
        return $this->constructorArguments;
114
    }
115
116 36
    public function getMethodsAndProperties(): array
117
    {
118 36
        return $this->methodsAndProperties;
119
    }
120
121
    /**
122
     * @throws NotInstantiableException
123
     * @throws InvalidConfigException
124
     */
125 42
    public function resolve(ContainerInterface $container): object
126
    {
127 42
        return ArrayDefinitionBuilder::getInstance()->build($container, $this);
128
    }
129
130 2
    public function merge(self $other): self
131
    {
132 2
        $methodsAndProperties = $this->getMethodsAndProperties();
133
134 2
        foreach ($other->getMethodsAndProperties() as $name => $arguments) {
135 1
            $methodsAndProperties[$name] = isset($methodsAndProperties[$name])
136 1
                ? $this->mergeArguments($methodsAndProperties[$name], $arguments)
137
                : $arguments;
138
        }
139
140 2
        return new self(array_merge([
141 2
            self::CLASS_NAME => $other->getClass(),
142 2
            self::CONSTRUCTOR => $this->mergeArguments(
143 2
                $this->getConstructorArguments(),
144 2
                $other->getConstructorArguments()
145
            ),
146
        ], $methodsAndProperties));
147
    }
148
149 2
    private function mergeArguments(array $selfArguments, array $otherArguments): array
150
    {
151
        /** @var mixed $argument */
152 2
        foreach ($otherArguments as $name => $argument) {
153
            /** @var mixed */
154 1
            $selfArguments[$name] = $argument;
155
        }
156
157 2
        return $selfArguments;
158
    }
159
160
    /**
161
     * @param mixed $value
162
     */
163 1
    private function getType($value): string
164
    {
165 1
        return is_object($value) ? get_class($value) : gettype($value);
166
    }
167
168
    public function getMeta(): array
169
    {
170
        return $this->meta;
171
    }
172
}
173