Make Types throw instead of returning Utils::undefined()

This commit is contained in:
Daniel Tschinder 2018-04-24 15:14:31 +02:00
parent ec0985619f
commit f140149127
14 changed files with 117 additions and 70 deletions

View File

@ -30,11 +30,12 @@ class UrlType extends ScalarType
*
* @param mixed $value
* @return mixed
* @throws Error
*/
public function parseValue($value)
{
if (!is_string($value) || !filter_var($value, FILTER_VALIDATE_URL)) { // quite naive, but after all this is example
throw new \UnexpectedValueException("Cannot represent value as URL: " . Utils::printSafe($value));
throw new Error("Cannot represent value as URL: " . Utils::printSafe($value));
}
return $value;
}

View File

@ -1197,8 +1197,7 @@ class Executor
}
/**
* Complete a Scalar or Enum by serializing to a valid value, returning
* null if serialization is not possible.
* Complete a Scalar or Enum by serializing to a valid value, throwing if serialization is not possible.
*
* @param LeafType $returnType
* @param $result
@ -1207,14 +1206,21 @@ class Executor
*/
private function completeLeafValue(LeafType $returnType, &$result)
{
$serializedResult = $returnType->serialize($result);
if (Utils::isInvalid($serializedResult)) {
try {
return $returnType->serialize($result);
} catch (\Exception $error) {
throw new InvariantViolation(
'Expected a value of type "'. Utils::printSafe($returnType) . '" but received: ' . Utils::printSafe($result)
'Expected a value of type "'. Utils::printSafe($returnType) . '" but received: ' . Utils::printSafe($result),
0,
$error
);
} catch (\Throwable $error) {
throw new InvariantViolation(
'Expected a value of type "'. Utils::printSafe($returnType) . '" but received: ' . Utils::printSafe($result),
0,
$error
);
}
return $serializedResult;
}
/**

View File

@ -1,7 +1,9 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use GraphQL\Language\AST\BooleanValueNode;
use GraphQL\Language\AST\Node;
use GraphQL\Utils\Utils;
/**
@ -32,22 +34,30 @@ class BooleanType extends ScalarType
/**
* @param mixed $value
* @return bool
* @throws Error
*/
public function parseValue($value)
{
return is_bool($value) ? $value : Utils::undefined();
if (is_bool($value)) {
return $value;
}
throw new Error("Cannot represent value as boolean: " . Utils::printSafe($value));
}
/**
* @param $valueNode
* @param Node $valueNode
* @param array|null $variables
* @return bool|null
* @throws \Exception
*/
public function parseLiteral($valueNode, array $variables = null)
{
if ($valueNode instanceof BooleanValueNode) {
return (bool) $valueNode->value;
}
return Utils::undefined();
// Intentionally without message, as all information already in wrapped Exception
throw new \Exception();
}
}

View File

