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
Pull Request — master (#277)
by Jérémiah
20:41
created

GraphQLParser   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 272
Duplicated Lines 7.35 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 50
lcom 1
cbo 5
dl 20
loc 272
rs 8.6206
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
B parse() 0 27 5
A mustOverrideConfig() 0 4 1
C typeDefinitionToConfig() 0 44 7
A addTypeFields() 0 16 3
A addFieldArguments() 0 15 3
A addInterfaces() 10 10 3
A addTypes() 10 10 3
A addValues() 0 11 3
A addType() 0 6 2
A addDescription() 0 9 3
A addDefaultValue() 0 6 2
A astTypeNodeToString() 0 19 4
D astValueNodeToConfig() 0 26 9
A cleanAstDescription() 0 6 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like GraphQLParser 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 GraphQLParser, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Overblog\GraphQLBundle\Config\Parser;
4
5
use GraphQL\Language\AST\FieldDefinitionNode;
6
use GraphQL\Language\AST\InputValueDefinitionNode;
7
use GraphQL\Language\AST\Node;
8
use GraphQL\Language\AST\NodeKind;
9
use GraphQL\Language\AST\TypeNode;
10
use GraphQL\Language\AST\ValueNode;
11
use GraphQL\Language\Parser;
12
use Symfony\Component\Config\Resource\FileResource;
13
use Symfony\Component\DependencyInjection\ContainerBuilder;
14
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
15
16
class GraphQLParser implements ParserInterface
17
{
18
    /** @var self */
19
    private static $parser;
20
21
    const DEFINITION_TYPE_MAPPING = [
22
        NodeKind::OBJECT_TYPE_DEFINITION => 'object',
23
        NodeKind::INTERFACE_TYPE_DEFINITION => 'interface',
24
        NodeKind::ENUM_TYPE_DEFINITION => 'enum',
25
        NodeKind::UNION_TYPE_DEFINITION => 'union',
26
        NodeKind::INPUT_OBJECT_TYPE_DEFINITION => 'input-object',
27
        NodeKind::SCALAR_TYPE_DEFINITION => 'custom-scalar',
28
    ];
29
30
    /**
31
     * {@inheritdoc}
32
     */
33
    public static function parse(\SplFileInfo $file, ContainerBuilder $container)
34
    {
35
        $container->addResource(new FileResource($file->getRealPath()));
36
        $content = trim(file_get_contents($file->getPathname()));
37
        $typesConfig = [];
38
39
        // allow empty files
40
        if (empty($content)) {
41
            return [];
42
        }
43
        if (!self::$parser) {
44
            self::$parser = new static();
45
        }
46
        try {
47
            $ast = Parser::parse($content);
48
        } catch (\Exception $e) {
49
            throw new InvalidArgumentException(sprintf('An error occurred while parsing the file "%s".', $file), $e->getCode(), $e);
50
        }
51
52
        /** @var Node $typeDef */
53
        foreach ($ast->definitions as $typeDef) {
54
            $typeConfig = self::$parser->typeDefinitionToConfig($typeDef);
0 ignored issues
show
Documentation introduced by
$typeDef is of type object<GraphQL\Language\AST\DefinitionNode>, but the function expects a object<GraphQL\Language\AST\Node>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
55
            $typesConfig[$typeDef->name->value] = $typeConfig;
0 ignored issues
show
Bug introduced by
Accessing name on the interface GraphQL\Language\AST\DefinitionNode suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
56
        }
57
58
        return $typesConfig;
59
    }
60
61
    public static function mustOverrideConfig()
62
    {
63
        throw new \RuntimeException('Config entry must be override with ResolverMap to be used.');
64
    }
65
66
    protected function typeDefinitionToConfig(Node $typeDef)
67
    {
68
        switch ($typeDef->kind) {
69
            case NodeKind::OBJECT_TYPE_DEFINITION:
70
            case NodeKind::INTERFACE_TYPE_DEFINITION:
71
            case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
72
            case NodeKind::ENUM_TYPE_DEFINITION:
73
            case NodeKind::UNION_TYPE_DEFINITION:
74
                $config = [];
75
                $this->addTypeFields($typeDef, $config);
76
                $this->addDescription($typeDef, $config);
77
                $this->addInterfaces($typeDef, $config);
78
                $this->addTypes($typeDef, $config);
79
                $this->addValues($typeDef, $config);
80
81
                return [
82
                    'type' => self::DEFINITION_TYPE_MAPPING[$typeDef->kind],
83
                    'config' => $config,
84
                ];
85
86
            case NodeKind::SCALAR_TYPE_DEFINITION:
87
                $mustOverride = [__CLASS__, 'mustOverrideConfig'];
88
                $config = [
89
                    'serialize' => $mustOverride,
90
                    'parseValue' => $mustOverride,
91
                    'parseLiteral' => $mustOverride,
92
                ];
93
                $this->addDescription($typeDef, $config);
94
95
                return [
96
                    'type' => self::DEFINITION_TYPE_MAPPING[$typeDef->kind],
97
                    'config' => $config,
98
                ];
99
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
100
101
            default:
102
                throw new InvalidArgumentException(
103
                    sprintf(
104
                        '%s definition is not supported right now.',
105
                        preg_replace('@Definition$@', '', $typeDef->kind)
106
                    )
107
                );
108
        }
109
    }
110
111
    /**
112
     * @param Node  $typeDef
113
     * @param array $config
114
     */
115
    private function addTypeFields(Node $typeDef, array &$config)
116
    {
117
        if (!empty($typeDef->fields)) {
118
            $fields = [];
119
            /** @var FieldDefinitionNode|InputValueDefinitionNode $fieldDef */
120
            foreach ($typeDef->fields as $fieldDef) {
0 ignored issues
show
Bug introduced by
The property fields does not seem to exist in GraphQL\Language\AST\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
121
                $fieldName = $fieldDef->name->value;
122
                $fields[$fieldName] = [];
123
                $this->addType($fieldDef, $fields[$fieldName]);
124
                $this->addDescription($fieldDef, $fields[$fieldName]);
125
                $this->addDefaultValue($fieldDef, $fields[$fieldName]);
126
                $this->addFieldArguments($fieldDef, $fields[$fieldName]);
127
            }
128
            $config['fields'] = $fields;
129
        }
130
    }
131
132
    /**
133
     * @param Node  $fieldDef
134
     * @param array $fieldConf
135
     */
136
    private function addFieldArguments(Node $fieldDef, array &$fieldConf)
137
    {
138
        if (!empty($fieldDef->arguments)) {
139
            $arguments = [];
140
            /** @var InputValueDefinitionNode $definition */
141
            foreach ($fieldDef->arguments as $definition) {
0 ignored issues
show
Bug introduced by
The property arguments does not seem to exist in GraphQL\Language\AST\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
142
                $name = $definition->name->value;
143
                $arguments[$name] = [];
144
                $this->addType($definition, $arguments[$name]);
145
                $this->addDescription($definition, $arguments[$name]);
146
                $this->addDefaultValue($definition, $arguments[$name]);
147
            }
148
            $fieldConf['args'] = $arguments;
149
        }
150
    }
151
152
    /**
153
     * @param Node  $typeDef
154
     * @param array $config
155
     */
156 View Code Duplication
    private function addInterfaces(Node $typeDef, array &$config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157
    {
158
        if (!empty($typeDef->interfaces)) {
159
            $interfaces = [];
160
            foreach ($typeDef->interfaces as $interface) {
0 ignored issues
show
Bug introduced by
The property interfaces does not seem to exist in GraphQL\Language\AST\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
161
                $interfaces[] = $this->astTypeNodeToString($interface);
162
            }
163
            $config['interfaces'] = $interfaces;
164
        }
165
    }
166
167
    /**
168
     * @param Node  $typeDef
169
     * @param array $config
170
     */
171 View Code Duplication
    private function addTypes(Node $typeDef, array &$config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
172
    {
173
        if (!empty($typeDef->types)) {
174
            $types = [];
175
            foreach ($typeDef->types as $type) {
0 ignored issues
show
Bug introduced by
The property types does not seem to exist in GraphQL\Language\AST\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
176
                $types[] = $this->astTypeNodeToString($type);
177
            }
178
            $config['types'] = $types;
179
        }
180
    }
181
182
    /**
183
     * @param Node  $typeDef
184
     * @param array $config
185
     */
186
    private function addValues(Node $typeDef, array &$config)
187
    {
188
        if (!empty($typeDef->values)) {
189
            $values = [];
190
            foreach ($typeDef->values as $value) {
0 ignored issues
show
Bug introduced by
The property values does not seem to exist in GraphQL\Language\AST\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
191
                $values[$value->name->value] = ['value' => $value->name->value];
192
                $this->addDescription($value, $values[$value->name->value]);
193
            }
194
            $config['values'] = $values;
195
        }
196
    }
197
198
    /**
199
     * @param Node  $definition
200
     * @param array $config
201
     */
202
    private function addType(Node $definition, array &$config)
203
    {
204
        if (!empty($definition->type)) {
205
            $config['type'] = $this->astTypeNodeToString($definition->type);
0 ignored issues
show
Bug introduced by
The property type does not seem to exist in GraphQL\Language\AST\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
206
        }
207
    }
208
209
    /**
210
     * @param Node  $definition
211
     * @param array $config
212
     */
213
    private function addDescription(Node $definition, array &$config)
214
    {
215
        if (
216
            !empty($definition->description)
0 ignored issues
show
Bug introduced by
The property description does not seem to exist in GraphQL\Language\AST\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
217
            && $description = $this->cleanAstDescription($definition->description)
218
        ) {
219
            $config['description'] = $description;
220
        }
221
    }
222
223
    /**
224
     * @param InputValueDefinitionNode|FieldDefinitionNode $definition
225
     * @param array                                        $config
226
     */
227
    private function addDefaultValue($definition, array &$config)
228
    {
229
        if (!empty($definition->defaultValue)) {
230
            $config['defaultValue'] = $this->astValueNodeToConfig($definition->defaultValue);
231
        }
232
    }
233
234
    private function astTypeNodeToString(TypeNode $typeNode)
235
    {
236
        $type = '';
237
        switch ($typeNode->kind) {
0 ignored issues
show
Bug introduced by
Accessing kind on the interface GraphQL\Language\AST\TypeNode suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
238
            case NodeKind::NAMED_TYPE:
239
                $type = $typeNode->name->value;
0 ignored issues
show
Bug introduced by
Accessing name on the interface GraphQL\Language\AST\TypeNode suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
240
                break;
241
242
            case NodeKind::NON_NULL_TYPE:
243
                $type = $this->astTypeNodeToString($typeNode->type).'!';
0 ignored issues
show
Bug introduced by
Accessing type on the interface GraphQL\Language\AST\TypeNode suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
244
                break;
245
246
            case NodeKind::LIST_TYPE:
247
                $type = '['.$this->astTypeNodeToString($typeNode->type).']';
0 ignored issues
show
Bug introduced by
Accessing type on the interface GraphQL\Language\AST\TypeNode suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
248
                break;
249
        }
250
251
        return $type;
252
    }
253
254
    private function astValueNodeToConfig(ValueNode $valueNode)
255
    {
256
        $config = null;
257
        switch ($valueNode->kind) {
0 ignored issues
show
Bug introduced by
Accessing kind on the interface GraphQL\Language\AST\ValueNode suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
258
            case NodeKind::INT:
259
            case NodeKind::FLOAT:
260
            case NodeKind::STRING:
261
            case NodeKind::BOOLEAN:
262
            case NodeKind::ENUM:
263
                $config = $valueNode->value;
0 ignored issues
show
Bug introduced by
Accessing value on the interface GraphQL\Language\AST\ValueNode suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
264
                break;
265
266
            case NodeKind::LST:
267
                $config = [];
268
                foreach ($valueNode->values as $node) {
0 ignored issues
show
Bug introduced by
Accessing values on the interface GraphQL\Language\AST\ValueNode suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
269
                    $config[] = $this->astValueNodeToConfig($node);
270
                }
271
                break;
272
273
            case NodeKind::NULL:
274
                $config = null;
275
                break;
276
        }
277
278
        return $config;
279
    }
280
281
    private function cleanAstDescription($description)
282
    {
283
        $description = trim($description);
284
285
        return empty($description) ? null : $description;
286
    }
287
}
288