Passed
Pull Request — master (#85)
by Alexander
03:33 queued 01:24
created

Normalizer::parse()   C

Complexity

Conditions 12
Paths 17

Size

Total Lines 42
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 25
c 1
b 0
f 0
dl 0
loc 42
ccs 0
cts 26
cp 0
rs 6.9666
cc 12
nc 17
nop 2
crap 156

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory\Definition;
6
7
use Yiisoft\Factory\Exception\InvalidConfigException;
8
9
use function array_key_exists;
10
use function is_array;
11
use function is_callable;
12
use function is_object;
13
use function is_string;
14
15
/**
16
 * Class Definition represents a definition in a container
17
 */
18
class Normalizer
19
{
20
    /**
21
     * Definition may be defined multiple ways.
22
     * Interface name as string:
23
     *
24
     * ```php
25
     * $container->set('interface_name', EngineInterface::class);
26
     * ```
27
     *
28
     * A closure:
29
     *
30
     * ```php
31
     * $container->set('closure', function($container) {
32
     *     return new MyClass($container->get('db'));
33
     * });
34
     * ```
35
     *
36
     * A callable array:
37
     *
38
     * ```php
39
     * $container->set('static_call', [MyClass::class, 'create']);
40
     * ```
41
     *
42
     * A definition array:
43
     *
44
     * ```php
45
     * $container->set('full_definition', [
46
     *     'class' => EngineMarkOne::class,
47
     *     '__construct()' => [42],
48
     *     '$argName' => 'value',
49
     *     'setX()' => [42],
50
     * ]);
51
     * ```
52
     *
53
     * @param mixed $definition
54
     *
55
     * @throws InvalidConfigException
56
     */
57 25
    public static function normalize($definition, string $id = null, array $constructorArguments = []): DefinitionInterface
58
    {
59 25
        if ($definition instanceof ReferenceInterface) {
60 1
            return $definition;
61
        }
62
63 25
        if (is_string($definition)) {
64 18
            if (empty($definition)) {
65
                throw new InvalidConfigException('Invalid definition: empty string.');
66
            }
67 18
            if ($id === $definition || (!empty($constructorArguments) && class_exists($definition))) {
68
                /** @psalm-var class-string $definition */
69 6
                return ArrayDefinition::fromPreparedData($definition, $constructorArguments);
70
            }
71 12
            return Reference::to($definition);
72
        }
73
74 11
        if (is_callable($definition, true)) {
75 3
            return new CallableDefinition($definition);
76
        }
77
78 8
        if (is_array($definition)) {
79 7
            $config = $definition;
80 7
            if (!array_key_exists(ArrayDefinition::CLASS_NAME, $config)) {
81
                $config[ArrayDefinition::CLASS_NAME] = $id;
82
            }
83
            /** @psalm-suppress ArgumentTypeCoercion */
84 7
            return ArrayDefinition::create($config);
85
        }
86
87 1
        if (is_object($definition)) {
88 1
            return new ValueDefinition($definition);
89
        }
90
91
        throw new InvalidConfigException('Invalid definition:' . var_export($definition, true));
92
    }
93
94
    /**
95
     * Validates definition for correctness.
96
     *
97
     * @param mixed $definition {@see normalize()}
98
     *
99
     * @throws InvalidConfigException
100
     */
101
    public static function validate($definition, bool $throw = true): bool
102
    {
103
        if ($definition instanceof ReferenceInterface) {
104
            return true;
105
        }
106
107
        if (is_string($definition) && !empty($definition)) {
108
            return true;
109
        }
110
111
        if (is_callable($definition)) {
112
            return true;
113
        }
114
115
        if (is_array($definition)) {
116
            return true;
117
        }
118
119
        if (is_object($definition)) {
120
            return true;
121
        }
122
123
        if ($throw) {
124
            throw new InvalidConfigException('Invalid definition:' . var_export($definition, true));
125
        }
126
127
        return false;
128
    }
129
130
    /**
131
     * Validates definition for correctness.
132
     *
133
     * @param mixed $definition
134
     * @param array $allowedMeta
135
     *
136
     * @throws InvalidConfigException
137
     */
138
    public static function parse($definition, array $allowedMeta): array
139
    {
140
        if (!is_array($definition)) {
141
            return [$definition, []];
142
        }
143
144
        $meta = [];
145
        if (isset($definition[self::DEFINITION_META])) {
0 ignored issues
show
Bug introduced by
The constant Yiisoft\Factory\Definiti...alizer::DEFINITION_META was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
146
            $newDefinition = $definition[self::DEFINITION_META];
147
            unset($definition[self::DEFINITION_META]);
148
            $meta = array_filter($definition, static function ($key) use ($allowedMeta) {
149
                return in_array($key, $allowedMeta, true);
150
            }, ARRAY_FILTER_USE_KEY);
151
            $definition = $newDefinition;
152
        }
153
154
        if (is_callable($definition, true)) {
155
            return [$definition, $meta];
156
        }
157
158
        foreach ($definition as $key => $value) {
159
            // Method.
160
            if ($key === ArrayDefinition::CLASS_NAME || $key === ArrayDefinition::CONSTRUCTOR) {
161
                continue;
162
            }
163
            if (substr($key, -2) === '()') {
164
                if (!is_array($value)) {
165
                    throw new InvalidConfigException(
166
                        sprintf('Invalid definition: incorrect method arguments. Expected array, got %s.', self::getType($value))
167
                    );
168
                }
169
                // Not property = meta.
170
            } elseif (strpos($key, '$') !== 0) {
171
                if ($allowedMeta === [] || !in_array($key, $allowedMeta, true)) {
172
                    throw new InvalidConfigException(sprintf('Invalid definition: metadata "%s" is not allowed. Did you mean "%s()" or "$%s"?', $key, $key, $key));
173
                }
174
                $meta[$key] = $value;
175
                unset($definition[$key]);
176
            }
177
        }
178
179
        return [$definition, $meta];
180
    }
181
182
    /**
183
     * @param mixed $value
184
     */
185
    private static function getType($value): string
186
    {
187
        return is_object($value) ? get_class($value) : gettype($value);
188
    }
189
}
190