@ -1,6 +1,8 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use GraphQL\Language\AST\Node;
use GraphQL\Utils\AST;
use GraphQL\Utils\Utils;
@ -25,10 +27,6 @@ class CustomScalarType extends ScalarType
*/
public function parseValue($value)
{
if (Utils::isInvalid($value)) {
return Utils::undefined();
}
if (isset($this->config['parseValue'])) {
return call_user_func($this->config['parseValue'], $value);
} else {
@ -37,9 +35,10 @@ class CustomScalarType extends ScalarType
}
/**
* @param $valueNode
* @param Node $valueNode
* @param array|null $variables
* @return mixed
* @throws \Exception
*/
public function parseLiteral(/* GraphQL\Language\AST\ValueNode */ $valueNode, array $variables = null)
{

View File

@ -1,6 +1,7 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\EnumTypeDefinitionNode;
use GraphQL\Language\AST\EnumValueNode;
@ -103,7 +104,8 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
/**
* @param $value
* @return null
* @return mixed
* @throws Error
*/
public function serialize($value)
{
@ -112,36 +114,44 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
return $lookup[$value]->name;
}
return Utils::undefined();
throw new Error("Cannot serialize value as enum: " . Utils::printSafe($value));
}
/**
* @param $value
* @return null
* @return mixed
* @throws Error
*/
public function parseValue($value)
{
$lookup = $this->getNameLookup();
return isset($lookup[$value]) ? $lookup[$value]->value : Utils::undefined();
if (isset($lookup[$value])) {
return $lookup[$value]->value;
}
throw new Error("Cannot represent value as enum: " . Utils::printSafe($value));
}
/**
* @param $value
* @param $valueNode
* @param array|null $variables
* @return null
* @throws \Exception
*/
public function parseLiteral($value, array $variables = null)
public function parseLiteral($valueNode, array $variables = null)
{
if ($value instanceof EnumValueNode) {
if ($valueNode instanceof EnumValueNode) {
$lookup = $this->getNameLookup();
if (isset($lookup[$value->value])) {
$enumValue = $lookup[$value->value];
if (isset($lookup[$valueNode->value])) {
$enumValue = $lookup[$valueNode->value];
if ($enumValue) {
return $enumValue->value;
}
}
}
return null;
// Intentionally without message, as all information already in wrapped Exception
throw new \Exception();
}
/**

View File

@ -49,13 +49,16 @@ values as specified by
* @param $valueNode
* @param array|null $variables
* @return float|null
* @throws \Exception
*/
public function parseLiteral($valueNode, array $variables = null)
{
if ($valueNode instanceof FloatValueNode || $valueNode instanceof IntValueNode) {
return (float) $valueNode->value;
}
return Utils::undefined();
// Intentionally without message, as all information already in wrapped Exception
throw new \Exception();
}
private function coerceFloat($value) {

View File

@ -3,6 +3,7 @@ namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use GraphQL\Language\AST\IntValueNode;
use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\StringValueNode;
use GraphQL\Utils\Utils;
@ -30,6 +31,7 @@ When expected as an input type, any string (such as `"4"`) or integer
/**
* @param mixed $value
* @return string
* @throws Error
*/
public function serialize($value)
{
@ -51,22 +53,30 @@ When expected as an input type, any string (such as `"4"`) or integer
/**
* @param mixed $value
* @return string
* @throws Error
*/
public function parseValue($value)
{
return (is_string($value) || is_int($value)) ? (string) $value : Utils::undefined();
if (is_string($value) || is_int($value)) {
return (string) $value;
}
throw new Error("Cannot represent value as ID: " . Utils::printSafe($value));
}
/**
* @param $ast
* @param Node $valueNode
* @param array|null $variables
* @return null|string
* @throws \Exception
*/
public function parseLiteral($valueNode, array $variables = null)
{
if ($valueNode instanceof StringValueNode || $valueNode instanceof IntValueNode) {
return $valueNode->value;
}
return Utils::undefined();
// Intentionally without message, as all information already in wrapped Exception
throw new \Exception();
}
}

View File

@ -55,6 +55,7 @@ values. Int can represent values between -(2^31) and 2^31 - 1. ';
* @param $valueNode
* @param array|null $variables
* @return int|null
* @throws \Exception
*/
public function parseLiteral($valueNode, array $variables = null)
{
@ -64,7 +65,9 @@ values. Int can represent values between -(2^31) and 2^31 - 1. ';
return $val;
}
}
return Utils::undefined();
// Intentionally without message, as all information already in wrapped Exception
throw new \Exception();
}
private function coerceInt($value) {

View File

@ -1,6 +1,7 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Error\Error;
use \GraphQL\Language\AST\Node;
/*
@ -15,27 +16,30 @@ interface LeafType
*
* @param mixed $value
* @return mixed
* @throws Error
*/
public function serialize($value);
/**
* Parses an externally provided value (query variable) to use as an input
*
* In the case of an invalid value this method must return Utils::undefined()
* In the case of an invalid value this method must throw an Exception
*
* @param mixed $value
* @return mixed
* @throws Error
*/
public function parseValue($value);
/**
* Parses an externally provided literal value (hardcoded in GraphQL query) to use as an input
*
* In the case of an invalid value this method must return Utils::undefined()
* In the case of an invalid node or value this method must throw an Exception
*
* @param Node $valueNode
* @param array|null $variables
* @return mixed
* @throws \Exception
*/
public function parseLiteral($valueNode, array $variables = null);
}

View File

@ -60,13 +60,16 @@ represent free-form human-readable text.';
* @param $valueNode
* @param array|null $variables
* @return null|string
* @throws \Exception
*/
public function parseLiteral($valueNode, array $variables = null)
{
if ($valueNode instanceof StringValueNode) {
return $valueNode->value;
}
return Utils::undefined();
// Intentionally without message, as all information already in wrapped Exception
throw new \Exception();
}
private function coerceString($value) {

View File

@ -209,10 +209,19 @@ class AST
if ($type instanceof ScalarType || $type instanceof EnumType) {
// Since value is an internally represented value, it must be serialized
// to an externally represented value before converting into an AST.
try {
$serialized = $type->serialize($value);
if (null === $serialized || Utils::isInvalid($serialized)) {
} catch (\Exception $error) {
if ($error instanceof Error && $type instanceof EnumType) {
return null;
}
throw $error;
} catch (\Throwable $error) {
if ($error instanceof Error && $type instanceof EnumType) {
return null;
}
throw $error;
}
// Others serialize based on their corresponding PHP scalar types.
if (is_bool($serialized)) {
@ -400,18 +409,12 @@ class AST
// Invalid values represent a failure to parse correctly, in which case
// no value is returned.
try {
$result = $type->parseLiteral($valueNode, $variables);
return $type->parseLiteral($valueNode, $variables);
} catch (\Exception $error) {
return $undefined;
} catch (\Throwable $error) {
return $undefined;
}
if (Utils::isInvalid($result)) {
return $undefined;
}
return $result;
}
throw new Error('Unknown type: ' . Utils::printSafe($type) . '.');
@ -420,7 +423,7 @@ class AST
/**
* Produces a PHP value given a GraphQL Value AST.
*
* Unlike `valueFromAST()`, no type is provided. The resulting JavaScript value
* Unlike `valueFromAST()`, no type is provided. The resulting PHP value
* will reflect the provided GraphQL value AST.
*
* | GraphQL Value | PHP Value |
@ -471,7 +474,7 @@ class AST
);
case $valueNode instanceof VariableNode:
$variableName = $valueNode->name->value;
return ($variables && isset($variables[$variableName]) && !Utils::isInvalid($variables[$variableName]))
return ($variables && isset($variables[$variableName]))
? $variables[$variableName]
: null;
}

View File

@ -46,14 +46,7 @@ class Value
// throw to indicate failure. If it throws, maintain a reference to
// the original error.
try {
$parseResult = $type->parseValue($value);
if (Utils::isInvalid($parseResult)) {
return self::ofErrors([
self::coercionError("Expected type {$type->name}", $blameNode, $path),
]);
}
return self::ofValue($parseResult);
return self::ofValue($type->parseValue($value));
} catch (\Exception $error) {
return self::ofErrors([
self::coercionError(

View File

@ -173,20 +173,9 @@ class ValuesOfCorrectType extends AbstractValidationRule
}
// Scalars determine if a literal value is valid via parseLiteral() which
// may throw or return an invalid value to indicate failure.
// may throw to indicate failure.
try {
$parseResult = $type->parseLiteral($node);
if (Utils::isInvalid($parseResult)) {
$context->reportError(
new Error(
self::badValueMessage(
(string) $locationType,
Printer::doPrint($node)
),
$node
)
);
}
$type->parseLiteral($node);
} catch (\Exception $error) {
// Ensure a reference to the original error is maintained.
$context->reportError(

View File

@ -1,6 +1,7 @@
<?php
namespace GraphQL\Tests\Executor;
use GraphQL\Error\Error;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Utils\Utils;
@ -53,28 +54,40 @@ class ComplexScalar extends ScalarType
public $name = 'ComplexScalar';
/**
* {@inheritdoc}
*/
public function serialize($value)
{
if ($value === 'DeserializedValue') {
return 'SerializedValue';
}
return null;
throw new Error("Cannot serialize value as ComplexScalar: " . Utils::printSafe($value));
}
/**
* {@inheritdoc}
*/
public function parseValue($value)
{
if ($value === 'SerializedValue') {
return 'DeserializedValue';
}
return Utils::undefined();
throw new Error("Cannot represent value as ComplexScalar: " . Utils::printSafe($value));
}
/**
* {@inheritdoc}
*/
public function parseLiteral($valueNode, array $variables = null)
{
if ($valueNode->value === 'SerializedValue') {
return 'DeserializedValue';
}
return Utils::undefined();
throw new Error("Cannot represent literal as ComplexScalar: " . Utils::printSafe($valueNode->value));
}
}