Completed
Pull Request — master (#80)
by Christoffer
02:18
created

valueFromAST()   D

Complexity

Conditions 28
Paths 27

Size

Total Lines 137
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 28
eloc 69
nc 27
nop 3
dl 0
loc 137
rs 4.4803
c 0
b 0
f 0

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
namespace Digia\GraphQL\Language;
4
5
use Digia\GraphQL\Error\InvalidTypeException;
6
use Digia\GraphQL\Error\InvariantException;
7
use Digia\GraphQL\Language\Node\EnumValueNode;
8
use Digia\GraphQL\Language\Node\FieldNode;
9
use Digia\GraphQL\Language\Node\ListValueNode;
10
use Digia\GraphQL\Language\Node\NullValueNode;
11
use Digia\GraphQL\Language\Node\ObjectFieldNode;
12
use Digia\GraphQL\Language\Node\ObjectValueNode;
13
use Digia\GraphQL\Language\Node\ValueNodeInterface;
14
use Digia\GraphQL\Language\Node\VariableNode;
15
use Digia\GraphQL\Type\Definition\EnumType;
16
use Digia\GraphQL\Type\Definition\InputObjectType;
17
use Digia\GraphQL\Type\Definition\InputTypeInterface;
18
use Digia\GraphQL\Type\Definition\ListType;
19
use Digia\GraphQL\Type\Definition\NonNullType;
20
use Digia\GraphQL\Type\Definition\ScalarType;
21
use Digia\GraphQL\Type\Definition\TypeInterface;
22
use function Digia\GraphQL\Util\keyMap;
23
24
/**
25
 * @param ValueNodeInterface|null          $node
26
 * @param TypeInterface|InputTypeInterface $type
27
 * @param array                            $variables
28
 * @return mixed|null
29
 * @throws InvalidTypeException
30
 * @throws InvariantException
31
 */
32
function valueFromAST(?ValueNodeInterface $node, TypeInterface $type, array $variables = [])
33
{
34
    // TODO: Refactor this method, maybe into a more OOP approach.
35
36
    if (null === $node) {
37
        return; // Invalid: intentionally return no value.
38
    }
39
40
    if ($type instanceof NonNullType) {
41
        if ($node instanceof NullValueNode) {
42
            return; // Invalid: intentionally return no value.
43
        }
44
45
        return valueFromAST($node, $type->getOfType(), $variables);
46
    }
47
48
    if ($node instanceof NullValueNode) {
49
        return null;
50
    }
51
52
    if ($node instanceof VariableNode) {
53
        $variableName = $node->getNameValue();
54
55
        if (!isset($variables[$variableName])) {
56
            // No valid return value.
57
            return;
58
        }
59
60
        // Note: we're not doing any checking that this variable is correct. We're
61
        // assuming that this query has been validated and the variable usage here
62
        // is of the correct type.
63
        return $variables[$variableName];
64
    }
65
66
    if ($type instanceof ListType) {
67
        $itemType = $type->getOfType();
68
69
        if ($node instanceof ListValueNode) {
70
            $coercedValues = [];
71
72
            foreach ($node->getValues() as $value) {
73
                if (isMissingVariable($value, $variables)) {
74
                    // If an array contains a missing variable, it is either coerced to
75
                    // null or if the item type is non-null, it considered invalid.
76
                    if ($itemType instanceof NonNullType) {
77
                        return; // Invalid: intentionally return no value.
78
                    }
79
                    $coercedValues[] = null;
80
                } else {
81
                    $itemValue = valueFromAST($value, $itemType, $variables);
82
                    if (!$itemValue) {
83
                        return; // Invalid: intentionally return no value.
84
                    }
85
                    $coercedValues[] = $itemValue;
86
                }
87
            }
88
89
            return $coercedValues;
90
        }
91
92
        $coercedValue = valueFromAST($node, $itemType, $variables);
93
        if (!$coercedValue) {
94
            return; // Invalid: intentionally return no value.
95
        }
96
        return [$coercedValue];
97
    }
98
99
    if ($type instanceof InputObjectType) {
100
        if (!$node instanceof ObjectValueNode) {
101
            return; // Invalid: intentionally return no value.
102
        }
103
104
        $coercedObject = [];
105
106
        /** @var ObjectFieldNode[] $fieldNodes */
107
        $fieldNodes = keyMap($node->getFields(), function (FieldNode $value) {
108
            return $value->getNameValue();
109
        });
110
111
        foreach ($type->getFields() as $field) {
112
            $name      = $field->getName();
113
            $fieldNode = $fieldNodes[$name];
114
115
            if (null === $fieldNode || isMissingVariable($fieldNode->getValue(), $variables)) {
116
                if (null !== $field->getDefaultValue()) {
117
                    $coercedObject[$name] = $field->getDefaultValue();
118
                } elseif ($field->getType() instanceof NonNullType) {
119
                    return; // Invalid: intentionally return no value.
120
                }
121
                continue;
122
            }
123
124
            $fieldValue = valueFromAST($fieldNode->getValue(), $field->getType(), $variables);
125
126
            if (!$fieldValue) {
127
                return; // Invalid: intentionally return no value.
128
            }
129
130
            $coercedObject[$name] = $fieldValue;
131
        }
132
133
        return $coercedObject;
134
    }
135
136
    if ($type instanceof EnumType) {
137
        if (!$node instanceof EnumValueNode) {
138
            return; // Invalid: intentionally return no value.
139
        }
140
141
        $enumValue = $type->getValue($node->getValue());
142
143
        if (null === $enumValue) {
144
            return; // Invalid: intentionally return no value.
145
        }
146
147
        return $enumValue->getValue();
148
    }
149
150
    if ($type instanceof ScalarType) {
151
        // Scalars fulfill parsing a literal value via parseLiteral().
152
        // Invalid values represent a failure to parse correctly, in which case
153
        // no value is returned.
154
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
155
        try {
156
            $result = $type->parseLiteral($node, $variables);
0 ignored issues
show
Bug introduced by
$node of type Digia\GraphQL\Language\Node\ValueNodeInterface is incompatible with the type array expected by parameter $args of Digia\GraphQL\Type\Defin...larType::parseLiteral(). ( Ignorable by Annotation )

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

156
            $result = $type->parseLiteral(/** @scrutinizer ignore-type */ $node, $variables);
Loading history...
157
        } catch (\Exception $e) {
158
            return; // Invalid: intentionally return no value.
159
        }
160
161
        if (null === $result) {
162
            return; // Invalid: intentionally return no value.
163
        }
164
165
        return $result;
166
    }
167
168
    throw new InvalidTypeException(sprintf('Unknown type: %s', $type));
0 ignored issues
show
Bug introduced by
$type of type Digia\GraphQL\Type\Definition\TypeInterface is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

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

168
    throw new InvalidTypeException(sprintf('Unknown type: %s', /** @scrutinizer ignore-type */ $type));
Loading history...
169
}
170
171
/**
172
 * @param ValueNodeInterface $node
173
 * @param array              $variables
174
 * @return bool
175
 */
176
function isMissingVariable(ValueNodeInterface $node, array $variables): bool
177
{
178
    return $node instanceof VariableNode && isset($variables[$node->getNameValue()]);
179
}
180