Skip to content

Commit b47c87f

Browse files
committed
Allow null values for enums
1 parent f569c6d commit b47c87f

File tree

10 files changed

+99
-15
lines changed

10 files changed

+99
-15
lines changed

src/Executor/Values.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ public static function isValidPHPValue($value, InputType $type)
247247
// Scalar/Enum input checks to ensure the type can parse the value to
248248
// a non-null value.
249249
$parseResult = $type->parseValue($value);
250-
if (null === $parseResult) {
250+
if (null === $parseResult && !$type->isValidValue($value)) {
251251
$v = Utils::printSafe($value);
252252
return [
253253
"Expected type \"{$type->name}\", found $v."

src/Type/Definition/EnumType.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,24 @@ public function serialize($value)
9191
return isset($lookup[$value]) ? $lookup[$value]->name : null;
9292
}
9393

94+
/**
95+
* @param string $value
96+
* @return bool
97+
*/
98+
public function isValidValue($value)
99+
{
100+
return is_string($value) && $this->getNameLookup()->offsetExists($value);
101+
}
102+
103+
/**
104+
* @param $valueNode
105+
* @return bool
106+
*/
107+
public function isValidLiteral($valueNode)
108+
{
109+
return $valueNode instanceof EnumValueNode && $this->getNameLookup()->offsetExists($valueNode->value);
110+
}
111+
94112
/**
95113
* @param $value
96114
* @return null

src/Type/Definition/LeafType.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,16 @@ public function parseValue($value);
3131
* @return mixed
3232
*/
3333
public function parseLiteral($valueNode);
34+
35+
/**
36+
* @param string $value
37+
* @return bool
38+
*/
39+
public function isValidValue($value);
40+
41+
/**
42+
* @param \GraphQL\Language\AST\Node $valueNode
43+
* @return mixed
44+
*/
45+
public function isValidLiteral($valueNode);
3446
}

src/Type/Definition/ScalarType.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,28 @@ public function __construct()
3434

3535
Utils::assertValidName($this->name);
3636
}
37+
38+
/**
39+
* Determines if an internal value is valid for this type.
40+
* Equivalent to checking for if the parsedValue is nullish.
41+
*
42+
* @param $value
43+
* @return bool
44+
*/
45+
public function isValidValue($value)
46+
{
47+
return null !== $this->parseValue($value);
48+
}
49+
50+
/**
51+
* Determines if an internal value is valid for this type.
52+
* Equivalent to checking for if the parsedLiteral is nullish.
53+
*
54+
* @param $valueNode
55+
* @return bool
56+
*/
57+
public function isValidLiteral($valueNode)
58+
{
59+
return null !== $this->parseLiteral($valueNode);
60+
}
3761
}

src/Utils/AST.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,9 @@ public static function valueFromAST($valueNode, InputType $type, $variables = nu
307307
if ($type instanceof LeafType) {
308308
$parsed = $type->parseLiteral($valueNode);
309309

310-
if (null === $parsed) {
311-
// null represent a failure to parse correctly,
312-
// in which case no value is returned.
310+
if (null === $parsed && !$type->isValidLiteral($valueNode)) {
311+
// Invalid values represent a failure to parse correctly, in which case
312+
// no value is returned.
313313
return $undefined;
314314
}
315315

src/Utils/MixedStore.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ class MixedStore implements \ArrayAccess
4343
*/
4444
private $lastArrayValue;
4545

46+
/**
47+
* @var mixed
48+
*/
49+
private $nullValue;
50+
51+
/**
52+
* @var bool
53+
*/
54+
private $nullValueIsSet = false;
55+
4656
/**
4757
* MixedStore constructor.
4858
*/
@@ -83,6 +93,9 @@ public function offsetExists($offset)
8393
}
8494
}
8595
}
96+
if (null === $offset) {
97+
return $this->nullValueIsSet;
98+
}
8699
return false;
87100
}
88101

@@ -114,6 +127,9 @@ public function offsetGet($offset)
114127
}
115128
}
116129
}
130+
if (null === $offset) {
131+
return $this->nullValue;
132+
}
117133
return null;
118134
}
119135

