Passed
Pull Request — master (#93)
by Sergei
04:47 queued 02:36
created

ArrayDefinition   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 143
Duplicated Lines 0 %

Test Coverage

Coverage 94.23%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 20
eloc 46
c 2
b 0
f 0
dl 0
loc 143
ccs 49
cts 52
cp 0.9423
rs 10

11 Methods

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