Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Push — master ( 26bc07...3d1c69 )
by Jérémiah
18:09
created

BuilderProcessor   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 268
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 41
eloc 133
dl 0
loc 268
ccs 119
cts 119
cp 1
rs 9.1199
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A checkBuilderClass() 0 23 4
B process() 0 38 7
A addBuilderClass() 0 4 1
A processFieldsBuilders() 0 18 2
A getBuilder() 0 14 3
B processFieldArgumentsBuilders() 0 23 9
B processFieldBuilders() 0 35 9
B getFieldBuilderMapping() 0 37 6

How to fix   Complexity   

Complex Class

Complex classes like BuilderProcessor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BuilderProcessor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\Config\Processor;
6
7
use Overblog\GraphQLBundle\Definition\Builder\MappingInterface;
8
use Overblog\GraphQLBundle\Relay\Builder\RelayConnectionFieldsBuilder;
9
use Overblog\GraphQLBundle\Relay\Builder\RelayEdgeFieldsBuilder;
10
use Overblog\GraphQLBundle\Relay\Connection\BackwardConnectionArgsDefinition;
11
use Overblog\GraphQLBundle\Relay\Connection\ConnectionArgsDefinition;
12
use Overblog\GraphQLBundle\Relay\Connection\ForwardConnectionArgsDefinition;
13
use Overblog\GraphQLBundle\Relay\Mutation\MutationFieldDefinition;
14
use Overblog\GraphQLBundle\Relay\Node\GlobalIdFieldDefinition;
15
use Overblog\GraphQLBundle\Relay\Node\NodeFieldDefinition;
16
use Overblog\GraphQLBundle\Relay\Node\PluralIdentifyingRootFieldDefinition;
17
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
18
19
final class BuilderProcessor implements ProcessorInterface
20
{
21
    public const BUILDER_FIELD_TYPE = 'field';
22
    public const BUILDER_FIELDS_TYPE = 'fields';
23
    public const BUILDER_ARGS_TYPE = 'args';
24
25
    public const BUILDER_TYPES = [
26
        self::BUILDER_FIELD_TYPE,
27
        self::BUILDER_FIELDS_TYPE,
28
        self::BUILDER_ARGS_TYPE,
29
    ];
30
31
    /** @var MappingInterface[] */
32
    private static $builderClassMap = [
33
        self::BUILDER_ARGS_TYPE => [
34
            'Relay::ForwardConnection' => ForwardConnectionArgsDefinition::class,
35
            'Relay::BackwardConnection' => BackwardConnectionArgsDefinition::class,
36
            'Relay::Connection' => ConnectionArgsDefinition::class,
37
        ],
38
        self::BUILDER_FIELD_TYPE => [
39
            'Relay::Mutation' => MutationFieldDefinition::class,
40
            'Relay::GlobalId' => GlobalIdFieldDefinition::class,
41
            'Relay::Node' => NodeFieldDefinition::class,
42
            'Relay::PluralIdentifyingRoot' => PluralIdentifyingRootFieldDefinition::class,
43
        ],
44
        self::BUILDER_FIELDS_TYPE => [
45
            'relay-connection' => RelayConnectionFieldsBuilder::class,
46
            'relay-edge' => RelayEdgeFieldsBuilder::class,
47
        ],
48
    ];
49
50
    /**
51
     * {@inheritdoc}
52
     */
53 43
    public static function process(array $configs): array
54
    {
55 43
        $addedTypes = [];
56
        // map: "type name" => "provided by" for better DX, while debugging accidental type overrides in builders
57 43
        $reservedTypesMap = \array_combine(
58 43
            \array_keys($configs),
59 43
            \array_fill(0, \count($configs), 'configs')
60
        );
61
62 43
        foreach ($configs as &$config) {
63 43
            if (isset($config['config']['builders']) && \is_array($config['config']['builders'])) {
64 3
                ['fields' => $buildersFields, 'types' => $buildersTypes] = self::processFieldsBuilders(
65 3
                    $config['config']['builders'],
66 3
                    $reservedTypesMap
0 ignored issues
show
Bug introduced by
It seems like $reservedTypesMap can also be of type false; however, parameter $reservedTypesMap of Overblog\GraphQLBundle\C...processFieldsBuilders() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

66
                    /** @scrutinizer ignore-type */ $reservedTypesMap
Loading history...
67
                );
68
69 2
                $config['config']['fields'] = isset($config['config']['fields'])
70 1
                    ? \array_merge($buildersFields, $config['config']['fields'])
71 2
                    : $buildersFields;
72
73 2
                $addedTypes = \array_merge($addedTypes, $buildersTypes);
74
75 2
                unset($config['config']['builders']);
76
            }
77
78 42
            if (isset($config['config']['fields']) && \is_array($config['config']['fields'])) {
79 37
                ['fields' => $buildersFields, 'types' => $buildersTypes] = self::processFieldBuilders(
80 37
                    $config['config']['fields'],
81 37
                    $reservedTypesMap
82
                );
83
84 33
                $config['config']['fields'] = $buildersFields;
85
86 33
                $addedTypes = \array_merge($addedTypes, $buildersTypes);
87
            }
88
        }
89
90 37
        return \array_merge($configs, $addedTypes);
91
    }
92
93 15
    public static function addBuilderClass($name, $type, $builderClass): void
94
    {
95 15
        self::checkBuilderClass($builderClass, $type);
96 5
        self::$builderClassMap[$type][$name] = $builderClass;
97 5
    }
98
99
    /**
100
     * @param string $builderClass
101
     * @param string $type
102
     */
103 15
    private static function checkBuilderClass($builderClass, $type): void
104
    {
105 15
        $interface = MappingInterface::class;
106
107 15
        if (!\is_string($builderClass)) {
0 ignored issues
show
introduced by
The condition is_string($builderClass) is always true.
Loading history...
108 5
            throw new \InvalidArgumentException(
109 5
                \sprintf('%s builder class should be string, but %s given.', \ucfirst($type), \gettype($builderClass))
110
            );
111
        }
112
113 10
        if (!\class_exists($builderClass)) {
114 2
            throw new \InvalidArgumentException(
115 2
                \sprintf('%s builder class "%s" not found.', \ucfirst($type), $builderClass)
116
            );
117
        }
118
119 8
        if (!\is_subclass_of($builderClass, $interface)) {
120 3
            throw new \InvalidArgumentException(
121 3
                \sprintf(
122 3
                    '%s builder class should implement "%s", but "%s" given.',
123 3
                    \ucfirst($type),
124 3
                    $interface,
125 3
                    $builderClass
126
                )
127
            );
128
        }
129 5
    }
130
131 37
    private static function processFieldBuilders(array $fields, array &$reservedTypesMap)
132
    {
133 37
        $newTypes = [];
134
135 37
        foreach ($fields as &$field) {
136 37
            $fieldBuilderName = null;
137
138 37
            if (isset($field['builder']) && \is_string($field['builder'])) {
139 11
                $fieldBuilderName = $field['builder'];
140 11
                unset($field['builder']);
141
            }
142
143 37
            $builderConfig = [];
144 37
            if (isset($field['builderConfig'])) {
145 10
                if (\is_array($field['builderConfig'])) {
146 10
                    $builderConfig = $field['builderConfig'];
147
                }
148 10
                unset($field['builderConfig']);
149
            }
150
151 37
            if ($fieldBuilderName) {
152 11
                $mapping = self::getFieldBuilderMapping($fieldBuilderName, self::BUILDER_FIELD_TYPE, $builderConfig, $reservedTypesMap);
153
154 9
                $fieldMapping = $mapping['field'];
155 9
                $field = \is_array($field) ? \array_merge($fieldMapping, $field) : $fieldMapping;
156 9
                $newTypes = \array_merge($newTypes, $mapping['types']);
157
            }
158 35
            if (isset($field['argsBuilder'])) {
159 15
                $field = self::processFieldArgumentsBuilders($field);
160
            }
161
        }
162
163
        return [
164 33
            'fields' => $fields,
165 33
            'types' => $newTypes,
166
        ];
167
    }
168
169 3
    private static function processFieldsBuilders(array $builders, array &$reservedTypesMap)
170
    {
171 3
        $fields = [];
172 3
        $newTypes = [];
173
174 3
        foreach ($builders as $builder) {
175 3
            $builderName = $builder['builder'];
176 3
            $builderConfig = $builder['builderConfig'] ?? [];
177
178 3
            $mapping = self::getFieldBuilderMapping($builderName, self::BUILDER_FIELDS_TYPE, $builderConfig, $reservedTypesMap);
179
180 2
            $fields = \array_merge($fields, $mapping['fields']);
181 2
            $newTypes = \array_merge($newTypes, $mapping['types']);
182
        }
183
184
        return [
185 2
            'fields' => $fields,
186 2
            'types' => $newTypes,
187
        ];
188
    }
189
190
    /**
191
     * @param string $builderName
192
     * @param string $builderType
193
     * @param array  $builderConfig
194
     * @param array  $reservedTypesMap
195
     *
196
     * @return array
197
     *
198
     * @throws InvalidConfigurationException
199
     */
200 13
    private static function getFieldBuilderMapping(string $builderName, string $builderType, array $builderConfig, array &$reservedTypesMap)
201
    {
202 13
        $builder = self::getBuilder($builderName, $builderType);
203 12
        $mapping = $builder->toMappingDefinition($builderConfig);
204
205 12
        $fieldMappingKey = null;
206
207 12
        if (self::BUILDER_FIELD_TYPE === $builderType) {
208 10
            $fieldMappingKey = 'field';
209 3
        } elseif (self::BUILDER_FIELDS_TYPE === $builderType) {
210 3
            $fieldMappingKey = 'fields';
211
        }
212
213 12
        $fieldMapping = $mapping[$fieldMappingKey] ?? $mapping;
214 12
        $typesMapping = [];
215
216 12
        if (isset($mapping[$fieldMappingKey], $mapping['types'])) {
217 5
            $builderClass = \get_class($builder);
218
219 5
            foreach ($mapping['types'] as $typeName => $typeConfig) {
220 5
                if (isset($reservedTypesMap[$typeName])) {
221 4
                    throw new InvalidConfigurationException(\sprintf(
222 4
                        'Type "%s" emitted by builder "%s" already exists. Type was provided by "%s". Builder may only emit new types. Overriding is not allowed.',
223 4
                        $typeName,
224 4
                        $builderClass,
225 4
                        $reservedTypesMap[$typeName]
226
                    ));
227
                }
228
229 3
                $reservedTypesMap[$typeName] = $builderClass;
230 3
                $typesMapping[$typeName] = $typeConfig;
231
            }
232
        }
233
234
        return [
235 10
            $fieldMappingKey => $fieldMapping,
236 10
            'types' => $typesMapping,
237
        ];
238
    }
239
240
    /**
241
     * @param string $name
242
     * @param string $type
243
     *
244
     * @return MappingInterface
245
     *
246
     * @throws InvalidConfigurationException if builder class not define
247
     */
248 26
    private static function getBuilder($name, $type)
249
    {
250 26
        static $builders = [];
251 26
        if (isset($builders[$type][$name])) {
252 22
            return $builders[$type][$name];
253
        }
254
255 11
        $builderClassMap = self::$builderClassMap[$type];
256
257 11
        if (isset($builderClassMap[$name])) {
258 9
            return $builders[$type][$name] = new $builderClassMap[$name]();
259
        }
260
261 2
        throw new InvalidConfigurationException(\sprintf('%s builder "%s" not found.', \ucfirst($type), $name));
262
    }
263
264 15
    private static function processFieldArgumentsBuilders(array $field)
265
    {
266 15
        $argsBuilderName = null;
267
268 15
        if (\is_string($field['argsBuilder'])) {
269 15
            $argsBuilderName = $field['argsBuilder'];
270 1
        } elseif (isset($field['argsBuilder']['builder']) && \is_string($field['argsBuilder']['builder'])) {
271 1
            $argsBuilderName = $field['argsBuilder']['builder'];
272
        }
273
274 15
        $builderConfig = [];
275 15
        if (isset($field['argsBuilder']['config']) && \is_array($field['argsBuilder']['config'])) {
276 1
            $builderConfig = $field['argsBuilder']['config'];
277
        }
278
279 15
        if ($argsBuilderName) {
280 15
            $args = self::getBuilder($argsBuilderName, self::BUILDER_ARGS_TYPE)->toMappingDefinition($builderConfig);
281 14
            $field['args'] = isset($field['args']) && \is_array($field['args']) ? \array_merge($args, $field['args']) : $args;
282
        }
283
284 14
        unset($field['argsBuilder']);
285
286 14
        return $field;
287
    }
288
}
289