Passed
Push — master ( 91e90a...0ab929 )
by Alexander
02:25
created

ArrayDefinition::fromConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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