Passed
Push — master ( ef615e...64b3e0 )
by Sergei
02:11
created

ArrayDefinition::mergeConstructorArguments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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