Passed
Pull Request — master (#85)
by Sergei
06:13 queued 04:06
created

getMethodsAndPropertiesFromConfig()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
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 17
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 TYPE_PROPERTY = 'property';
22
    public const TYPE_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
     * @psalm-param array{
48
     *   class:class-string,
49
     *   '__construct()'?:array,
50
     * }&array<string, mixed> $config
51
     */
52 24
    public static function fromConfig(array $config): self
53
    {
54 24
        return new self(
55 24
            $config[self::CLASS_NAME],
56 24
            $config[self::CONSTRUCTOR] ?? [],
57 24
            self::getMethodsAndPropertiesFromConfig($config)
58
        );
59
    }
60
61
    /**
62
     * @psalm-param class-string $class
63
     * @psalm-param array<string, MethodOrPropertyItem> $methodsAndProperties
64
     */
65 20
    public static function fromPreparedData(string $class, array $constructorArguments = [], array $methodsAndProperties = []): self
66
    {
67 20
        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 24
    private static function getMethodsAndPropertiesFromConfig(array $config): array
76
    {
77 24
        $methodsAndProperties = [];
78
79
        /** @var mixed $value */
80 24
        foreach ($config as $key => $value) {
81 24
            if ($key === self::CONSTRUCTOR) {
82 8
                continue;
83
            }
84 24
            if (substr($key, -2) === '()') {
85 11
                $methodsAndProperties[$key] = [self::TYPE_METHOD, $key, $value];
86 24
            } elseif (strncmp($key, '$', 1) === 0) {
87 4
                $methodsAndProperties[$key] = [self::TYPE_PROPERTY, $key, $value];
88
            }
89
        }
90
91 24
        return $methodsAndProperties;
92
    }
93
94
    /**
95
     * @psalm-return class-string
96
     */
97 43
    public function getClass(): string
98
    {
99 43
        return $this->class;
100
    }
101
102 40
    public function getConstructorArguments(): array
103
    {
104 40
        return $this->constructorArguments;
105
    }
106
107 5
    public function setConstructorArguments(array $arguments): void
108
    {
109 5
        $this->constructorArguments = $arguments;
110 5
    }
111
112
    /**
113
     * @psalm-return array<string, MethodOrPropertyItem>
114
     */
115 37
    public function getMethodsAndProperties(): array
116
    {
117 37
        return $this->methodsAndProperties;
118
    }
119
120
    /**
121
     * @throws NotInstantiableException
122
     * @throws InvalidConfigException
123
     */
124 42
    public function resolve(ContainerInterface $container): object
125
    {
126 42
        return ArrayDefinitionBuilder::getInstance()->build($container, $this);
127
    }
128
129 4
    public function merge(self $other): self
130
    {
131 4
        $new = clone $this;
132 4
        $new->class = $other->class;
133 4
        $new->constructorArguments = $this->mergeArguments($this->constructorArguments, $other->constructorArguments);
134
135 4
        $methodsAndProperties = $this->methodsAndProperties;
136 4
        foreach ($other->methodsAndProperties as $key => $item) {
137 2
            if ($item[0] === self::TYPE_PROPERTY) {
138 1
                $methodsAndProperties[$key] = $item;
139 2
            } elseif ($item[0] === self::TYPE_METHOD) {
140
                /** @psalm-suppress MixedArgument */
141 2
                $methodsAndProperties[$key] = [
142 2
                    $item[0],
143 2
                    $item[1],
144 2
                    isset($methodsAndProperties[$key])
145 1
                        ? $this->mergeArguments($methodsAndProperties[$key][2], $item[2])
146 2
                        : $item[2],
147
                ];
148
            }
149
        }
150 4
        $new->methodsAndProperties = $methodsAndProperties;
151
152 4
        return $new;
153
    }
154
155 4
    private function mergeArguments(array $selfArguments, array $otherArguments): array
156
    {
157
        /** @var mixed $argument */
158 4
        foreach ($otherArguments as $name => $argument) {
159
            /** @var mixed */
160 2
            $selfArguments[$name] = $argument;
161
        }
162
163 4
        return $selfArguments;
164
    }
165
}
166