Failed Conditions
Push — master ( a4f39b...12ee90 )
by Vladimir
11:17
created

KnownDirectives::getASTVisitor()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 59
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 8

Importance

Changes 0
Metric Value
eloc 39
c 0
b 0
f 0
dl 0
loc 59
ccs 32
cts 32
cp 1
rs 8.0515
cc 8
nc 12
nop 1
crap 8

How to fix   Long Method   

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 GraphQL\Validator\Rules;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Language\AST\DirectiveDefinitionNode;
9
use GraphQL\Language\AST\DirectiveNode;
10
use GraphQL\Language\AST\EnumTypeDefinitionNode;
11
use GraphQL\Language\AST\EnumTypeExtensionNode;
12
use GraphQL\Language\AST\EnumValueDefinitionNode;
13
use GraphQL\Language\AST\FieldDefinitionNode;
14
use GraphQL\Language\AST\FieldNode;
15
use GraphQL\Language\AST\FragmentDefinitionNode;
16
use GraphQL\Language\AST\FragmentSpreadNode;
17
use GraphQL\Language\AST\InlineFragmentNode;
18
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
19
use GraphQL\Language\AST\InputObjectTypeExtensionNode;
20
use GraphQL\Language\AST\InputValueDefinitionNode;
21
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
22
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
23
use GraphQL\Language\AST\Node;
24
use GraphQL\Language\AST\NodeKind;
25
use GraphQL\Language\AST\NodeList;
26
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
27
use GraphQL\Language\AST\ObjectTypeExtensionNode;
28
use GraphQL\Language\AST\OperationDefinitionNode;
29
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
30
use GraphQL\Language\AST\ScalarTypeExtensionNode;
31
use GraphQL\Language\AST\SchemaDefinitionNode;
32
use GraphQL\Language\AST\SchemaTypeExtensionNode;
33
use GraphQL\Language\AST\UnionTypeDefinitionNode;
34
use GraphQL\Language\AST\UnionTypeExtensionNode;
35
use GraphQL\Language\AST\VariableDefinitionNode;
36
use GraphQL\Language\DirectiveLocation;
37
use GraphQL\Type\Definition\Directive;
38
use GraphQL\Validator\ASTValidationContext;
39
use GraphQL\Validator\SDLValidationContext;
40
use GraphQL\Validator\ValidationContext;
41
use function array_map;
42
use function count;
43
use function in_array;
44
use function sprintf;
45
46
class KnownDirectives extends ValidationRule
47
{
48 124
    public function getVisitor(ValidationContext $context)
49
    {
50 124
        return $this->getASTVisitor($context);
51
    }
52
53 207
    public function getSDLVisitor(SDLValidationContext $context)
54
    {
55 207
        return $this->getASTVisitor($context);
56
    }
57
58 316
    public function getASTVisitor(ASTValidationContext $context)
59
    {
60 316
        $locationsMap      = [];
61 316
        $schema            = $context->getSchema();
62 316
        $definedDirectives = $schema
0 ignored issues
show
introduced by
$schema is of type GraphQL\Type\Schema, thus it always evaluated to true.
Loading history...
63 191
            ? $schema->getDirectives()
64 316
            : Directive::getInternalDirectives();
65
66 316
        foreach ($definedDirectives as $directive) {
67 316
            $locationsMap[$directive->name] = $directive->locations;
68
        }
69
70 316
        $astDefinition = $context->getDocument()->definitions;
71
72 316
        foreach ($astDefinition as $def) {
73 316
            if (! ($def instanceof DirectiveDefinitionNode)) {
74 310
                continue;
75
            }
76
77 37
            $locationsMap[$def->name->value] = array_map(
78
                static function ($name) {
79 37
                    return $name->value;
80 37
                },
81 37
                $def->locations
82
            );
83
        }
84
85
        return [
86
            NodeKind::DIRECTIVE => function (
87
                DirectiveNode $node,
88
                $key,
89
                $parent,
90
                $path,
91
                $ancestors
92
            ) use (
93 32
                $context,
94 32
                $locationsMap
95
            ) {
96 32
                $name      = $node->name->value;
97 32
                $locations = $locationsMap[$name] ?? null;
98
99 32
                if (! $locations) {
100 5
                    $context->reportError(new Error(
101 5
                        self::unknownDirectiveMessage($name),
102 5
                        [$node]
103
                    ));
104
105 5
                    return;
106
                }
107
108 27
                $candidateLocation = $this->getDirectiveLocationForASTPath($ancestors);
109
110 27
                if (! $candidateLocation || in_array($candidateLocation, $locations, true)) {
111 25
                    return;
112
                }
113 3
                $context->reportError(
114 3
                    new Error(
115 3
                        self::misplacedDirectiveMessage($name, $candidateLocation),
116 3
                        [$node]
117
                    )
118
                );
119 316
            },
120
        ];
121
    }
122
123 5
    public static function unknownDirectiveMessage($directiveName)
124
    {
125 5
        return sprintf('Unknown directive "%s".', $directiveName);
126
    }
127
128
    /**
129
     * @param Node[]|NodeList[] $ancestors The type is actually (Node|NodeList)[] but this PSR-5 syntax is so far not supported by most of the tools
130
     *
131
     * @return string
132
     */
133 27
    private function getDirectiveLocationForASTPath(array $ancestors)
134
    {
135 27
        $appliedTo = $ancestors[count($ancestors) - 1];
136
        switch (true) {
137 27
            case $appliedTo instanceof OperationDefinitionNode:
138 2
                switch ($appliedTo->operation) {
139 2
                    case 'query':
140 2
                        return DirectiveLocation::QUERY;
141 2
                    case 'mutation':
142 2
                        return DirectiveLocation::MUTATION;
143
                    case 'subscription':
144
                        return DirectiveLocation::SUBSCRIPTION;
145
                }
146
                break;
147 27
            case $appliedTo instanceof FieldNode:
148 3
                return DirectiveLocation::FIELD;
149 26
            case $appliedTo instanceof FragmentSpreadNode:
150 2
                return DirectiveLocation::FRAGMENT_SPREAD;
151 24
            case $appliedTo instanceof InlineFragmentNode:
152
                return DirectiveLocation::INLINE_FRAGMENT;
153 24
            case $appliedTo instanceof FragmentDefinitionNode:
154
                return DirectiveLocation::FRAGMENT_DEFINITION;
155 24
            case $appliedTo instanceof VariableDefinitionNode:
156 2
                return DirectiveLocation::VARIABLE_DEFINITION;
157 22
            case $appliedTo instanceof SchemaDefinitionNode:
158 20
            case $appliedTo instanceof SchemaTypeExtensionNode:
159 6
                return DirectiveLocation::SCHEMA;
160 19
            case $appliedTo instanceof ScalarTypeDefinitionNode:
161 19
            case $appliedTo instanceof ScalarTypeExtensionNode:
162 6
                return DirectiveLocation::SCALAR;
163 17
            case $appliedTo instanceof ObjectTypeDefinitionNode:
164 17
            case $appliedTo instanceof ObjectTypeExtensionNode:
165 8
                return DirectiveLocation::OBJECT;
166 14
            case $appliedTo instanceof FieldDefinitionNode:
167 8
                return DirectiveLocation::FIELD_DEFINITION;
168 11
            case $appliedTo instanceof InterfaceTypeDefinitionNode:
169 11
            case $appliedTo instanceof InterfaceTypeExtensionNode:
170 5
                return DirectiveLocation::IFACE;
171 11
            case $appliedTo instanceof UnionTypeDefinitionNode:
172 11
            case $appliedTo instanceof UnionTypeExtensionNode:
173 6
                return DirectiveLocation::UNION;
174 10
            case $appliedTo instanceof EnumTypeDefinitionNode:
175 10
            case $appliedTo instanceof EnumTypeExtensionNode:
176 6
                return DirectiveLocation::ENUM;
177 9
            case $appliedTo instanceof EnumValueDefinitionNode:
178 6
                return DirectiveLocation::ENUM_VALUE;
179 6
            case $appliedTo instanceof InputObjectTypeDefinitionNode:
180 6
            case $appliedTo instanceof InputObjectTypeExtensionNode:
181 6
                return DirectiveLocation::INPUT_OBJECT;
182 3
            case $appliedTo instanceof InputValueDefinitionNode:
183 3
                $parentNode = $ancestors[count($ancestors) - 3];
184
185 3
                return $parentNode instanceof InputObjectTypeDefinitionNode
186 3
                    ? DirectiveLocation::INPUT_FIELD_DEFINITION
187 3
                    : DirectiveLocation::ARGUMENT_DEFINITION;
188
        }
189
    }
190
191 3
    public static function misplacedDirectiveMessage($directiveName, $location)
192
    {
193 3
        return sprintf('Directive "%s" may not be used on "%s".', $directiveName, $location);
194
    }
195
}
196