@@ -138,6 +154,9 @@ public function offsetSet($offset, $value)
138154
} else if (is_array($offset)) {
139155
$this->arrayKeys[] = $offset;
140156
$this->arrayValues[] = $value;
157+
} else if (null === $offset) {
158+
$this->nullValue = $value;
159+
$this->nullValueIsSet = true;
141160
} else {
142161
throw new \InvalidArgumentException("Unexpected offset type: " . Utils::printSafe($offset));
143162
}
@@ -165,6 +184,9 @@ public function offsetUnset($offset)
165184
array_splice($this->arrayKeys, $index, 1);
166185
array_splice($this->arrayValues, $index, 1);
167186
}
187+
} else if (null === $offset) {
188+
$this->nullValue = null;
189+
$this->nullValueIsSet = false;
168190
}
169191
}
170192
}

src/Validator/DocumentValidator.php

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
<?php
22
namespace GraphQL\Validator;
33

4-
use GraphQL\Error\Error;
54
use GraphQL\Error\InvariantViolation;
65
use GraphQL\Language\AST\ListValueNode;
76
use GraphQL\Language\AST\DocumentNode;
8-
use GraphQL\Language\AST\FragmentSpreadNode;
9-
use GraphQL\Language\AST\Node;
107
use GraphQL\Language\AST\NodeKind;
118
use GraphQL\Language\AST\NullValueNode;
12-
use GraphQL\Language\AST\ValueNode;
139
use GraphQL\Language\AST\VariableNode;
1410
use GraphQL\Language\Printer;
1511
use GraphQL\Language\Visitor;
16-
use GraphQL\Language\VisitorOperation;
1712
use GraphQL\Schema;
1813
use GraphQL\Type\Definition\InputObjectType;
19-
use GraphQL\Type\Definition\InputType;
2014
use GraphQL\Type\Definition\LeafType;
2115
use GraphQL\Type\Definition\ListOfType;
2216
use GraphQL\Type\Definition\NonNull;
@@ -231,11 +225,8 @@ public static function isValidLiteralValue(Type $type, $valueNode)
231225
}
232226

233227
if ($type instanceof LeafType) {
234-
// Scalar/Enum input checks to ensure the type can parse the value to
235-
// a non-null value.
236-
$parseResult = $type->parseLiteral($valueNode);
237-
238-
if (null === $parseResult) {
228+
// Scalars must parse to a non-null value
229+
if (!$type->isValidLiteral($valueNode)) {
239230
$printed = Printer::doPrint($valueNode);
240231
return [ "Expected type \"{$type->name}\", found $printed." ];
241232
}

tests/Utils/ValueFromAstTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public function testConvertsEnumValuesAccordingToInputCoercionRules()
7272
'RED' => ['value' => 1],
7373
'GREEN' => ['value' => 2],
7474
'BLUE' => ['value' => 3],
75+
'NULL' => ['value' => null],
7576
]
7677
]);
7778

@@ -80,6 +81,7 @@ public function testConvertsEnumValuesAccordingToInputCoercionRules()
8081
$this->runTestCase($testEnum, '3', Utils::undefined());
8182
$this->runTestCase($testEnum, '"BLUE"', Utils::undefined());
8283
$this->runTestCase($testEnum, 'null', null);
84+
$this->runTestCase($testEnum, 'NULL', null);
8385
}
8486

8587
/**

tests/Validator/ArgumentsOfCorrectTypeTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,20 @@ public function testGoodEnumValue()
132132
');
133133
}
134134

135+
/**
136+
* @it Enum with null value
137+
*/
138+
public function testEnumWithNullValue()
139+
{
140+
$this->expectPassesRule(new ArgumentsOfCorrectType(), '
141+
{
142+
complicatedArgs {
143+
enumArgField(enumArg: NO_FUR)
144+
}
145+
}
146+
');
147+
}
148+
135149
/**
136150
* @it null into nullable type
137151
*/

tests/Validator/TestCase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ public static function getDefaultSchema()
183183
'BLACK' => [ 'value' => 1 ],
184184
'TAN' => [ 'value' => 2 ],
185185
'SPOTTED' => [ 'value' => 3 ],
186+
'NO_FUR' => [ 'value' => null ],
186187
],
187188
]);
188189

0 commit comments

Comments
 (0)