Completed
Push — master ( 89600f...8b2509 )
by Maxime
01:55 queued 11s
created

Configuration   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 3
dl 0
loc 187
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getConfigTreeBuilder() 0 18 3
B addDoctrineSection() 0 73 6
A addTranslationExtractorSection() 0 29 1
A addJsEnumsSection() 0 32 1
A hasNonEnumKeys() 0 11 3
A throwsNonEnumKeysException() 0 16 3
1
<?php
2
3
/*
4
 * This file is part of the "elao/enum" package.
5
 *
6
 * Copyright (C) Elao
7
 *
8
 * @author Elao <[email protected]>
9
 */
10
11
namespace Elao\Enum\Bridge\Symfony\Bundle\DependencyInjection;
12
13
use Elao\Enum\EnumInterface;
14
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
15
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
16
use Symfony\Component\Config\Definition\ConfigurationInterface;
17
use Symfony\Component\Serializer\SerializerInterface;
18
19
class Configuration implements ConfigurationInterface
20
{
21
    public function getConfigTreeBuilder()
22
    {
23
        $treeBuilder = new TreeBuilder('elao_enum');
24
        $rootNode = method_exists(TreeBuilder::class, 'getRootNode') ? $treeBuilder->getRootNode() : $treeBuilder->root('elao_enum');
0 ignored issues
show
Bug introduced by
The method root() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
25
26
        $rootNode->children()
27
            ->arrayNode('argument_value_resolver')->canBeDisabled()->end()
28
            ->arrayNode('serializer')
29
                ->{interface_exists(SerializerInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
30
            ->end()
31
        ->end();
32
33
        $this->addDoctrineSection($rootNode);
34
        $this->addTranslationExtractorSection($rootNode);
35
        $this->addJsEnumsSection($rootNode);
36
37
        return $treeBuilder;
38
    }
39
40
    private function addDoctrineSection(ArrayNodeDefinition $rootNode)
41
    {
42
        $rootNode->children()
43
            ->arrayNode('doctrine')
44
            ->addDefaultsIfNotSet()
45
            ->fixXmlConfig('type')
46
            ->children()
47
                ->booleanNode('enum_sql_declaration')
48
                    ->defaultValue(false)
49
                    ->info('If true, by default for string enumerations, generate DBAL types with an ENUM SQL declaration with enum values instead of a VARCHAR (Your platform must support it)')
50
                ->end()
51
                ->arrayNode('types')
52
                    ->beforeNormalization()
53
                        ->always(function ($values) {
54
                            $legacyFormat = false;
55
                            foreach ($values as $name => $config) {
56
                                if (class_exists($name)) {
57
                                    $legacyFormat = true;
58
                                    @trigger_error('Using enum FQCN as keys at path "elao_enum.doctrine.types" is deprecated. Provide the name as keys and add the "class" option for each entry instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
59
                                    break;
60
                                }
61
                            }
62
                            $newValues = [];
63
64
                            if ($legacyFormat) {
65
                                foreach ($values as $name => $value) {
66
                                    if (\is_string($value)) {
67
                                        $newValues[$value] = $name;
68
                                    } else {
69
                                        $newValues[$value['name']] = $value + ['class' => $name];
70
                                    }
71
                                }
72
                            } else {
73
                                $newValues = $values;
74
                            }
75
76
                            return $newValues;
77
                        })
78
                    ->end()
79
                    ->useAttributeAsKey('name')
80
                    ->arrayPrototype()
81
                    ->beforeNormalization()
82
                        ->ifString()->then(static function (string $v): array { return ['class' => $v]; })
83
                    ->end()
84
                    ->children()
85
                        ->scalarNode('class')
86
                            ->cannotBeEmpty()
87
                            ->validate()
88
                                ->ifTrue(static function (string $class): bool {return !is_a($class, EnumInterface::class, true); })
89
                                ->thenInvalid(sprintf('Invalid class. Expected instance of "%s"', EnumInterface::class))
90
                            ->end()
91
                        ->end()
92
                        ->enumNode('type')
93
                            ->values(['enum', 'string', 'int', 'collection'])
94
                            ->info(<<<TXT
95
Which column definition to use and the way the enumeration values are stored in the database:
96
- string: VARCHAR
97
- enum: ENUM(...values) (Your platform must support it)
98
- int: INT
99
- collection: JSON
100
101
Default is either "string" or "enum", controlled by the `elao_enum.doctrine.enum_sql_declaration` option.
102
Default for flagged enums is "int".
103
TXT
104
                            )
105
                            ->cannotBeEmpty()
106
                            ->defaultValue(null)
107
                        ->end()
108
                    ->end()
109
                ->end()
110
            ->end()
111
        ->end();
112
    }
113
114
    private function addTranslationExtractorSection(ArrayNodeDefinition $rootNode)
115
    {
116
        $rootNode->children()
117
            ->arrayNode('translation_extractor')
118
            ->canBeEnabled()
119
            ->fixXmlConfig('path')
120
            ->children()
121
                ->arrayNode('paths')
122
                    ->example(['App\Enum' => '%kernel.project_dir%/src/Enum'])
123
                    ->useAttributeAsKey('namespace')
124
                    ->scalarPrototype()
125
                    ->end()
126
                ->end()
127
                ->scalarNode('domain')
128
                    ->defaultValue('messages')
129
                    ->cannotBeEmpty()
130
                ->end()
131
                ->scalarNode('filename_pattern')
132
                    ->example('*Enum.php')
133
                    ->defaultValue('*.php')
134
                    ->cannotBeEmpty()
135
                ->end()
136
                ->arrayNode('ignore')
137
                    ->example(['%kernel.project_dir%/src/Enum/Other/*'])
138
                    ->scalarPrototype()->cannotBeEmpty()->end()
139
                ->end()
140
            ->end()
141
        ->end();
142
    }
143
144
    private function addJsEnumsSection(ArrayNodeDefinition $rootNode)
145
    {
146
        $rootNode->children()
147
            ->arrayNode('js')
148
                ->addDefaultsIfNotSet()
149
                ->fixXmlConfig('path')
150
                ->children()
151
                    ->scalarNode('base_dir')
152
                        ->info('A prefixed dir used for relative paths supplied for each of the generated enums and library path')
153
                        ->example('%kernel.project_dir%/assets/js/modules')
154
                        ->defaultNull()
155
                    ->end()
156
                    ->scalarNode('lib_path')
157
                        ->info('The path of the file were to place the javascript library sources used by the dumped enums.')
158
                        ->example('%kernel.project_dir%/assets/js/lib/enum.js')
159
                        ->defaultNull()
160
                    ->end()
161
                    ->arrayNode('paths')
162
                        ->defaultValue([])
163
                        ->info('Path where to generate the javascript enums per enum class')
164
                        ->example(['App\Enum\SimpleEnum' => '"common/SimpleEnum.js"'])
165
                        ->useAttributeAsKey('class')
166
                        ->validate()
167
                            ->ifTrue(static function (array $v): bool {return self::hasNonEnumKeys($v); })
168
                            ->then(static function (array $v) { self::throwsNonEnumKeysException($v); })
169
                        ->end()
170
                        ->scalarPrototype()->end()
171
                    ->end()
172
                ->end()
173
            ->end()
174
        ->end();
175
    }
176
177
    private static function hasNonEnumKeys(array $values): bool
178
    {
179
        $classes = array_column($values, 'class');
180
        foreach ($classes as $class) {
181
            if (!is_a($class, EnumInterface::class, true)) {
182
                return true;
183
            }
184
        }
185
186
        return false;
187
    }
188
189
    private static function throwsNonEnumKeysException(array $values)
190
    {
191
        $classes = array_column($values, 'class');
192
        $invalids = [];
193
        foreach ($classes as $class) {
194
            if (!is_a($class, EnumInterface::class, true)) {
195
                $invalids[] = $class;
196
            }
197
        }
198
199
        throw new \InvalidArgumentException(sprintf(
200
            'Invalid classes %s. Expected instances of "%s"',
201
            json_encode($invalids),
202
            EnumInterface::class
203
        ));
204
    }
205
}
206