Completed
Pull Request — master (#74)
by Quang
02:04
created

ValuesResolver::coerceValueForVariableNode()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.439
c 0
b 0
f 0
cc 5
eloc 13
nc 4
nop 5
1
<?php
2
3
namespace Digia\GraphQL\Execution;
4
5
use Digia\GraphQL\Error\ExecutionException;
6
use Digia\GraphQL\Error\InvalidTypeException;
7
use Digia\GraphQL\Error\InvariantException;
8
use Digia\GraphQL\Language\Node\ArgumentNode;
9
use Digia\GraphQL\Language\Node\DirectivesTrait;
10
use Digia\GraphQL\Language\Node\FieldNode;
11
use Digia\GraphQL\Language\Node\NamedTypeNode;
12
use Digia\GraphQL\Language\Node\NodeInterface;
13
use Digia\GraphQL\Language\Node\VariableNode;
14
use Digia\GraphQL\Type\Definition\DirectiveInterface;
15
use Digia\GraphQL\Type\Definition\Field;
16
use Digia\GraphQL\Type\Definition\NonNullType;
17
use function Digia\GraphQL\Language\valueFromAST;
18
use function Digia\GraphQL\Util\find;
19
use function Digia\GraphQL\Util\keyMap;
20
21
/**
22
 * Class ValuesResolver
23
 * @package Digia\GraphQL\Execution
24
 */
25
class ValuesResolver
26
{
27
    /**
28
     * @see http://facebook.github.io/graphql/October2016/#CoerceArgumentValues()
29
     *
30
     * @param Field|DirectiveInterface $objectType
31
     * @param FieldNode                $field
32
     * @param array                    $variableValues
33
     * @return array
34
     * @throws ExecutionException
35
     * @throws InvalidTypeException
36
     * @throws InvariantException
37
     */
38
    public function coerceArgumentValues($objectType, $field, array $variableValues = []): array
39
    {
40
        $coercedValues       = [];
41
        $argumentDefinitions = $objectType->getArguments();
42
        $argumentNodes       = $field->getArguments();
43
44
        if (empty($argumentDefinitions) || empty($argumentNodes)) {
45
            return $coercedValues;
46
        }
47
48
        $argumentNodeMap = keyMap($argumentNodes, function (ArgumentNode $value) {
49
            return $value->getNameValue();
50
        });
51
52
        foreach ($argumentDefinitions as $argumentDefinition) {
53
            $argumentName = $argumentDefinition->getName();
54
            $argumentType = $argumentDefinition->getType();
55
            /** @var ArgumentNode $argumentNode */
56
            $argumentNode = $argumentNodeMap[$argumentName];
57
            $defaultValue = $argumentDefinition->getDefaultValue();
58
59
            if (null === $argumentNode) {
60
                if (null === $defaultValue) {
61
                    $coercedValues[$argumentName] = $defaultValue;
62
                } elseif (!$argumentType instanceof NonNullType) {
63
                    throw new ExecutionException(
64
                        sprintf('Argument "%s" of required type "%s" was not provided.', $argumentName, $argumentType),
65
                        [$field]
66
                    );
67
                }
68
            } elseif ($argumentNode instanceof VariableNode) {
69
                $coercedValues[$argumentName] = $this->coerceValueForVariableNode(
70
                    $argumentNode,
71
                    $argumentType,
72
                    $argumentName,
73
                    $variableValues,
74
                    $defaultValue
75
                );
76
            } else {
77
                $coercedValues[$argumentName] = $this->coerceValueForASTNode(
78
                    $argumentNode,
79
                    $argumentType,
80
                    $argumentName,
81
                    $variableValues
82
                );
83
            }
84
        }
85
86
        return $coercedValues;
87
    }
88
89
    /**
90
     * @param $argumentNode
91
     * @param $argumentType
92
     * @param $argumentName
93
     * @param $variableValues
94
     * @return mixed|null
95
     * @throws ExecutionException
96
     * @throws InvalidTypeException
97
     * @throws InvariantException
98
     */
99
    private function coerceValueForASTNode($argumentNode, $argumentType, $argumentName, $variableValues)
100
    {
101
        $valueNode = $argumentNode->getValue();
102
103
        $coercedValue = valueFromAST($valueNode, $argumentType, $variableValues);
104
105
        if (null === $coercedValue) {
106
            // Note: ValuesOfCorrectType validation should catch this before
107
            // execution. This is a runtime check to ensure execution does not
108
            // continue with an invalid argument value.
109
            throw new ExecutionException(
110
                sprintf('Argument "%s" has invalid value %s.', $argumentName, $valueNode),
111
                [$argumentNode->getValue()]
112
            );
113
        }
114
115
        return $coercedValue;
116
    }
117
118
    /**
119
     * @param VariableNode $variableNode
120
     * @param              $argumentType
121
     * @param              $name
122
     * @param              $variableValues
123
     * @param              $defaultValue
124
     * @return mixed
125
     * @throws ExecutionException
126
     */
127
    private function coerceValueForVariableNode(
128
        VariableNode $variableNode,
129
        $argumentType,
130
        $argumentName,
131
        $variableValues,
132
        $defaultValue
133
    ) {
134
        $variableName = $variableNode->getNameValue();
135
136
        if (!empty($variableValues) && isset($variableValues[$variableName])) {
137
            // Note: this does not check that this variable value is correct.
138
            // This assumes that this query has been validated and the variable
139
            // usage here is of the correct type.
140
            return $variableValues[$variableName];
141
        }
142
143
        if (null !== $defaultValue) {
144
            return $defaultValue;
145
        }
146
147
        if ($argumentType instanceof NonNullType) {
148
            throw new ExecutionException(
149
                sprintf(
150
                    'Argument "%s" of required type "%s" was provided the variable "%s" which was not provided a runtime value.',
151
                    $argumentName,
152
                    $argumentType,
153
                    $variableName
154
                ),
155
                [$variableNode->getValue()]
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on Digia\GraphQL\Language\Node\VariableNode. ( Ignorable by Annotation )

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

155
                [$variableNode->/** @scrutinizer ignore-call */ getValue()]

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
156
            );
157
        }
158
    }
159
160
    /**
161
     * @param DirectiveInterface            $directive
162
     * @param NodeInterface|DirectivesTrait $node
163
     * @param array                         $variableValues
164
     * @return array|null
165
     * @throws ExecutionException
166
     * @throws InvalidTypeException
167
     * @throws InvariantException
168
     */
169
    function getDirectiveValues(DirectiveInterface $directive, NodeInterface $node, array $variableValues = []): ?array
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
170
    {
171
        $directiveNode = $node->hasDirectives()
0 ignored issues
show
Bug introduced by
The method hasDirectives() does not exist on Digia\GraphQL\Language\Node\NodeInterface. It seems like you code against a sub-type of Digia\GraphQL\Language\Node\NodeInterface such as Digia\GraphQL\Language\Node\FragmentSpreadNode or Digia\GraphQL\Language\Node\InlineFragmentNode or Digia\GraphQL\Language\Node\FieldNode or Digia\GraphQL\Language\Node\EnumTypeDefinitionNode or Digia\GraphQL\Language\N...EnumValueDefinitionNode or Digia\GraphQL\Language\N...nputValueDefinitionNode or Digia\GraphQL\Language\Node\FieldDefinitionNode or Digia\GraphQL\Language\Node\FragmentDefinitionNode or Digia\GraphQL\Language\N...OperationDefinitionNode or Digia\GraphQL\Language\Node\SchemaDefinitionNode or Digia\GraphQL\Language\N...bjectTypeDefinitionNode or Digia\GraphQL\Language\N...calarTypeDefinitionNode or Digia\GraphQL\Language\N...bjectTypeDefinitionNode or Digia\GraphQL\Language\N...rfaceTypeDefinitionNode or Digia\GraphQL\Language\N...UnionTypeDefinitionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...ScalarTypeExtensionNode or Digia\GraphQL\Language\Node\EnumTypeExtensionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...erfaceTypeExtensionNode or Digia\GraphQL\Language\Node\UnionTypeExtensionNode. ( Ignorable by Annotation )

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

171
        $directiveNode = $node->/** @scrutinizer ignore-call */ hasDirectives()
Loading history...
172
            ? find($node->getDirectives(), function (NamedTypeNode $value) use ($directive) {
0 ignored issues
show
Bug introduced by
The method getDirectives() does not exist on Digia\GraphQL\Language\Node\NodeInterface. It seems like you code against a sub-type of Digia\GraphQL\Language\Node\NodeInterface such as Digia\GraphQL\Language\Node\FragmentSpreadNode or Digia\GraphQL\Language\Node\InlineFragmentNode or Digia\GraphQL\Language\Node\FieldNode or Digia\GraphQL\Language\Node\EnumTypeDefinitionNode or Digia\GraphQL\Language\N...EnumValueDefinitionNode or Digia\GraphQL\Language\N...nputValueDefinitionNode or Digia\GraphQL\Language\Node\FieldDefinitionNode or Digia\GraphQL\Language\Node\FragmentDefinitionNode or Digia\GraphQL\Language\N...OperationDefinitionNode or Digia\GraphQL\Language\Node\SchemaDefinitionNode or Digia\GraphQL\Language\N...bjectTypeDefinitionNode or Digia\GraphQL\Language\N...calarTypeDefinitionNode or Digia\GraphQL\Language\N...bjectTypeDefinitionNode or Digia\GraphQL\Language\N...rfaceTypeDefinitionNode or Digia\GraphQL\Language\N...UnionTypeDefinitionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...ScalarTypeExtensionNode or Digia\GraphQL\Language\Node\EnumTypeExtensionNode or Digia\GraphQL\Language\N...ObjectTypeExtensionNode or Digia\GraphQL\Language\N...erfaceTypeExtensionNode or Digia\GraphQL\Language\Node\UnionTypeExtensionNode. ( Ignorable by Annotation )

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

172
            ? find($node->/** @scrutinizer ignore-call */ getDirectives(), function (NamedTypeNode $value) use ($directive) {
Loading history...
173
                return $value->getNameValue() === $directive->getName();
174
            }) : null;
175
176
        if (null !== $directiveNode) {
177
            return $this->coerceArgumentValues($directive, $directiveNode, $variableValues);
178
        }
179
180
        return null;
181
    }
182
}