Completed
Pull Request — master (#103)
by Christoffer
02:14
created

VariablesInAllowedPositionRule::enterNode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
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
    /**
45
     * @inheritdoc
46
     */
47
    public function enterNode(NodeInterface $node): ?NodeInterface
48
    {
49
        if ($node instanceof OperationDefinitionNode) {
50
            $this->variableDefinitionMap = [];
51
        }
52
53
        return $node;
54
    }
55
56
    /**
57
     * @inheritdoc
58
     * @throws InvalidTypeException
59
     */
60
    public function leaveNode(NodeInterface $node): ?NodeInterface
61
    {
62
        if ($node instanceof OperationDefinitionNode) {
63
            $usages = $this->validationContext->getRecursiveVariableUsages($node);
64
65
            /**
66
             * @var VariableNode  $variableNode
67
             * @var TypeInterface $type
68
             */
69
            foreach ($usages as ['node' => $variableNode, 'type' => $type]) {
70
                $variableName       = $variableNode->getNameValue();
71
                $variableDefinition = $this->variableDefinitionMap[$variableName];
72
73
                if (null !== $variableDefinition && null !== $type) {
74
                    // A var type is allowed if it is the same or more strict (e.g. is
75
                    // a subtype of) than the expected type. It can be more strict if
76
                    // the variable type is non-null when the expected type is nullable.
77
                    // If both are list types, the variable item type can be more strict
78
                    // than the expected item type (contravariant).
79
                    $schema       = $this->validationContext->getSchema();
80
                    $variableType = typeFromAST($schema, $variableDefinition->getType());
81
82
                    if (null !== $variableType &&
83
                        !$this->typeComparator->isTypeSubTypeOf($schema,
84
                            $this->getEffectiveType($variableType, $variableDefinition), $type)) {
85
                        $this->validationContext->reportError(
86
                            new ValidationException(
87
                                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

87
                                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

87
                                badVariablePositionMessage($variableName, $variableType, /** @scrutinizer ignore-type */ $type),
Loading history...
88
                                [$variableDefinition, $variableNode]
89
                            )
90
                        );
91
                    }
92
                }
93
            }
94
        }
95
96
        if ($node instanceof VariableDefinitionNode) {
97
            $this->variableDefinitionMap[$node->getVariable()->getNameValue()] = $node;
98
        }
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