Passed
Pull Request — master (#88)
by Alexander
02:22
created

ArrayDefinition::setClass()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 18
ccs 9
cts 9
cp 1
rs 10
cc 4
nc 4
nop 1
crap 4
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
     * @param array $config Container entry config.
40
     *
41
     * @throws InvalidConfigException
42
     */
43 46
    public function __construct(array $config)
44
    {
45 46
        $this->setClass($config);
46 43
        unset($config[self::CLASS_NAME]);
47 43
        $this->setConstructorArguments($config);
48 42
        unset($config[self::CONSTRUCTOR]);
49 42
        $this->methodsAndProperties = $config;
50 42
    }
51
52
    /**
53
     * @throws InvalidConfigException
54
     */
55 46
    private function setClass(array $config): void
56
    {
57 46
        if (!array_key_exists(self::CLASS_NAME, $config)) {
58 1
            throw new InvalidConfigException('Invalid definition: no class name specified.');
59
        }
60
61
        /** @var mixed */
62 45
        $class = $config[self::CLASS_NAME];
63
64 45
        if (!is_string($class)) {
65 1
            throw new InvalidConfigException(sprintf('Invalid definition: invalid class name "%s".', (string)$class));
66
        }
67
68 44
        if ($class === '') {
69 1
            throw new InvalidConfigException('Invalid definition: empty class name.');
70
        }
71
72 43
        $this->class = $class;
73 43
    }
74
75
    /**
76
     * @throws InvalidConfigException
77
     */
78 43
    private function setConstructorArguments(array $config): void
79
    {
80 43
        $arguments = $config[self::CONSTRUCTOR] ?? [];
81
82 43
        if (!is_array($arguments)) {
83 1
            throw new InvalidConfigException(
84 1
                sprintf(
85 1
                    'Invalid definition: incorrect constructor arguments. Expected array, got %s.',
86 1
                    $this->getType($arguments)
87
                )
88
            );
89
        }
90
91 42
        $this->constructorArguments = $arguments;
92 42
    }
93
94
    /**
95
     * @psalm-return class-string
96
     */
97 42
    public function getClass(): string
98
    {
99 42
        return $this->class;
100
    }
101
102 39
    public function getConstructorArguments(): array
103
    {
104 39
        return $this->constructorArguments;
105
    }
106
107 36
    public function getMethodsAndProperties(): array
108
    {
109 36
        return $this->methodsAndProperties;
110
    }
111
112
    /**
113
     * @throws NotInstantiableException
114
     * @throws InvalidConfigException
115
     */
116 42
    public function resolve(ContainerInterface $container): object
117
    {
118 42
        return ArrayDefinitionBuilder::getInstance()->build($container, $this);
119
    }
120
121 2
    public function merge(self $other): self
122
    {
123 2
        $methodsAndProperties = $this->getMethodsAndProperties();
124
125 2
        foreach ($other->getMethodsAndProperties() as $name => $arguments) {
126 1
            $methodsAndProperties[$name] = isset($methodsAndProperties[$name])
127 1
                ? $this->mergeArguments($methodsAndProperties[$name], $arguments)
128
                : $arguments;
129
        }
130
131 2
        return new self(array_merge([
132 2
            self::CLASS_NAME => $other->getClass(),
133 2
            self::CONSTRUCTOR => $this->mergeArguments(
134 2
                $this->getConstructorArguments(),
135 2
                $other->getConstructorArguments()
136
            ),
137
        ], $methodsAndProperties));
138
    }
139
140 2
    private function mergeArguments(array $selfArguments, array $otherArguments): array
141
    {
142
        /** @var mixed $argument */
143 2
        foreach ($otherArguments as $name => $argument) {
144
            /** @var mixed */
145 1
            $selfArguments[$name] = $argument;
146
        }
147
148 2
        return $selfArguments;
149
    }
150
151
    /**
152
     * @param mixed $value
153
     */
154 1
    private function getType($value): string
155
    {
156 1
        return is_object($value) ? get_class($value) : gettype($value);
157
    }
158
}
159