mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-22 04:46:04 +03:00
Merge branch 'master' of https://github.com/webonyx/graphql-php
This commit is contained in:
commit
d5fbf1b29f
@ -16,9 +16,9 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/coding-standard": "^6.0",
|
"doctrine/coding-standard": "^6.0",
|
||||||
"phpbench/phpbench": "^0.14.0",
|
"phpbench/phpbench": "^0.14.0",
|
||||||
"phpstan/phpstan": "^0.11.4",
|
"phpstan/phpstan": "^0.11.8",
|
||||||
"phpstan/phpstan-phpunit": "^0.11.0",
|
"phpstan/phpstan-phpunit": "^0.11.2",
|
||||||
"phpstan/phpstan-strict-rules": "^0.11.0",
|
"phpstan/phpstan-strict-rules": "^0.11.1",
|
||||||
"phpunit/phpcov": "^5.0",
|
"phpunit/phpcov": "^5.0",
|
||||||
"phpunit/phpunit": "^7.2",
|
"phpunit/phpunit": "^7.2",
|
||||||
"psr/http-message": "^1.0",
|
"psr/http-message": "^1.0",
|
||||||
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace GraphQL\Error;
|
namespace GraphQL\Error;
|
||||||
|
|
||||||
|
use GraphQL\Exception\InvalidArgument;
|
||||||
|
use function is_int;
|
||||||
use function trigger_error;
|
use function trigger_error;
|
||||||
use const E_USER_WARNING;
|
use const E_USER_WARNING;
|
||||||
|
|
||||||
@ -15,12 +17,12 @@ use const E_USER_WARNING;
|
|||||||
*/
|
*/
|
||||||
final class Warning
|
final class Warning
|
||||||
{
|
{
|
||||||
const WARNING_ASSIGN = 2;
|
public const WARNING_ASSIGN = 2;
|
||||||
const WARNING_CONFIG = 4;
|
public const WARNING_CONFIG = 4;
|
||||||
const WARNING_FULL_SCHEMA_SCAN = 8;
|
public const WARNING_FULL_SCHEMA_SCAN = 8;
|
||||||
const WARNING_CONFIG_DEPRECATION = 16;
|
public const WARNING_CONFIG_DEPRECATION = 16;
|
||||||
const WARNING_NOT_A_TYPE = 32;
|
public const WARNING_NOT_A_TYPE = 32;
|
||||||
const ALL = 63;
|
public const ALL = 63;
|
||||||
|
|
||||||
/** @var int */
|
/** @var int */
|
||||||
private static $enableWarnings = self::ALL;
|
private static $enableWarnings = self::ALL;
|
||||||
@ -37,7 +39,7 @@ final class Warning
|
|||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public static function setWarningHandler(?callable $warningHandler = null)
|
public static function setWarningHandler(?callable $warningHandler = null) : void
|
||||||
{
|
{
|
||||||
self::$warningHandler = $warningHandler;
|
self::$warningHandler = $warningHandler;
|
||||||
}
|
}
|
||||||
@ -54,14 +56,16 @@ final class Warning
|
|||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public static function suppress($suppress = true)
|
public static function suppress($suppress = true) : void
|
||||||
{
|
{
|
||||||
if ($suppress === true) {
|
if ($suppress === true) {
|
||||||
self::$enableWarnings = 0;
|
self::$enableWarnings = 0;
|
||||||
} elseif ($suppress === false) {
|
} elseif ($suppress === false) {
|
||||||
self::$enableWarnings = self::ALL;
|
self::$enableWarnings = self::ALL;
|
||||||
} else {
|
} elseif (is_int($suppress)) {
|
||||||
self::$enableWarnings &= ~$suppress;
|
self::$enableWarnings &= ~$suppress;
|
||||||
|
} else {
|
||||||
|
throw InvalidArgument::fromExpectedTypeAndArgument('bool|int', $suppress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,20 +81,22 @@ final class Warning
|
|||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public static function enable($enable = true)
|
public static function enable($enable = true) : void
|
||||||
{
|
{
|
||||||
if ($enable === true) {
|
if ($enable === true) {
|
||||||
self::$enableWarnings = self::ALL;
|
self::$enableWarnings = self::ALL;
|
||||||
} elseif ($enable === false) {
|
} elseif ($enable === false) {
|
||||||
self::$enableWarnings = 0;
|
self::$enableWarnings = 0;
|
||||||
} else {
|
} elseif (is_int($enable)) {
|
||||||
self::$enableWarnings |= $enable;
|
self::$enableWarnings |= $enable;
|
||||||
|
} else {
|
||||||
|
throw InvalidArgument::fromExpectedTypeAndArgument('bool|int', $enable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function warnOnce($errorMessage, $warningId, $messageLevel = null)
|
public static function warnOnce(string $errorMessage, int $warningId, ?int $messageLevel = null) : void
|
||||||
{
|
{
|
||||||
if (self::$warningHandler) {
|
if (self::$warningHandler !== null) {
|
||||||
$fn = self::$warningHandler;
|
$fn = self::$warningHandler;
|
||||||
$fn($errorMessage, $warningId);
|
$fn($errorMessage, $warningId);
|
||||||
} elseif ((self::$enableWarnings & $warningId) > 0 && ! isset(self::$warned[$warningId])) {
|
} elseif ((self::$enableWarnings & $warningId) > 0 && ! isset(self::$warned[$warningId])) {
|
||||||
@ -99,9 +105,9 @@ final class Warning
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function warn($errorMessage, $warningId, $messageLevel = null)
|
public static function warn(string $errorMessage, int $warningId, ?int $messageLevel = null) : void
|
||||||
{
|
{
|
||||||
if (self::$warningHandler) {
|
if (self::$warningHandler !== null) {
|
||||||
$fn = self::$warningHandler;
|
$fn = self::$warningHandler;
|
||||||
$fn($errorMessage, $warningId);
|
$fn($errorMessage, $warningId);
|
||||||
} elseif ((self::$enableWarnings & $warningId) > 0) {
|
} elseif ((self::$enableWarnings & $warningId) > 0) {
|
||||||
|
20
src/Exception/InvalidArgument.php
Normal file
20
src/Exception/InvalidArgument.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Exception;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use function gettype;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
|
final class InvalidArgument extends InvalidArgumentException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param mixed $argument
|
||||||
|
*/
|
||||||
|
public static function fromExpectedTypeAndArgument(string $expectedType, $argument) : self
|
||||||
|
{
|
||||||
|
return new self(sprintf('Expected type "%s", got "%s"', $expectedType, gettype($argument)));
|
||||||
|
}
|
||||||
|
}
|
@ -38,16 +38,16 @@ class SyncPromise
|
|||||||
*/
|
*/
|
||||||
private $waiting = [];
|
private $waiting = [];
|
||||||
|
|
||||||
public static function runQueue()
|
public static function runQueue() : void
|
||||||
{
|
{
|
||||||
$q = self::$queue;
|
$q = self::$queue;
|
||||||
while ($q && ! $q->isEmpty()) {
|
while ($q !== null && ! $q->isEmpty()) {
|
||||||
$task = $q->dequeue();
|
$task = $q->dequeue();
|
||||||
$task();
|
$task();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function resolve($value)
|
public function resolve($value) : self
|
||||||
{
|
{
|
||||||
switch ($this->state) {
|
switch ($this->state) {
|
||||||
case self::PENDING:
|
case self::PENDING:
|
||||||
@ -83,7 +83,7 @@ class SyncPromise
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reject($reason)
|
public function reject($reason) : self
|
||||||
{
|
{
|
||||||
if (! $reason instanceof Exception && ! $reason instanceof Throwable) {
|
if (! $reason instanceof Exception && ! $reason instanceof Throwable) {
|
||||||
throw new Exception('SyncPromise::reject() has to be called with an instance of \Throwable');
|
throw new Exception('SyncPromise::reject() has to be called with an instance of \Throwable');
|
||||||
@ -107,7 +107,7 @@ class SyncPromise
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function enqueueWaitingPromises()
|
private function enqueueWaitingPromises() : void
|
||||||
{
|
{
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$this->state !== self::PENDING,
|
$this->state !== self::PENDING,
|
||||||
@ -116,7 +116,7 @@ class SyncPromise
|
|||||||
|
|
||||||
foreach ($this->waiting as $descriptor) {
|
foreach ($this->waiting as $descriptor) {
|
||||||
self::getQueue()->enqueue(function () use ($descriptor) {
|
self::getQueue()->enqueue(function () use ($descriptor) {
|
||||||
/** @var $promise self */
|
/** @var self $promise */
|
||||||
[$promise, $onFulfilled, $onRejected] = $descriptor;
|
[$promise, $onFulfilled, $onRejected] = $descriptor;
|
||||||
|
|
||||||
if ($this->state === self::FULFILLED) {
|
if ($this->state === self::FULFILLED) {
|
||||||
@ -145,17 +145,17 @@ class SyncPromise
|
|||||||
$this->waiting = [];
|
$this->waiting = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getQueue()
|
public static function getQueue() : SplQueue
|
||||||
{
|
{
|
||||||
return self::$queue ?: self::$queue = new SplQueue();
|
return self::$queue ?: self::$queue = new SplQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function then(?callable $onFulfilled = null, ?callable $onRejected = null)
|
public function then(?callable $onFulfilled = null, ?callable $onRejected = null)
|
||||||
{
|
{
|
||||||
if ($this->state === self::REJECTED && ! $onRejected) {
|
if ($this->state === self::REJECTED && $onRejected === null) {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
if ($this->state === self::FULFILLED && ! $onFulfilled) {
|
if ($this->state === self::FULFILLED && $onFulfilled === null) {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
$tmp = new self();
|
$tmp = new self();
|
||||||
|
@ -92,7 +92,7 @@ class Values
|
|||||||
),
|
),
|
||||||
[$varDefNode]
|
[$varDefNode]
|
||||||
);
|
);
|
||||||
} elseif ($varDefNode->defaultValue) {
|
} elseif ($varDefNode->defaultValue !== null) {
|
||||||
$coercedValues[$varName] = AST::valueFromAST($varDefNode->defaultValue, $varType);
|
$coercedValues[$varName] = AST::valueFromAST($varDefNode->defaultValue, $varType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ class Values
|
|||||||
$argType = $argumentDefinition->getType();
|
$argType = $argumentDefinition->getType();
|
||||||
$argumentValueNode = $argumentValueMap[$name] ?? null;
|
$argumentValueNode = $argumentValueMap[$name] ?? null;
|
||||||
|
|
||||||
if (! $argumentValueNode) {
|
if ($argumentValueNode === null) {
|
||||||
if ($argumentDefinition->defaultValueExists()) {
|
if ($argumentDefinition->defaultValueExists()) {
|
||||||
$coercedValues[$name] = $argumentDefinition->defaultValue;
|
$coercedValues[$name] = $argumentDefinition->defaultValue;
|
||||||
} elseif ($argType instanceof NonNull) {
|
} elseif ($argType instanceof NonNull) {
|
||||||
@ -209,7 +209,7 @@ class Values
|
|||||||
} elseif ($argumentValueNode instanceof VariableNode) {
|
} elseif ($argumentValueNode instanceof VariableNode) {
|
||||||
$variableName = $argumentValueNode->name->value;
|
$variableName = $argumentValueNode->name->value;
|
||||||
|
|
||||||
if ($variableValues && array_key_exists($variableName, $variableValues)) {
|
if ($variableValues !== null && array_key_exists($variableName, $variableValues)) {
|
||||||
// Note: this does not check that this variable value is correct.
|
// Note: this does not check that this variable value is correct.
|
||||||
// This assumes that this query has been validated and the variable
|
// This assumes that this query has been validated and the variable
|
||||||
// usage here is of the correct type.
|
// usage here is of the correct type.
|
||||||
|
@ -199,7 +199,7 @@ class Collector
|
|||||||
if ($selection instanceof FieldNode) {
|
if ($selection instanceof FieldNode) {
|
||||||
/** @var FieldNode $selection */
|
/** @var FieldNode $selection */
|
||||||
|
|
||||||
$resultName = $selection->alias ? $selection->alias->value : $selection->name->value;
|
$resultName = $selection->alias === null ? $selection->name->value : $selection->alias->value;
|
||||||
|
|
||||||
if (! isset($this->fields[$resultName])) {
|
if (! isset($this->fields[$resultName])) {
|
||||||
$this->fields[$resultName] = [];
|
$this->fields[$resultName] = [];
|
||||||
|
@ -498,7 +498,7 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation
|
|||||||
|
|
||||||
if ($type !== $this->schema->getType($type->name)) {
|
if ($type !== $this->schema->getType($type->name)) {
|
||||||
$hint = '';
|
$hint = '';
|
||||||
if ($this->schema->getConfig()->typeLoader) {
|
if ($this->schema->getConfig()->typeLoader !== null) {
|
||||||
$hint = sprintf(
|
$hint = sprintf(
|
||||||
'Make sure that type loader returns the same instance as defined in %s.%s',
|
'Make sure that type loader returns the same instance as defined in %s.%s',
|
||||||
$ctx->type,
|
$ctx->type,
|
||||||
@ -646,7 +646,7 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation
|
|||||||
} else {
|
} else {
|
||||||
if ($type !== $this->schema->getType($type->name)) {
|
if ($type !== $this->schema->getType($type->name)) {
|
||||||
$hint = '';
|
$hint = '';
|
||||||
if ($this->schema->getConfig()->typeLoader) {
|
if ($this->schema->getConfig()->typeLoader !== null) {
|
||||||
$hint = sprintf(
|
$hint = sprintf(
|
||||||
'Make sure that type loader returns the same instance as defined in %s.%s',
|
'Make sure that type loader returns the same instance as defined in %s.%s',
|
||||||
$ctx->type,
|
$ctx->type,
|
||||||
@ -904,7 +904,7 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation
|
|||||||
return $this->schema->getType($value['__typename']);
|
return $this->schema->getType($value['__typename']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($abstractType instanceof InterfaceType && $this->schema->getConfig()->typeLoader) {
|
if ($abstractType instanceof InterfaceType && $this->schema->getConfig()->typeLoader !== null) {
|
||||||
Warning::warnOnce(
|
Warning::warnOnce(
|
||||||
sprintf(
|
sprintf(
|
||||||
'GraphQL Interface Type `%s` returned `null` from its `resolveType` function ' .
|
'GraphQL Interface Type `%s` returned `null` from its `resolveType` function ' .
|
||||||
|
@ -69,7 +69,7 @@ class Location
|
|||||||
$this->endToken = $endToken;
|
$this->endToken = $endToken;
|
||||||
$this->source = $source;
|
$this->source = $source;
|
||||||
|
|
||||||
if (! $startToken || ! $endToken) {
|
if ($startToken === null || $endToken === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ abstract class Node
|
|||||||
|
|
||||||
$tmp = (array) $this;
|
$tmp = (array) $this;
|
||||||
|
|
||||||
if ($this->loc) {
|
if ($this->loc !== null) {
|
||||||
$tmp['loc'] = [
|
$tmp['loc'] = [
|
||||||
'start' => $this->loc->start,
|
'start' => $this->loc->start,
|
||||||
'end' => $this->loc->end,
|
'end' => $this->loc->end,
|
||||||
@ -125,7 +125,7 @@ abstract class Node
|
|||||||
'kind' => $node->kind,
|
'kind' => $node->kind,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($node->loc) {
|
if ($node->loc !== null) {
|
||||||
$result['loc'] = [
|
$result['loc'] = [
|
||||||
'start' => $node->loc->start,
|
'start' => $node->loc->start,
|
||||||
'end' => $node->loc->end,
|
'end' => $node->loc->end,
|
||||||
|
@ -314,11 +314,11 @@ class Lexer
|
|||||||
$start = $this->position;
|
$start = $this->position;
|
||||||
[$char, $code] = $this->readChar();
|
[$char, $code] = $this->readChar();
|
||||||
|
|
||||||
while ($code && (
|
while ($code !== null && (
|
||||||
$code === 95 || // _
|
$code === 95 || // _
|
||||||
$code >= 48 && $code <= 57 || // 0-9
|
($code >= 48 && $code <= 57) || // 0-9
|
||||||
$code >= 65 && $code <= 90 || // A-Z
|
($code >= 65 && $code <= 90) || // A-Z
|
||||||
$code >= 97 && $code <= 122 // a-z
|
($code >= 97 && $code <= 122) // a-z
|
||||||
)) {
|
)) {
|
||||||
$value .= $char;
|
$value .= $char;
|
||||||
[$char, $code] = $this->moveStringCursor(1, 1)->readChar();
|
[$char, $code] = $this->moveStringCursor(1, 1)->readChar();
|
||||||
@ -695,7 +695,7 @@ class Lexer
|
|||||||
do {
|
do {
|
||||||
[$char, $code, $bytes] = $this->moveStringCursor(1, $bytes)->readChar();
|
[$char, $code, $bytes] = $this->moveStringCursor(1, $bytes)->readChar();
|
||||||
$value .= $char;
|
$value .= $char;
|
||||||
} while ($code &&
|
} while ($code !== null &&
|
||||||
// SourceCharacter but not LineTerminator
|
// SourceCharacter but not LineTerminator
|
||||||
($code > 0x001F || $code === 0x0009)
|
($code > 0x001F || $code === 0x0009)
|
||||||
);
|
);
|
||||||
|
@ -1655,9 +1655,7 @@ class Parser
|
|||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
$directives = $this->parseDirectives(true);
|
$directives = $this->parseDirectives(true);
|
||||||
$types = $this->parseUnionMemberTypes();
|
$types = $this->parseUnionMemberTypes();
|
||||||
if (count($directives) === 0 &&
|
if (count($directives) === 0 && count($types) === 0) {
|
||||||
! $types
|
|
||||||
) {
|
|
||||||
throw $this->unexpected();
|
throw $this->unexpected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,28 +11,28 @@ namespace GraphQL\Language;
|
|||||||
class Token
|
class Token
|
||||||
{
|
{
|
||||||
// Each kind of token.
|
// Each kind of token.
|
||||||
const SOF = '<SOF>';
|
public const SOF = '<SOF>';
|
||||||
const EOF = '<EOF>';
|
public const EOF = '<EOF>';
|
||||||
const BANG = '!';
|
public const BANG = '!';
|
||||||
const DOLLAR = '$';
|
public const DOLLAR = '$';
|
||||||
const AMP = '&';
|
public const AMP = '&';
|
||||||
const PAREN_L = '(';
|
public const PAREN_L = '(';
|
||||||
const PAREN_R = ')';
|
public const PAREN_R = ')';
|
||||||
const SPREAD = '...';
|
public const SPREAD = '...';
|
||||||
const COLON = ':';
|
public const COLON = ':';
|
||||||
const EQUALS = '=';
|
public const EQUALS = '=';
|
||||||
const AT = '@';
|
public const AT = '@';
|
||||||
const BRACKET_L = '[';
|
public const BRACKET_L = '[';
|
||||||
const BRACKET_R = ']';
|
public const BRACKET_R = ']';
|
||||||
const BRACE_L = '{';
|
public const BRACE_L = '{';
|
||||||
const PIPE = '|';
|
public const PIPE = '|';
|
||||||
const BRACE_R = '}';
|
public const BRACE_R = '}';
|
||||||
const NAME = 'Name';
|
public const NAME = 'Name';
|
||||||
const INT = 'Int';
|
public const INT = 'Int';
|
||||||
const FLOAT = 'Float';
|
public const FLOAT = 'Float';
|
||||||
const STRING = 'String';
|
public const STRING = 'String';
|
||||||
const BLOCK_STRING = 'BlockString';
|
public const BLOCK_STRING = 'BlockString';
|
||||||
const COMMENT = 'Comment';
|
public const COMMENT = 'Comment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The kind of Token (see one of constants above).
|
* The kind of Token (see one of constants above).
|
||||||
@ -104,18 +104,15 @@ class Token
|
|||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getDescription() : string
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDescription()
|
|
||||||
{
|
{
|
||||||
return $this->kind . ($this->value ? ' "' . $this->value . '"' : '');
|
return $this->kind . ($this->value === null ? '' : ' "' . $this->value . '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return (string|int|null)[]
|
* @return (string|int|null)[]
|
||||||
*/
|
*/
|
||||||
public function toArray()
|
public function toArray() : array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'kind' => $this->kind,
|
'kind' => $this->kind,
|
||||||
|
@ -9,6 +9,7 @@ use function is_string;
|
|||||||
use function json_decode;
|
use function json_decode;
|
||||||
use function json_last_error;
|
use function json_last_error;
|
||||||
use const CASE_LOWER;
|
use const CASE_LOWER;
|
||||||
|
use const JSON_ERROR_NONE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure representing parsed HTTP parameters for GraphQL operation
|
* Structure representing parsed HTTP parameters for GraphQL operation
|
||||||
@ -93,7 +94,7 @@ class OperationParams
|
|||||||
}
|
}
|
||||||
|
|
||||||
$tmp = json_decode($params[$param], true);
|
$tmp = json_decode($params[$param], true);
|
||||||
if (json_last_error()) {
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ use GraphQL\Language\DirectiveLocation;
|
|||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
use function array_key_exists;
|
use function array_key_exists;
|
||||||
use function array_keys;
|
use function array_keys;
|
||||||
|
use function count;
|
||||||
use function in_array;
|
use function in_array;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
|
|
||||||
@ -16,11 +17,11 @@ class Directive
|
|||||||
{
|
{
|
||||||
public const DEFAULT_DEPRECATION_REASON = 'No longer supported';
|
public const DEFAULT_DEPRECATION_REASON = 'No longer supported';
|
||||||
|
|
||||||
const INCLUDE_NAME = 'include';
|
public const INCLUDE_NAME = 'include';
|
||||||
const IF_ARGUMENT_NAME = 'if';
|
public const IF_ARGUMENT_NAME = 'if';
|
||||||
const SKIP_NAME = 'skip';
|
public const SKIP_NAME = 'skip';
|
||||||
const DEPRECATED_NAME = 'deprecated';
|
public const DEPRECATED_NAME = 'deprecated';
|
||||||
const REASON_ARGUMENT_NAME = 'reason';
|
public const REASON_ARGUMENT_NAME = 'reason';
|
||||||
|
|
||||||
/** @var Directive[] */
|
/** @var Directive[] */
|
||||||
public static $internalDirectives;
|
public static $internalDirectives;
|
||||||
@ -84,9 +85,9 @@ class Directive
|
|||||||
/**
|
/**
|
||||||
* @return Directive[]
|
* @return Directive[]
|
||||||
*/
|
*/
|
||||||
public static function getInternalDirectives()
|
public static function getInternalDirectives() : array
|
||||||
{
|
{
|
||||||
if (! self::$internalDirectives) {
|
if (count(self::$internalDirectives) === 0) {
|
||||||
self::$internalDirectives = [
|
self::$internalDirectives = [
|
||||||
'include' => new self([
|
'include' => new self([
|
||||||
'name' => self::INCLUDE_NAME,
|
'name' => self::INCLUDE_NAME,
|
||||||
|
@ -168,7 +168,7 @@ class ObjectType extends Type implements OutputType, CompositeType, NullableType
|
|||||||
|
|
||||||
private function getInterfaceMap()
|
private function getInterfaceMap()
|
||||||
{
|
{
|
||||||
if (! $this->interfaceMap) {
|
if ($this->interfaceMap === null) {
|
||||||
$this->interfaceMap = [];
|
$this->interfaceMap = [];
|
||||||
foreach ($this->getInterfaces() as $interface) {
|
foreach ($this->getInterfaces() as $interface) {
|
||||||
$this->interfaceMap[$interface->name] = $interface;
|
$this->interfaceMap[$interface->name] = $interface;
|
||||||
|
@ -140,9 +140,11 @@ class QueryPlan
|
|||||||
/**
|
/**
|
||||||
* @return mixed[]
|
* @return mixed[]
|
||||||
*
|
*
|
||||||
|
* $parentType InterfaceType|ObjectType.
|
||||||
|
*
|
||||||
* @throws Error
|
* @throws Error
|
||||||
*/
|
*/
|
||||||
private function analyzeSelectionSet(SelectionSetNode $selectionSet, ObjectType $parentType) : array
|
private function analyzeSelectionSet(SelectionSetNode $selectionSet, Type $parentType) : array
|
||||||
{
|
{
|
||||||
$fields = [];
|
$fields = [];
|
||||||
foreach ($selectionSet->selections as $selectionNode) {
|
foreach ($selectionSet->selections as $selectionNode) {
|
||||||
|
@ -42,7 +42,7 @@ class SchemaConfig
|
|||||||
/** @var Directive[] */
|
/** @var Directive[] */
|
||||||
public $directives;
|
public $directives;
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable|null */
|
||||||
public $typeLoader;
|
public $typeLoader;
|
||||||
|
|
||||||
/** @var SchemaDefinitionNode */
|
/** @var SchemaDefinitionNode */
|
||||||
|
18
tests/Exception/InvalidArgumentTest.php
Normal file
18
tests/Exception/InvalidArgumentTest.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Exception;
|
||||||
|
|
||||||
|
use GraphQL\Exception\InvalidArgument;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class InvalidArgumentTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testFromExpectedTypeAndArgument() : void
|
||||||
|
{
|
||||||
|
$exception = InvalidArgument::fromExpectedTypeAndArgument('bool|int', 'stringValue');
|
||||||
|
|
||||||
|
self::assertSame('Expected type "bool|int", got "string"', $exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,8 @@ declare(strict_types=1);
|
|||||||
namespace GraphQL\Tests\Type;
|
namespace GraphQL\Tests\Type;
|
||||||
|
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Dog;
|
||||||
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\QueryPlan;
|
use GraphQL\Type\Definition\QueryPlan;
|
||||||
use GraphQL\Type\Definition\ResolveInfo;
|
use GraphQL\Type\Definition\ResolveInfo;
|
||||||
@ -295,6 +297,117 @@ final class QueryPlanTest extends TestCase
|
|||||||
self::assertFalse($queryPlan->hasType('Test'));
|
self::assertFalse($queryPlan->hasType('Test'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testQueryPlanOnInterface() : void
|
||||||
|
{
|
||||||
|
$petType = new InterfaceType([
|
||||||
|
'name' => 'Pet',
|
||||||
|
'fields' => static function () {
|
||||||
|
return [
|
||||||
|
'name' => ['type' => Type::string()],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$dogType = new ObjectType([
|
||||||
|
'name' => 'Dog',
|
||||||
|
'interfaces' => [$petType],
|
||||||
|
'isTypeOf' => static function ($obj) {
|
||||||
|
return $obj instanceof Dog;
|
||||||
|
},
|
||||||
|
'fields' => static function () {
|
||||||
|
return [
|
||||||
|
'name' => ['type' => Type::string()],
|
||||||
|
'woofs' => ['type' => Type::boolean()],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$query = 'query Test {
|
||||||
|
pets {
|
||||||
|
name
|
||||||
|
... on Dog {
|
||||||
|
woofs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
|
||||||
|
$expectedQueryPlan = [
|
||||||
|
'woofs' => [
|
||||||
|
'type' => Type::boolean(),
|
||||||
|
'fields' => [],
|
||||||
|
'args' => [],
|
||||||
|
],
|
||||||
|
'name' => [
|
||||||
|
'type' => Type::string(),
|
||||||
|
'args' => [],
|
||||||
|
'fields' => [],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$expectedReferencedTypes = [
|
||||||
|
'Dog',
|
||||||
|
'Pet',
|
||||||
|
];
|
||||||
|
|
||||||
|
$expectedReferencedFields = [
|
||||||
|
'woofs',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var QueryPlan $queryPlan */
|
||||||
|
$queryPlan = null;
|
||||||
|
$hasCalled = false;
|
||||||
|
|
||||||
|
$petsQuery = new ObjectType([
|
||||||
|
'name' => 'Query',
|
||||||
|
'fields' => [
|
||||||
|
'pets' => [
|
||||||
|
'type' => Type::listOf($petType),
|
||||||
|
'resolve' => static function (
|
||||||
|
$value,
|
||||||
|
$args,
|
||||||
|
$context,
|
||||||
|
ResolveInfo $info
|
||||||
|
) use (
|
||||||
|
&$hasCalled,
|
||||||
|
&$queryPlan
|
||||||
|
) {
|
||||||
|
$hasCalled = true;
|
||||||
|
$queryPlan = $info->lookAhead();
|
||||||
|
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$schema = new Schema([
|
||||||
|
'query' => $petsQuery,
|
||||||
|
'types' => [$dogType],
|
||||||
|
'typeLoader' => static function ($name) use ($dogType, $petType) {
|
||||||
|
switch ($name) {
|
||||||
|
case 'Dog':
|
||||||
|
return $dogType;
|
||||||
|
case 'Pet':
|
||||||
|
return $petType;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
||||||
|
|
||||||
|
self::assertTrue($hasCalled);
|
||||||
|
self::assertEquals($expectedQueryPlan, $queryPlan->queryPlan());
|
||||||
|
self::assertEquals($expectedReferencedTypes, $queryPlan->getReferencedTypes());
|
||||||
|
self::assertEquals($expectedReferencedFields, $queryPlan->getReferencedFields());
|
||||||
|
self::assertEquals(['woofs'], $queryPlan->subFields('Dog'));
|
||||||
|
|
||||||
|
self::assertTrue($queryPlan->hasField('name'));
|
||||||
|
self::assertFalse($queryPlan->hasField('test'));
|
||||||
|
|
||||||
|
self::assertTrue($queryPlan->hasType('Dog'));
|
||||||
|
self::assertFalse($queryPlan->hasType('Test'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testMergedFragmentsQueryPlan() : void
|
public function testMergedFragmentsQueryPlan() : void
|
||||||
{
|
{
|
||||||
$image = new ObjectType([
|
$image = new ObjectType([
|
||||||
|
Loading…
Reference in New Issue
Block a user