Test Failed
Pull Request — master (#212)
by Sergei
02:18
created

DefinitionParser::checkMetaKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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