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

getMethodsAndPropertiesFromConfig()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 9
c 2
b 0
f 0
dl 0
loc 18
ccs 10
cts 10
cp 1
rs 9.6111
cc 5
nc 5
nop 1
crap 5
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 44
    private function __construct(string $class, array $constructorArguments, array $methodsAndProperties)
42
    {
43 44
        $this->class = $class;
44 44
        $this->constructorArguments = $constructorArguments;
45 44
        $this->methodsAndProperties = $methodsAndProperties;
46 44
    }
47
48
    /**
49
     * @psalm-param array{
50
     *   class:class-string,
51
     *   '__construct()'?:array,
52
     * }&array<string, mixed> $config
53
     */
54 24
    public static function fromConfig(array $config): self
55
    {
56 24
        return new self(
57 24
            $config[self::CLASS_NAME],
58 24
            $config[self::CONSTRUCTOR] ?? [],
59 24
            self::getMethodsAndPropertiesFromConfig($config)
60
        );
61
    }
62
63
    /**
64
     * @psalm-param class-string $class
65
     * @psalm-param array<string, MethodOrPropertyItem> $methodsAndProperties
66
     */
67 20
    public static function fromPreparedData(string $class, array $constructorArguments = [], array $methodsAndProperties = []): self
68
    {
69 20
        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 24
    private static function getMethodsAndPropertiesFromConfig(array $config): array
78
    {
79 24
        $methodsAndProperties = [];
80
81
        /** @var mixed $value */
82 24
        foreach ($config as $key => $value) {
83 24
            if ($key === self::CONSTRUCTOR) {
84 8
                continue;
85
            }
86
87 24
            if (count($methodArray = explode('()', $key)) === 2) {
88 11
                $methodsAndProperties[$key] = [self::TYPE_METHOD, $methodArray[0], $value];
89 24
            } elseif (count($propertyArray = explode('$', $key)) === 2) {
90 4
                $methodsAndProperties[$key] = [self::TYPE_PROPERTY, $propertyArray[1], $value];
91
            }
92
        }
93
94 24
        return $methodsAndProperties;
95
    }
96
97
    /**
98
     * @psalm-return class-string
99
     */
100 43
    public function getClass(): string
101
    {
102 43
        return $this->class;
103
    }
104
105 40
    public function getConstructorArguments(): array
106
    {
107 40
        return $this->constructorArguments;
108
    }
109
110 5
    public function setConstructorArguments(array $arguments): void
111
    {
112 5
        $this->constructorArguments = $arguments;
113 5
    }
114
115
    /**
116
     * @psalm-return array<string, MethodOrPropertyItem>
117
     */
118 37
    public function getMethodsAndProperties(): array
119
    {
120 37
        return $this->methodsAndProperties;
121
    }
122
123
    /**
124
     * @throws NotInstantiableException
125
     * @throws InvalidConfigException
126
     */
127 42
    public function resolve(ContainerInterface $container): object
128
    {
129 42
        return ArrayDefinitionBuilder::getInstance()->build($container, $this);
130
    }
131
132 4
    public function merge(self $other): self
133
    {
134 4
        $new = clone $this;
135 4
        $new->class = $other->class;
136 4
        $new->constructorArguments = $this->mergeArguments($this->constructorArguments, $other->constructorArguments);
137
138 4
        $methodsAndProperties = $this->methodsAndProperties;
139 4
        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 4
        $new->methodsAndProperties = $methodsAndProperties;
154
155 4
        return $new;
156
    }
157
158 4
    private function mergeArguments(array $selfArguments, array $otherArguments): array
159
    {
160
        /** @var mixed $argument */
161 4
        foreach ($otherArguments as $name => $argument) {
162
            /** @var mixed */
163 2
            $selfArguments[$name] = $argument;
164
        }
165
166 4
        return $selfArguments;
167
    }
168
}
169