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

153
            $result = $type->parseLiteral(/** @scrutinizer ignore-type */ $node, $variables);
Loading history...
154
        } catch (\Exception $e) {
155
            return; // Invalid: intentionally return no value.
156
        }
157
158
        if (null === $result) {
159
            return; // Invalid: intentionally return no value.
160
        }
161
162
        return $result;
163
    }
164
165
    throw new \Exception(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

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