GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

PossibleFragmentSpreads::doTypesOverlap()   D
last analyzed

Complexity

Conditions 22
Paths 20

Size

Total Lines 66
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 22

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 66
ccs 24
cts 24
cp 1
rs 4.1666
c 0
b 0
f 0
cc 22
nc 20
nop 3
crap 22

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
declare(strict_types=1);
4
5
namespace GraphQL\Validator\Rules;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Language\AST\FragmentSpreadNode;
9
use GraphQL\Language\AST\InlineFragmentNode;
10
use GraphQL\Language\AST\NodeKind;
11
use GraphQL\Type\Definition\AbstractType;
12
use GraphQL\Type\Definition\CompositeType;
13
use GraphQL\Type\Definition\InterfaceType;
14
use GraphQL\Type\Definition\ObjectType;
15
use GraphQL\Type\Definition\UnionType;
16
use GraphQL\Type\Schema;
17
use GraphQL\Utils\TypeInfo;
18
use GraphQL\Validator\ValidationContext;
19
use function sprintf;
20
21
class PossibleFragmentSpreads extends ValidationRule
22
{
23 142
    public function getVisitor(ValidationContext $context)
24
    {
25
        return [
26
            NodeKind::INLINE_FRAGMENT => function (InlineFragmentNode $node) use ($context) {
27 26
                $fragType   = $context->getType();
28 26
                $parentType = $context->getParentType();
29
30 26
                if (! ($fragType instanceof CompositeType) ||
31 26
                    ! ($parentType instanceof CompositeType) ||
32 26
                    $this->doTypesOverlap($context->getSchema(), $fragType, $parentType)) {
33 25
                    return;
34
                }
35
36 1
                $context->reportError(new Error(
37 1
                    self::typeIncompatibleAnonSpreadMessage($parentType, $fragType),
38 1
                    [$node]
39
                ));
40 142
            },
41
            NodeKind::FRAGMENT_SPREAD => function (FragmentSpreadNode $node) use ($context) {
42 32
                $fragName   = $node->name->value;
43 32
                $fragType   = $this->getFragmentType($context, $fragName);
44 32
                $parentType = $context->getParentType();
45
46 32
                if (! $fragType ||
47 31
                    ! $parentType ||
48 32
                    $this->doTypesOverlap($context->getSchema(), $fragType, $parentType)
49
                ) {
50 24
                    return;
51
                }
52
53 8
                $context->reportError(new Error(
54 8
                    self::typeIncompatibleSpreadMessage($fragName, $parentType, $fragType),
55 8
                    [$node]
56
                ));
57 142
            },
58
        ];
59
    }
60
61 51
    private function doTypesOverlap(Schema $schema, CompositeType $fragType, CompositeType $parentType)
62
    {
63
        // Checking in the order of the most frequently used scenarios:
64
        // Parent type === fragment type
65 51
        if ($parentType === $fragType) {
66 12
            return true;
67
        }
68
69
        // Parent type is interface or union, fragment type is object type
70 39
        if ($parentType instanceof AbstractType && $fragType instanceof ObjectType) {
71 23
            return $schema->isPossibleType($parentType, $fragType);
72
        }
73
74
        // Parent type is object type, fragment type is interface (or rather rare - union)
75 16
        if ($parentType instanceof ObjectType && $fragType instanceof AbstractType) {
76 4
            return $schema->isPossibleType($fragType, $parentType);
77
        }
78
79
        // Both are object types:
80 12
        if ($parentType instanceof ObjectType && $fragType instanceof ObjectType) {
81 2
            return $parentType === $fragType;
82
        }
83
84
        // Both are interfaces
85
        // This case may be assumed valid only when implementations of two interfaces intersect
86
        // But we don't have information about all implementations at runtime
87
        // (getting this information via $schema->getPossibleTypes() requires scanning through whole schema
88
        // which is very costly to do at each request due to PHP "shared nothing" architecture)
89
        //
90
        // So in this case we just make it pass - invalid fragment spreads will be simply ignored during execution
91
        // See also https://github.com/webonyx/graphql-php/issues/69#issuecomment-283954602
92 10
        if ($parentType instanceof InterfaceType && $fragType instanceof InterfaceType) {
93 4
            return true;
94
95
            // Note that there is one case when we do have information about all implementations:
96
            // When schema descriptor is defined ($schema->hasDescriptor())
97
            // BUT we must avoid situation when some query that worked in development had suddenly stopped
98
            // working in production. So staying consistent and always validate.
99
        }
100
101
        // Interface within union
102 6
        if ($parentType instanceof UnionType && $fragType instanceof InterfaceType) {
103 2
            foreach ($parentType->getTypes() as $type) {
104 2
                if ($type->implementsInterface($fragType)) {
105 2
                    return true;
106
                }
107
            }
108
        }
109
110 5
        if ($parentType instanceof InterfaceType && $fragType instanceof UnionType) {
111 2
            foreach ($fragType->getTypes() as $type) {
112 2
                if ($type->implementsInterface($parentType)) {
113 2
                    return true;
114
                }
115
            }
116
        }
117
118 4
        if ($parentType instanceof UnionType && $fragType instanceof UnionType) {
119 2
            foreach ($fragType->getTypes() as $type) {
120 2
                if ($parentType->isPossibleType($type)) {
121 2
                    return true;
122
                }
123
            }
124
        }
125
126 3
        return false;
127
    }
128
129 1
    public static function typeIncompatibleAnonSpreadMessage($parentType, $fragType)
130
    {
131 1
        return sprintf(
132 1
            'Fragment cannot be spread here as objects of type "%s" can never be of type "%s".',
133 1
            $parentType,
134 1
            $fragType
135
        );
136
    }
137
138 32
    private function getFragmentType(ValidationContext $context, $name)
139
    {
140 32
        $frag = $context->getFragment($name);
141 32
        if ($frag) {
142 32
            $type = TypeInfo::typeFromAST($context->getSchema(), $frag->typeCondition);
143 32
            if ($type instanceof CompositeType) {
144 31
                return $type;
145
            }
146
        }
147
148 1
        return null;
149
    }
150
151 8
    public static function typeIncompatibleSpreadMessage($fragName, $parentType, $fragType)
152
    {
153 8
        return sprintf(
154 8
            'Fragment "%s" cannot be spread here as objects of type "%s" can never be of type "%s".',
155 8
            $fragName,
156 8
            $parentType,
157 8
            $fragType
158
        );
159
    }
160
}
161