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

Completed
Pull Request — master (#21)
by Jérémiah
09:19
created

MaxQueryDepth::countSelectionDepth()   B

Complexity

Conditions 7
Paths 2

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 20
rs 8.2222
cc 7
eloc 12
nc 2
nop 4
1
<?php
2
3
/*
4
 * This file is part of the OverblogGraphQLBundle package.
5
 *
6
 * (c) Overblog <http://github.com/overblog/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Overblog\GraphQLBundle\Request\Validator\Rule;
13
14
use GraphQL\Error;
15
use GraphQL\Language\AST\Field;
16
use GraphQL\Language\AST\FragmentDefinition;
17
use GraphQL\Language\AST\FragmentSpread;
18
use GraphQL\Language\AST\InlineFragment;
19
use GraphQL\Language\AST\Node;
20
use GraphQL\Language\AST\SelectionSet;
21
use GraphQL\Type\Definition\WrappingType;
22
use GraphQL\Validator\ValidationContext;
23
24
class MaxQueryDepth
25
{
26
    const DEFAULT_QUERY_MAX_DEPTH = 100;
27
    const DEFAULT_MAX_COUNT_AFTER_DEPTH_LIMIT = 50;
28
29
    private static $maxQueryDepth;
30
31
    private $fragments = [];
32
33
    public function __construct($maxQueryDepth = self::DEFAULT_QUERY_MAX_DEPTH)
34
    {
35
        $this->setMaxQueryDepth($maxQueryDepth);
36
    }
37
38
    public static function setMaxQueryDepth($maxQueryDepth)
39
    {
40
        static::$maxQueryDepth = (int) $maxQueryDepth;
0 ignored issues
show
Bug introduced by
Since $maxQueryDepth is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $maxQueryDepth to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
41
    }
42
43
    public static function maxQueryDepthErrorMessage($max, $count)
44
    {
45
        return sprintf('Max query depth should be %d but is greater or equal to %d.', $max, $count);
46
    }
47
48
    public function __invoke(ValidationContext $context)
49
    {
50
        // Gather all the fragment definition.
51
        // Importantly this does not include inline fragments.
52
        $definitions = $context->getDocument()->definitions;
53
        foreach ($definitions as $node) {
54
            if ($node instanceof FragmentDefinition) {
55
                $this->fragments[$node->name->value] = $node;
56
            }
57
        }
58
        $schema = $context->getSchema();
59
        $rootTypes = [$schema->getQueryType(), $schema->getMutationType(), $schema->getSubscriptionType()];
60
61
        return [
62
            Node::FIELD => function (Field $node) use ($context, $rootTypes) {
63
                $parentType = $context->getParentType();
64
                $type = $this->retrieveCurrentTypeFromValidationContext($context);
65
                $isIntrospectionType = $type && $type->name === '__Schema';
66
                $isParentRootType = $parentType && in_array($parentType, $rootTypes);
67
68
                // check depth only on first rootTypes children and ignore check on introspection query
69
                if ($isParentRootType && !$isIntrospectionType) {
70
                    $depth = $node->selectionSet ?
71
                        $this->countSelectionDepth(
72
                            $node->selectionSet,
73
                            static::$maxQueryDepth + static::DEFAULT_MAX_COUNT_AFTER_DEPTH_LIMIT,
0 ignored issues
show
Bug introduced by
Since $maxQueryDepth is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $maxQueryDepth to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
74
                            0,
75
                            true
76
                        ) :
77
                        0
78
                    ;
79
80
                    if ($depth > static::$maxQueryDepth) {
0 ignored issues
show
Bug introduced by
Since $maxQueryDepth is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $maxQueryDepth to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
81
                        return new Error(static::maxQueryDepthErrorMessage(static::$maxQueryDepth, $depth), [$node]);
0 ignored issues
show
Bug introduced by
Since $maxQueryDepth is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $maxQueryDepth to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
82
                    }
83
                }
84
            },
85
        ];
86
    }
87
88
    private function retrieveCurrentTypeFromValidationContext(ValidationContext $context)
89
    {
90
        $type = $context->getType();
91
92
        if ($type instanceof WrappingType) {
93
            $type = $type->getWrappedType(true);
94
        }
95
96
        return $type;
97
    }
98
99
    private function countSelectionDepth(SelectionSet $selectionSet, $stopCountingAt, $depth = 0, $resetDepthForEachSelection = false)
100
    {
101
        foreach ($selectionSet->selections as $selectionAST) {
102
            if ($depth >= $stopCountingAt) {
103
                break;
104
            }
105
106
            $depth = $resetDepthForEachSelection ? 0 : $depth;
107
108
            if ($selectionAST instanceof Field) {
109
                $depth = $this->countFieldDepth($selectionAST->selectionSet, $stopCountingAt, $depth);
110
            } elseif ($selectionAST instanceof FragmentSpread) {
111
                $depth = $this->countFragmentDepth($selectionAST, $stopCountingAt, $depth);
112
            } elseif ($selectionAST instanceof InlineFragment) {
113
                $depth = $this->countInlineFragmentDepth($selectionAST->selectionSet, $stopCountingAt, $depth);
114
            }
115
        }
116
117
        return $depth;
118
    }
119
120
    private function countFieldDepth(SelectionSet $selectionSet = null, $stopCountingAt, $depth)
121
    {
122
        return null === $selectionSet ? $depth : $this->countSelectionDepth($selectionSet, $stopCountingAt, ++$depth);
123
    }
124
125
    private function countInlineFragmentDepth(SelectionSet $selectionSet = null, $stopCountingAt, $depth)
126
    {
127
        return null === $selectionSet ? $depth : $this->countSelectionDepth($selectionSet, $stopCountingAt, $depth);
128
    }
129
130
    private function countFragmentDepth(FragmentSpread $selectionAST, $stopCountingAt, $depth)
131
    {
132
        $spreadName = $selectionAST->name->value;
133
        if (isset($this->fragments[$spreadName])) {
134
            /** @var FragmentDefinition $fragment */
135
            $fragment = $this->fragments[$spreadName];
136
            $depth = $this->countSelectionDepth($fragment->selectionSet, $stopCountingAt, $depth);
137
        }
138
139
        return $depth;
140
    }
141
}
142