Completed
Pull Request — master (#45)
by Christoffer
02:10
created

getDirectiveLocationFromASTPath()   C

Complexity

Conditions 26
Paths 35

Size

Total Lines 61
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 26
eloc 41
nc 35
nop 1
dl 0
loc 61
rs 6.0372
c 0
b 0
f 0

How to fix   Long Method    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
namespace Digia\GraphQL\Validation\Rule;
4
5
use Digia\GraphQL\Error\ValidationException;
6
use Digia\GraphQL\Language\AST\DirectiveLocationEnum;
7
use Digia\GraphQL\Language\AST\Node\DirectiveNode;
8
use Digia\GraphQL\Language\AST\Node\EnumTypeDefinitionNode;
9
use Digia\GraphQL\Language\AST\Node\EnumTypeExtensionNode;
10
use Digia\GraphQL\Language\AST\Node\EnumValueDefinitionNode;
11
use Digia\GraphQL\Language\AST\Node\FieldDefinitionNode;
12
use Digia\GraphQL\Language\AST\Node\FieldNode;
13
use Digia\GraphQL\Language\AST\Node\FragmentDefinitionNode;
14
use Digia\GraphQL\Language\AST\Node\FragmentSpreadNode;
15
use Digia\GraphQL\Language\AST\Node\InlineFragmentNode;
16
use Digia\GraphQL\Language\AST\Node\InputObjectTypeDefinitionNode;
17
use Digia\GraphQL\Language\AST\Node\InputObjectTypeExtensionNode;
18
use Digia\GraphQL\Language\AST\Node\InputValueDefinitionNode;
19
use Digia\GraphQL\Language\AST\Node\InterfaceTypeDefinitionNode;
20
use Digia\GraphQL\Language\AST\Node\InterfaceTypeExtensionNode;
21
use Digia\GraphQL\Language\AST\Node\NodeInterface;
22
use Digia\GraphQL\Language\AST\Node\ObjectTypeDefinitionNode;
23
use Digia\GraphQL\Language\AST\Node\ObjectTypeExtensionNode;
24
use Digia\GraphQL\Language\AST\Node\OperationDefinitionNode;
25
use Digia\GraphQL\Language\AST\Node\ScalarTypeDefinitionNode;
26
use Digia\GraphQL\Language\AST\Node\ScalarTypeExtensionNode;
27
use Digia\GraphQL\Language\AST\Node\SchemaDefinitionNode;
28
use Digia\GraphQL\Language\AST\Node\UnionTypeDefinitionNode;
29
use Digia\GraphQL\Language\AST\Node\UnionTypeExtensionNode;
30
use Digia\GraphQL\Language\AST\Visitor\AcceptVisitorTrait;
31
use Digia\GraphQL\Type\Definition\Directive;
32
use function Digia\GraphQL\Util\find;
33
34
/**
35
 * Known directives
36
 *
37
 * A GraphQL document is only valid if all `@directives` are known by the
38
 * schema and legally positioned.
39
 */
40
class KnownDirectivesRule extends AbstractRule
41
{
42
    /**
43
     * @inheritdoc
44
     */
45
    public function enterNode(NodeInterface $node): ?NodeInterface
46
    {
47
        if ($node instanceof DirectiveNode) {
48
            /** @var Directive $directiveDefinition */
49
            $directiveDefinition = find(
50
                $this->context->getSchema()->getDirectives(),
51
                function (Directive $definition) use ($node) {
52
                    return $definition->getName() === $node->getNameValue();
53
                }
54
            );
55
56
            if (null == $directiveDefinition) {
57
                $this->context->reportError(
58
                    new ValidationException(unknownDirectiveMessage((string)$node), [$node])
59
                );
60
61
                return $node;
62
            }
63
64
            $location = getDirectiveLocationFromASTPath($node);
65
66
            if (null !== $location && !in_array($location, $directiveDefinition->getLocations())) {
67
                $this->context->reportError(
68
                    new ValidationException(misplacedDirectiveMessage((string)$node, $location), [$node])
69
                );
70
            }
71
        }
72
73
        return $node;
74
    }
75
}
76
77
/**
78
 * @param NodeInterface|AcceptVisitorTrait $node
79
 * @return string
80
 */
81
function getDirectiveLocationFromASTPath(NodeInterface $node): string
82
{
83
    /** @var NodeInterface $appliedTo */
84
    $appliedTo = $node->getAncestor();
0 ignored issues
show
Bug introduced by
The method getAncestor() does not exist on Digia\GraphQL\Language\AST\Node\NodeInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Digia\GraphQL\Language\A...DefinitionNodeInterface or Digia\GraphQL\Language\A...eExtensionNodeInterface or Digia\GraphQL\Language\AST\Node\ValueNodeInterface or Digia\GraphQL\Language\A...\SelectionNodeInterface or Digia\GraphQL\Language\AST\Node\TypeNodeInterface or Digia\GraphQL\Language\A...DefinitionNodeInterface or Digia\GraphQL\Language\A...DefinitionNodeInterface or Digia\GraphQL\Language\A...DefinitionNodeInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

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

84
    /** @scrutinizer ignore-call */ 
85
    $appliedTo = $node->getAncestor();
Loading history...
85
86
    if ($appliedTo instanceof OperationDefinitionNode) {
87
        switch ($appliedTo->getOperation()) {
88
            case 'query':
89
                return DirectiveLocationEnum::QUERY;
90
            case 'mutation':
91
                return DirectiveLocationEnum::MUTATION;
92
            case 'subscription':
93
                return DirectiveLocationEnum::SUBSCRIPTION;
94
            default:
95
                // TODO: Throw appropriate exception.
96
        }
97
    }
98
    if ($appliedTo instanceof FieldNode) {
99
        return DirectiveLocationEnum::FIELD;
100
    }
101
    if ($appliedTo instanceof FragmentSpreadNode) {
102
        return DirectiveLocationEnum::FRAGMENT_SPREAD;
103
    }
104
    if ($appliedTo instanceof InlineFragmentNode) {
105
        return DirectiveLocationEnum::INLINE_FRAGMENT;
106
    }
107
    if ($appliedTo instanceof FragmentDefinitionNode) {
108
        return DirectiveLocationEnum::FRAGMENT_DEFINITION;
109
    }
110
    if ($appliedTo instanceof SchemaDefinitionNode) {
111
        return DirectiveLocationEnum::SCHEMA;
112
    }
113
    if ($appliedTo instanceof ScalarTypeDefinitionNode || $appliedTo instanceof ScalarTypeExtensionNode) {
114
        return DirectiveLocationEnum::SCALAR;
115
    }
116
    if ($appliedTo instanceof ObjectTypeDefinitionNode || $appliedTo instanceof ObjectTypeExtensionNode) {
117
        return DirectiveLocationEnum::OBJECT;
118
    }
119
    if ($appliedTo instanceof FieldDefinitionNode) {
120
        return DirectiveLocationEnum::FIELD_DEFINITION;
121
    }
122
    if ($appliedTo instanceof InterfaceTypeDefinitionNode || $appliedTo instanceof InterfaceTypeExtensionNode) {
123
        return DirectiveLocationEnum::INTERFACE;
124
    }
125
    if ($appliedTo instanceof UnionTypeDefinitionNode || $appliedTo instanceof UnionTypeExtensionNode) {
126
        return DirectiveLocationEnum::UNION;
127
    }
128
    if ($appliedTo instanceof EnumTypeDefinitionNode || $appliedTo instanceof EnumTypeExtensionNode) {
129
        return DirectiveLocationEnum::ENUM;
130
    }
131
    if ($appliedTo instanceof EnumValueDefinitionNode) {
132
        return DirectiveLocationEnum::ENUM_VALUE;
133
    }
134
    if ($appliedTo instanceof InputObjectTypeDefinitionNode || $appliedTo instanceof InputObjectTypeExtensionNode) {
135
        return DirectiveLocationEnum::INPUT_OBJECT;
136
    }
137
    if ($appliedTo instanceof InputValueDefinitionNode) {
138
        $parentNode = $node->getAncestor(2);
139
        return $parentNode instanceof InputObjectTypeDefinitionNode
140
            ? DirectiveLocationEnum::INPUT_FIELD_DEFINITION
141
            : DirectiveLocationEnum::ARGUMENT_DEFINITION;
142
    }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 137 is false. This is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
143
144
    // TODO: Throw appropriate exception.
145
}
146