Passed
Pull Request — master (#85)
by Sergei
02:32
created

ArrayDefinition::mergeConstructorArguments()   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
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
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
use Yiisoft\Factory\Exception\NotInstantiableException;
10
11
use function count;
12
13
/**
14
 * Builds object by array config
15
 *
16
 * @psalm-type MethodOrPropertyItem = array{0:string,1:string,2:mixed}
17
 */
18
class ArrayDefinition implements DefinitionInterface
19
{
20
    public const CLASS_NAME = 'class';
21
    public const CONSTRUCTOR = '__construct()';
22
23
    public const TYPE_PROPERTY = 'property';
24
    public const TYPE_METHOD = 'method';
25
26
    /**
27
     * @psalm-var class-string
28
     */
29
    private string $class;
30
    private array $constructorArguments;
31
32
    /**
33
     * @psalm-var array<string, MethodOrPropertyItem>
34
     */
35
    private array $methodsAndProperties;
36
37
    /**
38
     * @psalm-param class-string $class
39
     * @psalm-param array<string, MethodOrPropertyItem> $methodsAndProperties
40
     */
41 46
    private function __construct(string $class, array $constructorArguments, array $methodsAndProperties)
42
    {
43 46
        $this->class = $class;
44 46
        $this->constructorArguments = $constructorArguments;
45 46
        $this->methodsAndProperties = $methodsAndProperties;
46 46
    }
47
48
    /**
49
     * @psalm-param array{
50
     *   class:class-string,
51
     *   '__construct()'?:array,
52
     * }&array<string, mixed> $config
53
     */
54 26
    public static function fromConfig(array $config): self
55
    {
56 26
        return new self(
57 26
            $config[self::CLASS_NAME],
58 26
            $config[self::CONSTRUCTOR] ?? [],
59 26
            self::getMethodsAndPropertiesFromConfig($config)
60
        );
61
    }
62
63
    /**
64
     * @psalm-param class-string $class
65
     * @psalm-param array<string, MethodOrPropertyItem> $methodsAndProperties
66
     */
67 22
    public static function fromPreparedData(string $class, array $constructorArguments = [], array $methodsAndProperties = []): self
68
    {
69 22
        return new self($class, $constructorArguments, $methodsAndProperties);
70
    }
71
72
    /**
73
     * @psalm-param array<string, mixed> $config
74
     *
75
     * @psalm-return array<string, MethodOrPropertyItem>
76
     */
77 26
    private static function getMethodsAndPropertiesFromConfig(array $config): array
78
    {
79 26
        $methodsAndProperties = [];
80
81
        /** @var mixed $value */
82 26
        foreach ($config as $key => $value) {
83 26
            if ($key === self::CONSTRUCTOR) {
84 10
                continue;
85
            }
86
87 26
            if (count($methodArray = explode('()', $key)) === 2) {
88 11
                $methodsAndProperties[$key] = [self::TYPE_METHOD, $methodArray[0], $value];
89 26
            } elseif (count($propertyArray = explode('$', $key)) === 2) {
90 4
                $methodsAndProperties[$key] = [self::TYPE_PROPERTY, $propertyArray[1], $value];
91
            }
92
        }
93
94 26
        return $methodsAndProperties;
95
    }
96
97
    /**
98
     * @psalm-return class-string
99
     */
100 45
    public function getClass(): string
101
    {
102 45
        return $this->class;
103
    }
104
105 42
    public function getConstructorArguments(): array
106
    {
107 42
        return $this->constructorArguments;
108
    }
109
110 7
    public function mergeConstructorArguments(array $arguments): void
111
    {
112 7
        $this->constructorArguments = $this->mergeArguments($this->constructorArguments, $arguments);
113 7
    }
114
115
    /**
116
     * @psalm-return array<string, MethodOrPropertyItem>
117
     */
118 39
    public function getMethodsAndProperties(): array
119
    {
120 39
        return $this->methodsAndProperties;
121
    }
122
123
    /**
124
     * @throws NotInstantiableException
125
     * @throws InvalidConfigException
126
     */
127 44
    public function resolve(ContainerInterface $container): object
128
    {
129 44
        return ArrayDefinitionBuilder::getInstance()->build($container, $this);
130
    }
131
132 6
    public function merge(self $other): self
133
    {
134 6
        $new = clone $this;
135 6
        $new->class = $other->class;
136 6
        $new->constructorArguments = $this->mergeArguments($this->constructorArguments, $other->constructorArguments);
137
138 6
        $methodsAndProperties = $this->methodsAndProperties;
139 6
        foreach ($other->methodsAndProperties as $key => $item) {
140 2
            if ($item[0] === self::TYPE_PROPERTY) {
141 1
                $methodsAndProperties[$key] = $item;
142 2
            } elseif ($item[0] === self::TYPE_METHOD) {
143
                /** @psalm-suppress MixedArgument */
144 2
                $methodsAndProperties[$key] = [
145 2
                    $item[0],
146 2
                    $item[1],
147 2
                    isset($methodsAndProperties[$key])
148 1
                        ? $this->mergeArguments($methodsAndProperties[$key][2], $item[2])
149 2
                        : $item[2],
150
                ];
151
            }
152
        }
153 6
        $new->methodsAndProperties = $methodsAndProperties;
154
155 6
        return $new;
156
    }
157
158 11
    private function mergeArguments(array $selfArguments, array $otherArguments): array
159
    {
160
        /** @var mixed $argument */
161 11
        foreach ($otherArguments as $name => $argument) {
162
            /** @var mixed */
163 9
            $selfArguments[$name] = $argument;
164
        }
165
166 11
        return $selfArguments;
167
    }
168
}
169