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

getMethodsAndPropertiesFromConfig()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

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