Passed
Pull Request — master (#78)
by Sergei
02:16
created

ArrayDefinition::getConstructorParameters()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory\Definitions;
6
7
use Psr\Container\ContainerInterface;
8
use Yiisoft\Factory\Exceptions\InvalidConfigException;
9
10
use Yiisoft\Factory\Exceptions\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_int;
17
use function is_object;
18
use function is_string;
19
20
/**
21
 * Builds object by array config
22
 */
23
class ArrayDefinition implements DefinitionInterface
24
{
25
    public const CLASS_NAME = 'class';
26
    public const CONSTRUCTOR = 'constructor';
27
    public const SET_PROPERTIES = 'setProperties';
28
    public const CALL_METHODS = 'callMethods';
29
30
    /**
31
     * @psalm-var class-string
32
     */
33
    private string $class;
34
    private array $constructorParameters;
35
36
    /**
37
     * @psalm-var array<string, array>
38
     */
39
    private array $callMethods;
40
41
    /**
42
     * @psalm-var array<string, mixed>
43
     */
44
    private array $setProperties;
45
46
    /**
47
     * @psalm-param array{
48
     *   class: class-string,
49
     *   constructor?: array,
50
     *   callMethods?: array,
51
     *   setProperties?: array,
52
     * } $config
53
     *
54
     * @throws InvalidConfigException
55
     */
56 52
    public function __construct(array $config)
57
    {
58 52
        $this->setClass($config);
59 45
        $this->setConstructorParameters($config);
60 44
        $this->setCallMethods($config);
61 40
        $this->setSetProperties($config);
62 38
    }
63
64
    /**
65
     * @throws InvalidConfigException
66
     */
67 52
    private function setClass(array $config): void
68
    {
69 52
        if (!array_key_exists(self::CLASS_NAME, $config)) {
70 1
            throw new InvalidConfigException('Invalid definition: no class name specified.');
71
        }
72
73
        /** @var mixed */
74 51
        $class = $config[self::CLASS_NAME];
75
76 51
        if (!is_string($class)) {
77 1
            throw new InvalidConfigException(sprintf('Invalid definition: invalid class name "%s".', (string)$class));
78
        }
79
80 50
        if ($class === '') {
81 1
            throw new InvalidConfigException('Invalid definition: empty class name.');
82
        }
83
84 49
        if (!class_exists($class)) {
85 6
            throw new InvalidConfigException(sprintf('Invalid definition: class "%s" does not exist.', $class));
86
        }
87
88 45
        $this->class = $class;
89 45
    }
90
91
    /**
92
     * @throws InvalidConfigException
93
     */
94 45
    private function setConstructorParameters(array $config): void
95
    {
96 45
        $parameters = $config[self::CONSTRUCTOR] ?? [];
97
98 45
        if (!is_array($parameters)) {
99 1
            throw new InvalidConfigException(
100 1
                sprintf(
101 1
                    'Invalid definition: incorrect constructor parameters. Expected array, got %s.',
102 1
                    $this->getType($parameters)
103
                )
104
            );
105
        }
106
107 44
        $this->constructorParameters = $parameters;
108 44
    }
109
110
    /**
111
     * @throws InvalidConfigException
112
     */
113 44
    private function setCallMethods(array $config): void
114
    {
115 44
        $items = $config[self::CALL_METHODS] ?? [];
116
117 44
        if (!is_array($items)) {
118 1
            throw new InvalidConfigException(
119 1
                sprintf('Invalid definition: incorrect method calls. Expected array, got %s.', $this->getType($items))
120
            );
121
        }
122
123 43
        $callMethods = [];
124 43
        foreach ($items as $key => $value) {
125 13
            if (is_int($key)) {
126 4
                if (!is_string($value)) {
127 1
                    throw new InvalidConfigException(
128 1
                        sprintf('Invalid definition: expected method name, got %s.', $this->getType($value))
129
                    );
130
                }
131 3
                if ($value === '') {
132 1
                    throw new InvalidConfigException('Invalid definition: expected method name, got empty string.');
133
                }
134 2
                $callMethods[$value] = [];
135
            } else {
136 10
                if (!is_array($value)) {
137 1
                    throw new InvalidConfigException(
138 1
                        sprintf('Invalid definition: incorrect method parameters. Expected array, got %s.', $this->getType($value))
139
                    );
140
                }
141 9
                $callMethods[$key] = $value;
142
            }
143
        }
144
145 40
        $this->callMethods = $callMethods;
146 40
    }
147
148
    /**
149
     * @throws InvalidConfigException
150
     */
151 40
    private function setSetProperties(array $config): void
152
    {
153 40
        $properties = $config[self::SET_PROPERTIES] ?? [];
154
155 40
        if (!is_array($properties)) {
156 1
            throw new InvalidConfigException(
157 1
                sprintf('Invalid definition: incorrect properties to set. Expected array, got %s.', $this->getType($properties))
158
            );
159
        }
160
161 39
        foreach ($properties as $key => $_value) {
162 3
            if (!is_string($key)) {
163 1
                throw new InvalidConfigException(
164 1
                    sprintf('Invalid definition: expected property name, got %s.', $this->getType($key))
165
                );
166
            }
167
        }
168
169
        /** @psalm-var array<string,mixed> $properties */
170
171 38
        $this->setProperties = $properties;
172 38
    }
173
174
    /**
175
     * @psalm-return class-string
176
     */
177 38
    public function getClass(): string
178
    {
179 38
        return $this->class;
180
    }
181
182 38
    public function getConstructorParameters(): array
183
    {
184 38
        return $this->constructorParameters;
185
    }
186
187
    /**
188
     * @psalm-return array<string, array>
189
     */
190 35
    public function getCallMethods(): array
191
    {
192 35
        return $this->callMethods;
193
    }
194
195
    /**
196
     * @psalm-return array<string, mixed>
197
     */
198 35
    public function getSetProperties(): array
199
    {
200 35
        return $this->setProperties;
201
    }
202
203
    /**
204
     * @throws NotInstantiableException
205
     * @throws InvalidConfigException
206
     */
207 38
    public function resolve(ContainerInterface $container): object
208
    {
209 38
        return ArrayDefinitionBuilder::getInstance()->build($container, $this);
210
    }
211
212 2
    public function merge(self $other): self
213
    {
214 2
        $callMethods = $this->getCallMethods();
215 2
        foreach ($other->getCallMethods() as $method => $parameters) {
216 1
            $callMethods[$method] = isset($callMethods[$method])
217 1
                ? $this->mergeParameters($callMethods[$method], $parameters)
218
                : $parameters;
219
        }
220
221 2
        return new self([
222 2
            self::CLASS_NAME => $other->getClass(),
223 2
            self::CONSTRUCTOR => $this->mergeParameters(
224 2
                $this->getConstructorParameters(),
225 2
                $other->getConstructorParameters()
226
            ),
227 2
            self::CALL_METHODS => $callMethods,
228 2
            self::SET_PROPERTIES => array_merge($this->getSetProperties(), $other->getSetProperties()),
229
        ]);
230
    }
231
232 2
    private function mergeParameters(array $selfParameters, array $otherParameters): array
233
    {
234
        /** @var mixed $parameter */
235 2
        foreach ($otherParameters as $index => $parameter) {
236
            /** @var mixed */
237 1
            $selfParameters[$index] = $parameter;
238
        }
239
240 2
        return $selfParameters;
241
    }
242
243
    /**
244
     * @param mixed $value
245
     */
246 6
    private function getType($value): string
247
    {
248 6
        return is_object($value) ? get_class($value) : gettype($value);
249
    }
250
}
251