Completed
Pull Request — master (#110)
by Christoffer
03:33
created

leaveOperationDefinition()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 35
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 8.439
c 0
b 0
f 0
cc 6
eloc 15
nc 4
nop 1
1
<?php
2
3
namespace Digia\GraphQL\Validation\Rule;
4
5
use Digia\GraphQL\Error\InvalidTypeException;
6
use Digia\GraphQL\Error\ValidationException;
7
use Digia\GraphQL\Language\Node\NodeInterface;
8
use Digia\GraphQL\Language\Node\OperationDefinitionNode;
9
use Digia\GraphQL\Language\Node\VariableDefinitionNode;
10
use Digia\GraphQL\Language\Node\VariableNode;
11
use Digia\GraphQL\Type\Definition\NonNullType;
12
use Digia\GraphQL\Type\Definition\TypeInterface;
13
use Digia\GraphQL\Util\TypeComparator;
14
use function Digia\GraphQL\Type\GraphQLNonNull;
15
use function Digia\GraphQL\Util\typeFromAST;
16
use function Digia\GraphQL\Validation\badVariablePositionMessage;
17
18
/**
19
 * Variables in allowed position
20
 *
21
 * Variables passed to field arguments conform to type.
22
 */
23
class VariablesInAllowedPositionRule extends AbstractRule
24
{
25
    /**
26
     * @var VariableDefinitionNode[]|null
27
     */
28
    protected $variableDefinitionMap;
29
30
    /**
31
     * @var TypeComparator
32
     */
33
    protected $typeComparator;
34
35
    /**
36
     * VariablesInAllowedPositionRule constructor.
37
     */
38
    public function __construct()
39
    {
40
        $this->typeComparator = new TypeComparator();
41
    }
42
43
    /**
44
     * @inheritdoc
45
     */
46
    protected function enterOperationDefinition(OperationDefinitionNode $node): ?NodeInterface
47
    {
48
        $this->variableDefinitionMap = [];
49
50
        return $node;
51
    }
52
53
    /**
54
     * @inheritdoc
55
     */
56
    protected function leaveOperationDefinition(OperationDefinitionNode $node): ?NodeInterface
57
    {
58
        $usages = $this->context->getRecursiveVariableUsages($node);
59
60
        /**
61
         * @var VariableNode  $variableNode
62
         * @var TypeInterface $type
63
         */
64
        foreach ($usages as ['node' => $variableNode, 'type' => $type]) {
65
            $variableName       = $variableNode->getNameValue();
66
            $variableDefinition = $this->variableDefinitionMap[$variableName];
67
68
            if (null !== $variableDefinition && null !== $type) {
69
                // A var type is allowed if it is the same or more strict (e.g. is
70
                // a subtype of) than the expected type. It can be more strict if
71
                // the variable type is non-null when the expected type is nullable.
72
                // If both are list types, the variable item type can be more strict
73
                // than the expected item type (contravariant).
74
                $schema       = $this->context->getSchema();
75
                $variableType = typeFromAST($schema, $variableDefinition->getType());
76
77
                if (null !== $variableType &&
78
                    !$this->typeComparator->isTypeSubTypeOf($schema,
79
                        $this->getEffectiveType($variableType, $variableDefinition), $type)) {
80
                    $this->context->reportError(
81
                        new ValidationException(
82
                            badVariablePositionMessage($variableName, $variableType, $type),
0 ignored issues
show
Bug introduced by
$variableType of type Digia\GraphQL\Type\Definition\TypeInterface is incompatible with the type string expected by parameter $typeName of Digia\GraphQL\Validation...riablePositionMessage(). ( Ignorable by Annotation )

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

82
                            badVariablePositionMessage($variableName, /** @scrutinizer ignore-type */ $variableType, $type),
Loading history...
Bug introduced by
$type of type Digia\GraphQL\Type\Definition\TypeInterface is incompatible with the type string expected by parameter $expectedTypeName of Digia\GraphQL\Validation...riablePositionMessage(). ( Ignorable by Annotation )

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

82
                            badVariablePositionMessage($variableName, $variableType, /** @scrutinizer ignore-type */ $type),
Loading history...
83
                            [$variableDefinition, $variableNode]
84
                        )
85
                    );
86
                }
87
            }
88
        }
89
90
        return $node;
91
    }
92
93
    /**
94
     * @inheritdoc
95
     */
96
    protected function enterVariableDefinition(VariableDefinitionNode $node): ?NodeInterface
97
    {
98
        $this->variableDefinitionMap[$node->getVariable()->getNameValue()] = $node;
99
100
        return $node;
101
    }
102
103
    /**
104
     * If a variable definition has a default value, it's effectively non-null.
105
     *
106
     * @param TypeInterface          $variableType
107
     * @param VariableDefinitionNode $variableDefinition
108
     * @return TypeInterface
109
     * @throws InvalidTypeException
110
     */
111
    protected function getEffectiveType(
112
        TypeInterface $variableType,
113
        VariableDefinitionNode $variableDefinition
114
    ): TypeInterface {
115
        return (!$variableDefinition->hasDefaultValue() || $variableType instanceof NonNullType)
116
            ? $variableType
117
            : GraphQLNonNull($variableType);
118
    }
119
}
120