Test Failed
Pull Request — master (#212)
by Alexander
06:37 queued 04:20
created

DefinitionParser::parse()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 5
eloc 16
c 3
b 0
f 0
nc 6
nop 1
dl 0
loc 28
rs 9.4222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Di;
6
7
use League\Container\Definition\DefinitionInterface;
8
use Yiisoft\Factory\Definition\ArrayDefinition;
9
use Yiisoft\Factory\Definition\ArrayDefinitionValidator;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Factory\Definiti...rrayDefinitionValidator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Yiisoft\Factory\Exception\InvalidConfigException;
11
12
use function in_array;
13
use function is_array;
14
use function is_object;
15
use function is_string;
16
17
/**
18
 * @internal Splits metadata and definition.
19
 *
20
 * Supports the following configuration:
21
 *
22
 * 1) With a dedicated definition:
23
 *
24
 * ```php
25
 * Engine::class => [
26
 *     'definition' => [
27
 *         '__class' => BigEngine::class,
28
 *         'setNumber()' => [42],
29
 *     ],
30
 *     'tags' => ['a', 'b'],
31
 *     'reset' => function () {
32
 *         $this->number = 42;
33
 *      },
34
 * ]
35
 * ```
36
 *
37
 * 2) Mixed in array definition:
38
 *
39
 * ```php
40
 * Engine::class => [
41
 *     '__class' => BigEngine::class,
42
 *     'setNumber()' => [42],
43
 *     'tags' => ['a', 'b'],
44
 *     'reset' => function () {
45
 *         $this->number = 42;
46
 *      },
47
 * ]
48
 * ```
49
 */
50
final class DefinitionParser
51
{
52
    private const DEFINITION_META = 'definition';
53
54
    public const IS_PREPARED_ARRAY_DEFINITION_DATA = 'isPreparedArrayDefinitionData';
55
56
    private array $allowedMeta;
57
58
    public function __construct(array $allowedMeta)
59
    {
60
        $this->allowedMeta = $allowedMeta;
61
    }
62
63
    /**
64
     * @param mixed $definition
65
     *
66
     * @throws InvalidConfigException
67
     */
68
    public function parse($definition): array
69
    {
70
        if (!is_array($definition)) {
71
            $this->checkNotArrayDefinitionConfig($definition);
72
            return [$definition, []];
73
        }
74
75
        // Dedicated definition
76
        if (isset($definition[self::DEFINITION_META])) {
77
            $newDefinition = $definition[self::DEFINITION_META];
78
            unset($definition[self::DEFINITION_META]);
79
80
            foreach ($definition as $key => $_value) {
81
                $this->checkMetaKey($key);
82
            }
83
84
            if (is_array($newDefinition)) {
85
                $this->prepareDefinitionFromArray($newDefinition);
86
            } else {
87
                $this->checkNotArrayDefinitionConfig($newDefinition);
88
            }
89
90
            return [$newDefinition, $definition];
91
        }
92
93
        $meta = [];
94
        $this->prepareDefinitionFromArray($definition, $meta);
95
        return [$definition, $meta];
96
    }
97
98
    /**
99
     * @throws InvalidConfigException
100
     */
101
    private function prepareDefinitionFromArray(array &$definition, array &$meta = null): void
102
    {
103
        $class = null;
104
        $constructorArguments = [];
105
        $methodsAndProperties = [];
106
        foreach ($definition as $key => $value) {
107
            // It is not array definition
108
            if (!is_string($key)) {
109
                $this->checkNotArrayDefinitionConfig($definition);
110
                return;
111
            }
112
113
            // Class
114
            if ($key === ArrayDefinition::CLASS_NAME) {
115
                ArrayDefinitionValidator::validateClassName($value);
116
                $class = $value;
117
                continue;
118
            }
119
120
            // Constructor arguments
121
            if ($key === ArrayDefinition::CONSTRUCTOR) {
122
                ArrayDefinitionValidator::validateConstructorArguments($value);
123
                $constructorArguments = $value;
124
                continue;
125
            }
126
127
            // Methods and properties
128
            if (substr($key, -2) === '()') {
129
                ArrayDefinitionValidator::validateMethodArguments($value);
130
                $methodsAndProperties[$key] = [ArrayDefinition::FLAG_METHOD, $key, $value];
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Factory\Definiti...Definition::FLAG_METHOD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
131
                continue;
132
            }
133
            if (strncmp($key, '$', 1) === 0) {
134
                $methodsAndProperties[$key] = [ArrayDefinition::FLAG_PROPERTY, $key, $value];
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Factory\Definiti...finition::FLAG_PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
135
                continue;
136
            }
137
138
            $this->checkMetaKey($key);
139
140
            if ($meta !== null) {
141
                $meta[$key] = $value;
142
            }
143
        }
144
        $definition = [
145
            $class,
146
            $constructorArguments,
147
            $methodsAndProperties,
148
            self::IS_PREPARED_ARRAY_DEFINITION_DATA => true,
149
        ];
150
    }
151
152
    /**
153
     * @param mixed $definition
154
     *
155
     * @throws InvalidConfigException
156
     */
157
    private function checkNotArrayDefinitionConfig($definition): void
158
    {
159
        if ($definition instanceof DefinitionInterface) {
160
            return;
161
        }
162
163
        if (is_array($definition)) {
164
            return;
165
        }
166
167
        if (is_string($definition) && !empty($definition)) {
168
            return;
169
        }
170
171
        if (is_object($definition)) {
172
            return;
173
        }
174
175
        throw new InvalidConfigException('Invalid definition:' . var_export($definition, true));
176
    }
177
178
    /**
179
     * @throws InvalidConfigException
180
     */
181
    private function checkMetaKey(string $key): void
182
    {
183
        if (!in_array($key, $this->allowedMeta, true)) {
184
            throw new InvalidConfigException(
185
                sprintf(
186
                    'Invalid definition: metadata "%s" is not allowed. Did you mean "%s()" or "$%s"?',
187
                    $key,
188
                    $key,
189
                    $key,
190
                )
191
            );
192
        }
193
    }
194
}
195