Passed
Pull Request — master (#80)
by Dmitriy
02:41
created

Normalizer::parse()   C

Complexity

Conditions 12
Paths 15

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 12.0104

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 23
c 1
b 0
f 0
dl 0
loc 38
ccs 23
cts 24
cp 0.9583
rs 6.9666
cc 12
nc 15
nop 2
crap 12.0104

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