Compare commits

..

No commits in common. "master" and "v0.13.5" have entirely different histories.

96 changed files with 607 additions and 1297 deletions

1
.gitattributes vendored
View file

@ -2,7 +2,6 @@
* text eol=lf * text eol=lf
/benchmarks export-ignore /benchmarks export-ignore
/tests export-ignore /tests export-ignore
/examples export-ignore
/tools export-ignore /tools export-ignore
.gitattributes export-ignore .gitattributes export-ignore
.gitignore export-ignore .gitignore export-ignore

1
.gitignore vendored
View file

@ -4,4 +4,3 @@ composer.phar
phpcs.xml phpcs.xml
phpstan.neon phpstan.neon
vendor/ vendor/
/.idea

View file

@ -1,8 +1,4 @@
# Changelog # Changelog
## Unreleased
- Add schema validation: Input Objects must not contain non-nullable circular references (#492)
#### v0.13.5 #### v0.13.5
- Fix coroutine executor when using with promise (#486) - Fix coroutine executor when using with promise (#486)

View file

@ -10,9 +10,8 @@ For smaller contributions just use this workflow:
* Fork the project. * Fork the project.
* Add your features and or bug fixes. * Add your features and or bug fixes.
* Add tests. Tests are important for us. * Add tests. Tests are important for us.
* Check your changes using `composer check-all`. * Check your changes using `composer check-all`
* Add an entry to the [Changelog's Unreleases section](CHANGELOG.md#unreleased). * Send a pull request
* Send a pull request.
## Setup the Development Environment ## Setup the Development Environment
First, copy the URL of your fork and `git clone` it to your local machine. First, copy the URL of your fork and `git clone` it to your local machine.

View file

@ -49,4 +49,4 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
## License ## License
See [LICENSE](LICENSE). See [LICENCE](LICENSE).

View file

@ -1,8 +1,3 @@
## Master
### Breaking (major): dropped deprecations
- dropped deprecated `GraphQL\Schema`. Use `GraphQL\Type\Schema`.
## Upgrade v0.12.x > v0.13.x ## Upgrade v0.12.x > v0.13.x
### Breaking (major): minimum supported version of PHP ### Breaking (major): minimum supported version of PHP

View file

@ -152,7 +152,7 @@ class SchemaGenerator
]; ];
} }
public function resolveField($objectValue, $args, $context, $resolveInfo) public function resolveField($value, $args, $context, $resolveInfo)
{ {
return $resolveInfo->fieldName . '-value'; return $resolveInfo->fieldName . '-value';
} }

View file

@ -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.12", "phpstan/phpstan": "^0.11.4",
"phpstan/phpstan-phpunit": "^0.11.2", "phpstan/phpstan-phpunit": "^0.11.0",
"phpstan/phpstan-strict-rules": "^0.11.1", "phpstan/phpstan-strict-rules": "^0.11.0",
"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",

View file

@ -3,7 +3,6 @@
* [Standard Server](executing-queries.md/#using-server) Out of the box integration with any PSR-7 compatible framework (like [Slim](http://slimframework.com) or [Zend Expressive](http://zendframework.github.io/zend-expressive/)). * [Standard Server](executing-queries.md/#using-server) Out of the box integration with any PSR-7 compatible framework (like [Slim](http://slimframework.com) or [Zend Expressive](http://zendframework.github.io/zend-expressive/)).
* [Relay Library for graphql-php](https://github.com/ivome/graphql-relay-php) Helps construct Relay related schema definitions. * [Relay Library for graphql-php](https://github.com/ivome/graphql-relay-php) Helps construct Relay related schema definitions.
* [Lighthouse](https://github.com/nuwave/lighthouse) Laravel based, uses Schema Definition Language * [Lighthouse](https://github.com/nuwave/lighthouse) Laravel based, uses Schema Definition Language
* [Laravel GraphQL](https://github.com/rebing/graphql-laravel) - Laravel wrapper for Facebook's GraphQL
* [OverblogGraphQLBundle](https://github.com/overblog/GraphQLBundle) Bundle for Symfony * [OverblogGraphQLBundle](https://github.com/overblog/GraphQLBundle) Bundle for Symfony
* [WP-GraphQL](https://github.com/wp-graphql/wp-graphql) - GraphQL API for WordPress * [WP-GraphQL](https://github.com/wp-graphql/wp-graphql) - GraphQL API for WordPress

View file

@ -103,25 +103,23 @@ for a field you simply override this default resolver.
**graphql-php** provides following default field resolver: **graphql-php** provides following default field resolver:
```php ```php
<?php <?php
function defaultFieldResolver($objectValue, $args, $context, \GraphQL\Type\Definition\ResolveInfo $info) function defaultFieldResolver($source, $args, $context, \GraphQL\Type\Definition\ResolveInfo $info)
{ {
$fieldName = $info->fieldName; $fieldName = $info->fieldName;
$property = null; $property = null;
if (is_array($objectValue) || $objectValue instanceof \ArrayAccess) { if (is_array($source) || $source instanceof \ArrayAccess) {
if (isset($objectValue[$fieldName])) { if (isset($source[$fieldName])) {
$property = $objectValue[$fieldName]; $property = $source[$fieldName];
} }
} elseif (is_object($objectValue)) { } else if (is_object($source)) {
if (isset($objectValue->{$fieldName})) { if (isset($source->{$fieldName})) {
$property = $objectValue->{$fieldName}; $property = $source->{$fieldName};
}
} }
return $property instanceof Closure
? $property($objectValue, $args, $context, $info)
: $property;
} }
return $property instanceof Closure ? $property($source, $args, $context, $info) : $property;
}
``` ```
As you see it returns value by key (for arrays) or property (for objects). As you see it returns value by key (for arrays) or property (for objects).
@ -163,6 +161,7 @@ $userType = new ObjectType([
Keep in mind that **field resolver** has precedence over **default field resolver per type** which in turn Keep in mind that **field resolver** has precedence over **default field resolver per type** which in turn
has precedence over **default field resolver**. has precedence over **default field resolver**.
# Solving N+1 Problem # Solving N+1 Problem
Since: 0.9.0 Since: 0.9.0

View file

@ -54,8 +54,8 @@ $queryType = new ObjectType([
'args' => [ 'args' => [
'message' => Type::nonNull(Type::string()), 'message' => Type::nonNull(Type::string()),
], ],
'resolve' => function ($rootValue, $args) { 'resolve' => function ($root, $args) {
return $rootValue['prefix'] . $args['message']; return $root['prefix'] . $args['message'];
} }
], ],
], ],

View file

@ -33,7 +33,7 @@ See [related documentation](executing-queries.md).
* fieldResolver: * fieldResolver:
* A resolver function to use when one is not provided by the schema. * A resolver function to use when one is not provided by the schema.
* If not provided, the default field resolver is used (which looks for a * If not provided, the default field resolver is used (which looks for a
* value on the object value with the field's name). * value on the source value with the field's name).
* validationRules: * validationRules:
* A set of rules for query validation step. Default value is all available rules. * A set of rules for query validation step. Default value is all available rules.
* Empty array would allow to skip query validation (may be convenient for persisted * Empty array would allow to skip query validation (may be convenient for persisted
@ -299,7 +299,7 @@ static function getNullableType($type)
``` ```
# GraphQL\Type\Definition\ResolveInfo # GraphQL\Type\Definition\ResolveInfo
Structure containing information useful for field resolution process. Structure containing information useful for field resolution process.
Passed as 4th argument to every field resolver. See [docs on field resolving (data fetching)](data-fetching.md). Passed as 3rd argument to every field resolver. See [docs on field resolving (data fetching)](data-fetching.md).
**Class Props:** **Class Props:**
```php ```php

View file

@ -158,7 +158,7 @@ $heroType = new ObjectType([
'args' => [ 'args' => [
'episode' => Type::nonNull($enumType) 'episode' => Type::nonNull($enumType)
], ],
'resolve' => function($hero, $args) { 'resolve' => function($_value, $args) {
return $args['episode'] === 5 ? true : false; return $args['episode'] === 5 ? true : false;
} }
] ]

View file

@ -131,7 +131,7 @@ $queryType = new ObjectType([
'type' => Type::listOf($storyType), 'type' => Type::listOf($storyType),
'args' => [ 'args' => [
'filters' => [ 'filters' => [
'type' => $filters, 'type' => Type::nonNull($filters),
'defaultValue' => [ 'defaultValue' => [
'popular' => true 'popular' => true
] ]

View file

@ -80,7 +80,7 @@ Option | Type | Notes
name | `string` | **Required.** Name of the field. When not set - inferred from **fields** array key (read about [shorthand field definition](#shorthand-field-definitions) below) name | `string` | **Required.** Name of the field. When not set - inferred from **fields** array key (read about [shorthand field definition](#shorthand-field-definitions) below)
type | `Type` | **Required.** An instance of internal or custom type. Note: type must be represented by a single instance within one schema (see also [Type Registry](index.md#type-registry)) type | `Type` | **Required.** An instance of internal or custom type. Note: type must be represented by a single instance within one schema (see also [Type Registry](index.md#type-registry))
args | `array` | An array of possible type arguments. Each entry is expected to be an array with keys: **name**, **type**, **description**, **defaultValue**. See [Field Arguments](#field-arguments) section below. args | `array` | An array of possible type arguments. Each entry is expected to be an array with keys: **name**, **type**, **description**, **defaultValue**. See [Field Arguments](#field-arguments) section below.
resolve | `callable` | **function($objectValue, $args, $context, [ResolveInfo](../reference.md#graphqltypedefinitionresolveinfo) $info)**<br> Given the **$objectValue** of this type, it is expected to return actual value of the current field. See section on [Data Fetching](../data-fetching.md) for details resolve | `callable` | **function($value, $args, $context, [ResolveInfo](../reference.md#graphqltypedefinitionresolveinfo) $info)**<br> Given the **$value** of this type, it is expected to return actual value of the current field. See section on [Data Fetching](../data-fetching.md) for details
complexity | `callable` | **function($childrenComplexity, $args)**<br> Used to restrict query complexity. The feature is disabled by default, read about [Security](../security.md#query-complexity-analysis) to use it. complexity | `callable` | **function($childrenComplexity, $args)**<br> Used to restrict query complexity. The feature is disabled by default, read about [Security](../security.md#query-complexity-analysis) to use it.
description | `string` | Plain-text description of this field for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation) description | `string` | Plain-text description of this field for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation)
deprecationReason | `string` | Text describing why this field is deprecated. When not empty - field will not be returned by introspection queries (unless forced) deprecationReason | `string` | Text describing why this field is deprecated. When not empty - field will not be returned by introspection queries (unless forced)

View file

@ -62,7 +62,7 @@ $mutationType = new ObjectType([
'episode' => $episodeEnum, 'episode' => $episodeEnum,
'review' => $reviewInputObject 'review' => $reviewInputObject
], ],
'resolve' => function($rootValue, $args) { 'resolve' => function($val, $args) {
// TODOC // TODOC
} }
] ]

View file

@ -19,8 +19,8 @@ try {
'args' => [ 'args' => [
'message' => ['type' => Type::string()], 'message' => ['type' => Type::string()],
], ],
'resolve' => function ($rootValue, $args) { 'resolve' => function ($root, $args) {
return $rootValue['prefix'] . $args['message']; return $root['prefix'] . $args['message'];
} }
], ],
], ],
@ -35,7 +35,7 @@ try {
'x' => ['type' => Type::int()], 'x' => ['type' => Type::int()],
'y' => ['type' => Type::int()], 'y' => ['type' => Type::int()],
], ],
'resolve' => function ($calc, $args) { 'resolve' => function ($root, $args) {
return $args['x'] + $args['y']; return $args['x'] + $args['y'];
}, },
], ],

View file

@ -35,12 +35,12 @@ class CommentType extends ObjectType
Types::htmlField('body') Types::htmlField('body')
]; ];
}, },
'resolveField' => function($comment, $args, $context, ResolveInfo $info) { 'resolveField' => function($value, $args, $context, ResolveInfo $info) {
$method = 'resolve' . ucfirst($info->fieldName); $method = 'resolve' . ucfirst($info->fieldName);
if (method_exists($this, $method)) { if (method_exists($this, $method)) {
return $this->{$method}($comment, $args, $context, $info); return $this->{$method}($value, $args, $context, $info);
} else { } else {
return $comment->{$info->fieldName}; return $value->{$info->fieldName};
} }
} }
]; ];

View file

@ -57,8 +57,8 @@ class QueryType extends ObjectType
], ],
'hello' => Type::string() 'hello' => Type::string()
], ],
'resolveField' => function($rootValue, $args, $context, ResolveInfo $info) { 'resolveField' => function($val, $args, $context, ResolveInfo $info) {
return $this->{$info->fieldName}($rootValue, $args, $context, $info); return $this->{$info->fieldName}($val, $args, $context, $info);
} }
]; ];
parent::__construct($config); parent::__construct($config);

View file

@ -75,12 +75,12 @@ class StoryType extends ObjectType
'interfaces' => [ 'interfaces' => [
Types::node() Types::node()
], ],
'resolveField' => function($story, $args, $context, ResolveInfo $info) { 'resolveField' => function($value, $args, $context, ResolveInfo $info) {
$method = 'resolve' . ucfirst($info->fieldName); $method = 'resolve' . ucfirst($info->fieldName);
if (method_exists($this, $method)) { if (method_exists($this, $method)) {
return $this->{$method}($story, $args, $context, $info); return $this->{$method}($value, $args, $context, $info);
} else { } else {
return $story->{$info->fieldName}; return $value->{$info->fieldName};
} }
} }
]; ];

View file

@ -44,12 +44,12 @@ class UserType extends ObjectType
'interfaces' => [ 'interfaces' => [
Types::node() Types::node()
], ],
'resolveField' => function($user, $args, $context, ResolveInfo $info) { 'resolveField' => function($value, $args, $context, ResolveInfo $info) {
$method = 'resolve' . ucfirst($info->fieldName); $method = 'resolve' . ucfirst($info->fieldName);
if (method_exists($this, $method)) { if (method_exists($this, $method)) {
return $this->{$method}($user, $args, $context, $info); return $this->{$method}($value, $args, $context, $info);
} else { } else {
return $user->{$info->fieldName}; return $value->{$info->fieldName};
} }
} }
]; ];

View file

@ -1,12 +1,12 @@
<?php <?php
interface Resolver { interface Resolver {
public function resolve($rootValue, $args, $context); public function resolve($root, $args, $context);
} }
class Addition implements Resolver class Addition implements Resolver
{ {
public function resolve($rootValue, $args, $context) public function resolve($root, $args, $context)
{ {
return $args['x'] + $args['y']; return $args['x'] + $args['y'];
} }
@ -14,22 +14,22 @@ class Addition implements Resolver
class Echoer implements Resolver class Echoer implements Resolver
{ {
public function resolve($rootValue, $args, $context) public function resolve($root, $args, $context)
{ {
return $rootValue['prefix'].$args['message']; return $root['prefix'].$args['message'];
} }
} }
return [ return [
'sum' => function($rootValue, $args, $context) { 'sum' => function($root, $args, $context) {
$sum = new Addition(); $sum = new Addition();
return $sum->resolve($rootValue, $args, $context); return $sum->resolve($root, $args, $context);
}, },
'echo' => function($rootValue, $args, $context) { 'echo' => function($root, $args, $context) {
$echo = new Echoer(); $echo = new Echoer();
return $echo->resolve($rootValue, $args, $context); return $echo->resolve($root, $args, $context);
}, },
'prefix' => 'You said: ', 'prefix' => 'You said: ',
]; ];

View file

@ -19,8 +19,8 @@ try {
'args' => [ 'args' => [
'message' => ['type' => Type::string()], 'message' => ['type' => Type::string()],
], ],
'resolve' => function ($rootValue, $args) { 'resolve' => function ($root, $args) {
return $rootValue['prefix'] . $args['message']; return $root['prefix'] . $args['message'];
} }
], ],
], ],
@ -35,7 +35,7 @@ try {
'x' => ['type' => Type::int()], 'x' => ['type' => Type::int()],
'y' => ['type' => Type::int()], 'y' => ['type' => Type::int()],
], ],
'resolve' => function ($calc, $args) { 'resolve' => function ($root, $args) {
return $args['x'] + $args['y']; return $args['x'] + $args['y'];
}, },
], ],

View file

@ -4,8 +4,6 @@ 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;
@ -17,12 +15,12 @@ use const E_USER_WARNING;
*/ */
final class Warning final class Warning
{ {
public const WARNING_ASSIGN = 2; const WARNING_ASSIGN = 2;
public const WARNING_CONFIG = 4; const WARNING_CONFIG = 4;
public const WARNING_FULL_SCHEMA_SCAN = 8; const WARNING_FULL_SCHEMA_SCAN = 8;
public const WARNING_CONFIG_DEPRECATION = 16; const WARNING_CONFIG_DEPRECATION = 16;
public const WARNING_NOT_A_TYPE = 32; const WARNING_NOT_A_TYPE = 32;
public const ALL = 63; const ALL = 63;
/** @var int */ /** @var int */
private static $enableWarnings = self::ALL; private static $enableWarnings = self::ALL;
@ -39,7 +37,7 @@ final class Warning
* *
* @api * @api
*/ */
public static function setWarningHandler(?callable $warningHandler = null) : void public static function setWarningHandler(?callable $warningHandler = null)
{ {
self::$warningHandler = $warningHandler; self::$warningHandler = $warningHandler;
} }
@ -56,16 +54,14 @@ final class Warning
* *
* @api * @api
*/ */
public static function suppress($suppress = true) : void public static function suppress($suppress = true)
{ {
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;
} elseif (is_int($suppress)) {
self::$enableWarnings &= ~$suppress;
} else { } else {
throw InvalidArgument::fromExpectedTypeAndArgument('bool|int', $suppress); self::$enableWarnings &= ~$suppress;
} }
} }
@ -81,22 +77,20 @@ final class Warning
* *
* @api * @api
*/ */
public static function enable($enable = true) : void public static function enable($enable = true)
{ {
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;
} elseif (is_int($enable)) {
self::$enableWarnings |= $enable;
} else { } else {
throw InvalidArgument::fromExpectedTypeAndArgument('bool|int', $enable); self::$enableWarnings |= $enable;
} }
} }
public static function warnOnce(string $errorMessage, int $warningId, ?int $messageLevel = null) : void public static function warnOnce($errorMessage, $warningId, $messageLevel = null)
{ {
if (self::$warningHandler !== null) { if (self::$warningHandler) {
$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])) {
@ -105,9 +99,9 @@ final class Warning
} }
} }
public static function warn(string $errorMessage, int $warningId, ?int $messageLevel = null) : void public static function warn($errorMessage, $warningId, $messageLevel = null)
{ {
if (self::$warningHandler !== null) { if (self::$warningHandler) {
$fn = self::$warningHandler; $fn = self::$warningHandler;
$fn($errorMessage, $warningId); $fn($errorMessage, $warningId);
} elseif ((self::$enableWarnings & $warningId) > 0) { } elseif ((self::$enableWarnings & $warningId) > 0) {

View file

@ -1,20 +0,0 @@
<?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)));
}
}

View file

@ -14,7 +14,7 @@ use GraphQL\Type\Schema;
* Data that must be available at all points during query execution. * Data that must be available at all points during query execution.
* *
* Namely, schema of the type system that is currently executing, * Namely, schema of the type system that is currently executing,
* and the fragments defined in the query document. * and the fragments defined in the query document
* *
* @internal * @internal
*/ */
@ -45,28 +45,28 @@ class ExecutionContext
public $errors; public $errors;
/** @var PromiseAdapter */ /** @var PromiseAdapter */
public $promiseAdapter; public $promises;
public function __construct( public function __construct(
$schema, $schema,
$fragments, $fragments,
$rootValue, $root,
$contextValue, $contextValue,
$operation, $operation,
$variableValues, $variables,
$errors, $errors,
$fieldResolver, $fieldResolver,
$promiseAdapter $promiseAdapter
) { ) {
$this->schema = $schema; $this->schema = $schema;
$this->fragments = $fragments; $this->fragments = $fragments;
$this->rootValue = $rootValue; $this->rootValue = $root;
$this->contextValue = $contextValue; $this->contextValue = $contextValue;
$this->operation = $operation; $this->operation = $operation;
$this->variableValues = $variableValues; $this->variableValues = $variables;
$this->errors = $errors ?: []; $this->errors = $errors ?: [];
$this->fieldResolver = $fieldResolver; $this->fieldResolver = $fieldResolver;
$this->promiseAdapter = $promiseAdapter; $this->promises = $promiseAdapter;
} }
public function addError(Error $error) public function addError(Error $error)

View file

@ -157,33 +157,31 @@ class Executor
/** /**
* If a resolve function is not given, then a default resolve behavior is used * If a resolve function is not given, then a default resolve behavior is used
* which takes the property of the root value of the same name as the field * which takes the property of the source object of the same name as the field
* and returns it as the result, or if it's a function, returns the result * and returns it as the result, or if it's a function, returns the result
* of calling that function while passing along args and context. * of calling that function while passing along args and context.
* *
* @param mixed $objectValue * @param mixed $source
* @param mixed[] $args * @param mixed[] $args
* @param mixed|null $context * @param mixed|null $context
* *
* @return mixed|null * @return mixed|null
*/ */
public static function defaultFieldResolver($objectValue, $args, $context, ResolveInfo $info) public static function defaultFieldResolver($source, $args, $context, ResolveInfo $info)
{ {
$fieldName = $info->fieldName; $fieldName = $info->fieldName;
$property = null; $property = null;
if (is_array($objectValue) || $objectValue instanceof ArrayAccess) { if (is_array($source) || $source instanceof ArrayAccess) {
if (isset($objectValue[$fieldName])) { if (isset($source[$fieldName])) {
$property = $objectValue[$fieldName]; $property = $source[$fieldName];
} }
} elseif (is_object($objectValue)) { } elseif (is_object($source)) {
if (isset($objectValue->{$fieldName})) { if (isset($source->{$fieldName})) {
$property = $objectValue->{$fieldName}; $property = $source->{$fieldName};
} }
} }
return $property instanceof Closure return $property instanceof Closure ? $property($source, $args, $context, $info) : $property;
? $property($objectValue, $args, $context, $info)
: $property;
} }
} }

View file

@ -38,16 +38,16 @@ class SyncPromise
*/ */
private $waiting = []; private $waiting = [];
public static function runQueue() : void public static function runQueue()
{ {
$q = self::$queue; $q = self::$queue;
while ($q !== null && ! $q->isEmpty()) { while ($q && ! $q->isEmpty()) {
$task = $q->dequeue(); $task = $q->dequeue();
$task(); $task();
} }
} }
public function resolve($value) : self public function resolve($value)
{ {
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) : self public function reject($reason)
{ {
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() : void private function enqueueWaitingPromises()
{ {
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 self $promise */ /** @var $promise self */
[$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() : SplQueue public static function getQueue()
{ {
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 === null) { if ($this->state === self::REJECTED && ! $onRejected) {
return $this; return $this;
} }
if ($this->state === self::FULFILLED && $onFulfilled === null) { if ($this->state === self::FULFILLED && ! $onFulfilled) {
return $this; return $this;
} }
$tmp = new self(); $tmp = new self();

View file

@ -134,30 +134,30 @@ class ReferenceExecutor implements ExecutorImplementation
) { ) {
$errors = []; $errors = [];
$fragments = []; $fragments = [];
/** @var OperationDefinitionNode|null $operation */ /** @var OperationDefinitionNode $operation */
$operation = null; $operation = null;
$hasMultipleAssumedOperations = false; $hasMultipleAssumedOperations = false;
foreach ($documentNode->definitions as $definition) { foreach ($documentNode->definitions as $definition) {
switch (true) { switch ($definition->kind) {
case $definition instanceof OperationDefinitionNode: case NodeKind::OPERATION_DEFINITION:
if ($operationName === null && $operation !== null) { if (! $operationName && $operation) {
$hasMultipleAssumedOperations = true; $hasMultipleAssumedOperations = true;
} }
if ($operationName === null || if (! $operationName ||
(isset($definition->name) && $definition->name->value === $operationName)) { (isset($definition->name) && $definition->name->value === $operationName)) {
$operation = $definition; $operation = $definition;
} }
break; break;
case $definition instanceof FragmentDefinitionNode: case NodeKind::FRAGMENT_DEFINITION:
$fragments[$definition->name->value] = $definition; $fragments[$definition->name->value] = $definition;
break; break;
} }
} }
if ($operation === null) { if ($operation === null) {
if ($operationName === null) { if ($operationName) {
$errors[] = new Error('Must provide an operation.');
} else {
$errors[] = new Error(sprintf('Unknown operation named "%s".', $operationName)); $errors[] = new Error(sprintf('Unknown operation named "%s".', $operationName));
} else {
$errors[] = new Error('Must provide an operation.');
} }
} elseif ($hasMultipleAssumedOperations) { } elseif ($hasMultipleAssumedOperations) {
$errors[] = new Error( $errors[] = new Error(
@ -199,7 +199,7 @@ class ReferenceExecutor implements ExecutorImplementation
public function doExecute() : Promise public function doExecute() : Promise
{ {
// Return a Promise that will eventually resolve to the data described by // Return a Promise that will eventually resolve to the data described by
// the "Response" section of the GraphQL specification. // The "Response" section of the GraphQL specification.
// //
// If errors are encountered while executing a GraphQL field, only that // If errors are encountered while executing a GraphQL field, only that
// field and its descendants will be omitted, and sibling fields will still // field and its descendants will be omitted, and sibling fields will still
@ -212,7 +212,7 @@ class ReferenceExecutor implements ExecutorImplementation
// But for the "sync" case it is always fulfilled // But for the "sync" case it is always fulfilled
return $this->isPromise($result) return $this->isPromise($result)
? $result ? $result
: $this->exeContext->promiseAdapter->createFulfilled($result); : $this->exeContext->promises->createFulfilled($result);
} }
/** /**
@ -237,7 +237,7 @@ class ReferenceExecutor implements ExecutorImplementation
/** /**
* Implements the "Evaluating operations" section of the spec. * Implements the "Evaluating operations" section of the spec.
* *
* @param mixed $rootValue * @param mixed[] $rootValue
* *
* @return Promise|stdClass|mixed[] * @return Promise|stdClass|mixed[]
*/ */
@ -252,18 +252,16 @@ class ReferenceExecutor implements ExecutorImplementation
// //
// Similar to completeValueCatchingError. // Similar to completeValueCatchingError.
try { try {
$result = $operation->operation === 'mutation' $result = $operation->operation === 'mutation' ?
? $this->executeFieldsSerially($type, $rootValue, $path, $fields) $this->executeFieldsSerially($type, $rootValue, $path, $fields) :
: $this->executeFields($type, $rootValue, $path, $fields); $this->executeFields($type, $rootValue, $path, $fields);
if ($this->isPromise($result)) { if ($this->isPromise($result)) {
return $result->then( return $result->then(
null, null,
function ($error) { function ($error) {
if ($error instanceof Error) { $this->exeContext->addError($error);
$this->exeContext->addError($error);
return $this->exeContext->promiseAdapter->createFulfilled(null); return $this->exeContext->promises->createFulfilled(null);
}
} }
); );
} }
@ -288,7 +286,7 @@ class ReferenceExecutor implements ExecutorImplementation
switch ($operation->operation) { switch ($operation->operation) {
case 'query': case 'query':
$queryType = $schema->getQueryType(); $queryType = $schema->getQueryType();
if ($queryType === null) { if (! $queryType) {
throw new Error( throw new Error(
'Schema does not define the required query root type.', 'Schema does not define the required query root type.',
[$operation] [$operation]
@ -298,7 +296,7 @@ class ReferenceExecutor implements ExecutorImplementation
return $queryType; return $queryType;
case 'mutation': case 'mutation':
$mutationType = $schema->getMutationType(); $mutationType = $schema->getMutationType();
if ($mutationType === null) { if (! $mutationType) {
throw new Error( throw new Error(
'Schema is not configured for mutations.', 'Schema is not configured for mutations.',
[$operation] [$operation]
@ -308,7 +306,7 @@ class ReferenceExecutor implements ExecutorImplementation
return $mutationType; return $mutationType;
case 'subscription': case 'subscription':
$subscriptionType = $schema->getSubscriptionType(); $subscriptionType = $schema->getSubscriptionType();
if ($subscriptionType === null) { if (! $subscriptionType) {
throw new Error( throw new Error(
'Schema is not configured for subscriptions.', 'Schema is not configured for subscriptions.',
[$operation] [$operation]
@ -345,8 +343,8 @@ class ReferenceExecutor implements ExecutorImplementation
) { ) {
$exeContext = $this->exeContext; $exeContext = $this->exeContext;
foreach ($selectionSet->selections as $selection) { foreach ($selectionSet->selections as $selection) {
switch (true) { switch ($selection->kind) {
case $selection instanceof FieldNode: case NodeKind::FIELD:
if (! $this->shouldIncludeNode($selection)) { if (! $this->shouldIncludeNode($selection)) {
break; break;
} }
@ -356,7 +354,7 @@ class ReferenceExecutor implements ExecutorImplementation
} }
$fields[$name][] = $selection; $fields[$name][] = $selection;
break; break;
case $selection instanceof InlineFragmentNode: case NodeKind::INLINE_FRAGMENT:
if (! $this->shouldIncludeNode($selection) || if (! $this->shouldIncludeNode($selection) ||
! $this->doesFragmentConditionMatch($selection, $runtimeType) ! $this->doesFragmentConditionMatch($selection, $runtimeType)
) { ) {
@ -369,7 +367,7 @@ class ReferenceExecutor implements ExecutorImplementation
$visitedFragmentNames $visitedFragmentNames
); );
break; break;
case $selection instanceof FragmentSpreadNode: case NodeKind::FRAGMENT_SPREAD:
$fragName = $selection->name->value; $fragName = $selection->name->value;
if (! empty($visitedFragmentNames[$fragName]) || ! $this->shouldIncludeNode($selection)) { if (! empty($visitedFragmentNames[$fragName]) || ! $this->shouldIncludeNode($selection)) {
break; break;
@ -377,7 +375,7 @@ class ReferenceExecutor implements ExecutorImplementation
$visitedFragmentNames[$fragName] = true; $visitedFragmentNames[$fragName] = true;
/** @var FragmentDefinitionNode|null $fragment */ /** @var FragmentDefinitionNode|null $fragment */
$fragment = $exeContext->fragments[$fragName] ?? null; $fragment = $exeContext->fragments[$fragName] ?? null;
if ($fragment === null || ! $this->doesFragmentConditionMatch($fragment, $runtimeType)) { if (! $fragment || ! $this->doesFragmentConditionMatch($fragment, $runtimeType)) {
break; break;
} }
$this->collectFields( $this->collectFields(
@ -398,8 +396,10 @@ class ReferenceExecutor implements ExecutorImplementation
* directives, where @skip has higher precedence than @include. * directives, where @skip has higher precedence than @include.
* *
* @param FragmentSpreadNode|FieldNode|InlineFragmentNode $node * @param FragmentSpreadNode|FieldNode|InlineFragmentNode $node
*
* @return bool
*/ */
private function shouldIncludeNode($node) : bool private function shouldIncludeNode($node)
{ {
$variableValues = $this->exeContext->variableValues; $variableValues = $this->exeContext->variableValues;
$skipDirective = Directive::skipDirective(); $skipDirective = Directive::skipDirective();
@ -423,10 +423,12 @@ class ReferenceExecutor implements ExecutorImplementation
/** /**
* Implements the logic to compute the key of a given fields entry * Implements the logic to compute the key of a given fields entry
*
* @return string
*/ */
private static function getFieldEntryKey(FieldNode $node) : string private static function getFieldEntryKey(FieldNode $node)
{ {
return $node->alias === null ? $node->name->value : $node->alias->value; return $node->alias ? $node->alias->value : $node->name->value;
} }
/** /**
@ -459,26 +461,26 @@ class ReferenceExecutor implements ExecutorImplementation
* Implements the "Evaluating selection sets" section of the spec * Implements the "Evaluating selection sets" section of the spec
* for "write" mode. * for "write" mode.
* *
* @param mixed $rootValue * @param mixed[] $sourceValue
* @param mixed[] $path * @param mixed[] $path
* @param ArrayObject $fields * @param ArrayObject $fields
* *
* @return Promise|stdClass|mixed[] * @return Promise|stdClass|mixed[]
*/ */
private function executeFieldsSerially(ObjectType $parentType, $rootValue, $path, $fields) private function executeFieldsSerially(ObjectType $parentType, $sourceValue, $path, $fields)
{ {
$result = $this->promiseReduce( $result = $this->promiseReduce(
array_keys($fields->getArrayCopy()), array_keys($fields->getArrayCopy()),
function ($results, $responseName) use ($path, $parentType, $rootValue, $fields) { function ($results, $responseName) use ($path, $parentType, $sourceValue, $fields) {
$fieldNodes = $fields[$responseName]; $fieldNodes = $fields[$responseName];
$fieldPath = $path; $fieldPath = $path;
$fieldPath[] = $responseName; $fieldPath[] = $responseName;
$result = $this->resolveField($parentType, $rootValue, $fieldNodes, $fieldPath); $result = $this->resolveField($parentType, $sourceValue, $fieldNodes, $fieldPath);
if ($result === self::$UNDEFINED) { if ($result === self::$UNDEFINED) {
return $results; return $results;
} }
$promise = $this->getPromise($result); $promise = $this->getPromise($result);
if ($promise !== null) { if ($promise) {
return $promise->then(static function ($resolvedResult) use ($responseName, $results) { return $promise->then(static function ($resolvedResult) use ($responseName, $results) {
$results[$responseName] = $resolvedResult; $results[$responseName] = $resolvedResult;
@ -501,33 +503,28 @@ class ReferenceExecutor implements ExecutorImplementation
} }
/** /**
* Resolves the field on the given root value. * Resolves the field on the given source object. In particular, this
* figures out the value that the field returns by calling its resolve function,
* then calls completeValue to complete promises, serialize scalars, or execute
* the sub-selection-set for objects.
* *
* In particular, this figures out the value that the field returns * @param object|null $source
* by calling its resolve function, then calls completeValue to complete promises,
* serialize scalars, or execute the sub-selection-set for objects.
*
* @param mixed $rootValue
* @param FieldNode[] $fieldNodes * @param FieldNode[] $fieldNodes
* @param mixed[] $path * @param mixed[] $path
* *
* @return mixed[]|Exception|mixed|null * @return mixed[]|Exception|mixed|null
*/ */
private function resolveField(ObjectType $parentType, $rootValue, $fieldNodes, $path) private function resolveField(ObjectType $parentType, $source, $fieldNodes, $path)
{ {
$exeContext = $this->exeContext; $exeContext = $this->exeContext;
$fieldNode = $fieldNodes[0]; $fieldNode = $fieldNodes[0];
$fieldName = $fieldNode->name->value; $fieldName = $fieldNode->name->value;
$fieldDef = $this->getFieldDef($exeContext->schema, $parentType, $fieldName); $fieldDef = $this->getFieldDef($exeContext->schema, $parentType, $fieldName);
if ($fieldDef === null) { if (! $fieldDef) {
return self::$UNDEFINED; return self::$UNDEFINED;
} }
$returnType = $fieldDef->getType(); $returnType = $fieldDef->getType();
// The resolve function's optional 3rd argument is a context value that // The resolve function's optional third argument is a collection of
// is provided to every resolve function within an execution. It is commonly
// used to represent an authenticated user, or request-specific caches.
$context = $exeContext->contextValue;
// The resolve function's optional 4th argument is a collection of
// information about the current execution state. // information about the current execution state.
$info = new ResolveInfo( $info = new ResolveInfo(
$fieldName, $fieldName,
@ -548,13 +545,17 @@ class ReferenceExecutor implements ExecutorImplementation
} else { } else {
$resolveFn = $this->exeContext->fieldResolver; $resolveFn = $this->exeContext->fieldResolver;
} }
// The resolve function's optional third argument is a context value that
// is provided to every resolve function within an execution. It is commonly
// used to represent an authenticated user, or request-specific caches.
$context = $exeContext->contextValue;
// Get the resolve function, regardless of if its result is normal // Get the resolve function, regardless of if its result is normal
// or abrupt (error). // or abrupt (error).
$result = $this->resolveOrError( $result = $this->resolveOrError(
$fieldDef, $fieldDef,
$fieldNode, $fieldNode,
$resolveFn, $resolveFn,
$rootValue, $source,
$context, $context,
$info $info
); );
@ -571,15 +572,18 @@ class ReferenceExecutor implements ExecutorImplementation
/** /**
* This method looks up the field on the given type definition. * This method looks up the field on the given type definition.
*
* It has special casing for the two introspection fields, __schema * It has special casing for the two introspection fields, __schema
* and __typename. __typename is special because it can always be * and __typename. __typename is special because it can always be
* queried as a field, even in situations where no other fields * queried as a field, even in situations where no other fields
* are allowed, like on a Union. __schema could get automatically * are allowed, like on a Union. __schema could get automatically
* added to the query type, but that would require mutating type * added to the query type, but that would require mutating type
* definitions, which would cause issues. * definitions, which would cause issues.
*
* @param string $fieldName
*
* @return FieldDefinition
*/ */
private function getFieldDef(Schema $schema, ObjectType $parentType, string $fieldName) : ?FieldDefinition private function getFieldDef(Schema $schema, ObjectType $parentType, $fieldName)
{ {
static $schemaMetaFieldDef, $typeMetaFieldDef, $typeNameMetaFieldDef; static $schemaMetaFieldDef, $typeMetaFieldDef, $typeNameMetaFieldDef;
$schemaMetaFieldDef = $schemaMetaFieldDef ?: Introspection::schemaMetaFieldDef(); $schemaMetaFieldDef = $schemaMetaFieldDef ?: Introspection::schemaMetaFieldDef();
@ -602,22 +606,22 @@ class ReferenceExecutor implements ExecutorImplementation
} }
/** /**
* Isolates the "ReturnOrAbrupt" behavior to not de-opt the `resolveField` function. * Isolates the "ReturnOrAbrupt" behavior to not de-opt the `resolveField`
* Returns the result of resolveFn or the abrupt-return Error object. * function. Returns the result of resolveFn or the abrupt-return Error object.
* *
* @param FieldDefinition $fieldDef * @param FieldDefinition $fieldDef
* @param FieldNode $fieldNode * @param FieldNode $fieldNode
* @param callable $resolveFn * @param callable $resolveFn
* @param mixed $rootValue * @param mixed $source
* @param mixed $context * @param mixed $context
* @param ResolveInfo $info * @param ResolveInfo $info
* *
* @return Throwable|Promise|mixed * @return Throwable|Promise|mixed
*/ */
private function resolveOrError($fieldDef, $fieldNode, $resolveFn, $rootValue, $context, $info) private function resolveOrError($fieldDef, $fieldNode, $resolveFn, $source, $context, $info)
{ {
try { try {
// Build a map of arguments from the field.arguments AST, using the // Build hash of arguments from the field.arguments AST, using the
// variables scope to fulfill any variable references. // variables scope to fulfill any variable references.
$args = Values::getArgumentValues( $args = Values::getArgumentValues(
$fieldDef, $fieldDef,
@ -625,7 +629,7 @@ class ReferenceExecutor implements ExecutorImplementation
$this->exeContext->variableValues $this->exeContext->variableValues
); );
return $resolveFn($rootValue, $args, $context, $info); return $resolveFn($source, $args, $context, $info);
} catch (Exception $error) { } catch (Exception $error) {
return $error; return $error;
} catch (Throwable $error) { } catch (Throwable $error) {
@ -673,13 +677,13 @@ class ReferenceExecutor implements ExecutorImplementation
$result $result
); );
$promise = $this->getPromise($completed); $promise = $this->getPromise($completed);
if ($promise !== null) { if ($promise) {
return $promise->then( return $promise->then(
null, null,
function ($error) use ($exeContext) { function ($error) use ($exeContext) {
$exeContext->addError($error); $exeContext->addError($error);
return $this->exeContext->promiseAdapter->createFulfilled(null); return $this->exeContext->promises->createFulfilled(null);
} }
); );
} }
@ -722,11 +726,11 @@ class ReferenceExecutor implements ExecutorImplementation
$result $result
); );
$promise = $this->getPromise($completed); $promise = $this->getPromise($completed);
if ($promise !== null) { if ($promise) {
return $promise->then( return $promise->then(
null, null,
function ($error) use ($fieldNodes, $path) { function ($error) use ($fieldNodes, $path) {
return $this->exeContext->promiseAdapter->createRejected(Error::createLocatedError( return $this->exeContext->promises->createRejected(Error::createLocatedError(
$error, $error,
$fieldNodes, $fieldNodes,
$path $path
@ -782,7 +786,7 @@ class ReferenceExecutor implements ExecutorImplementation
) { ) {
$promise = $this->getPromise($result); $promise = $this->getPromise($result);
// If result is a Promise, apply-lift over completeValue. // If result is a Promise, apply-lift over completeValue.
if ($promise !== null) { if ($promise) {
return $promise->then(function (&$resolved) use ($returnType, $fieldNodes, $info, $path) { return $promise->then(function (&$resolved) use ($returnType, $fieldNodes, $info, $path) {
return $this->completeValue($returnType, $fieldNodes, $info, $path, $resolved); return $this->completeValue($returnType, $fieldNodes, $info, $path, $resolved);
}); });
@ -820,7 +824,7 @@ class ReferenceExecutor implements ExecutorImplementation
// instance than `resolveType` or $field->getType() or $arg->getType() // instance than `resolveType` or $field->getType() or $arg->getType()
if ($returnType !== $this->exeContext->schema->getType($returnType->name)) { if ($returnType !== $this->exeContext->schema->getType($returnType->name)) {
$hint = ''; $hint = '';
if ($this->exeContext->schema->getConfig()->typeLoader !== null) { if ($this->exeContext->schema->getConfig()->typeLoader) {
$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',
$info->parentType, $info->parentType,
@ -858,7 +862,7 @@ class ReferenceExecutor implements ExecutorImplementation
*/ */
private function isPromise($value) private function isPromise($value)
{ {
return $value instanceof Promise || $this->exeContext->promiseAdapter->isThenable($value); return $value instanceof Promise || $this->exeContext->promises->isThenable($value);
} }
/** /**
@ -874,12 +878,12 @@ class ReferenceExecutor implements ExecutorImplementation
if ($value === null || $value instanceof Promise) { if ($value === null || $value instanceof Promise) {
return $value; return $value;
} }
if ($this->exeContext->promiseAdapter->isThenable($value)) { if ($this->exeContext->promises->isThenable($value)) {
$promise = $this->exeContext->promiseAdapter->convertThenable($value); $promise = $this->exeContext->promises->convertThenable($value);
if (! $promise instanceof Promise) { if (! $promise instanceof Promise) {
throw new InvariantViolation(sprintf( throw new InvariantViolation(sprintf(
'%s::convertThenable is expected to return instance of GraphQL\Executor\Promise\Promise, got: %s', '%s::convertThenable is expected to return instance of GraphQL\Executor\Promise\Promise, got: %s',
get_class($this->exeContext->promiseAdapter), get_class($this->exeContext->promises),
Utils::printSafe($promise) Utils::printSafe($promise)
)); ));
} }
@ -900,7 +904,7 @@ class ReferenceExecutor implements ExecutorImplementation
* @param mixed[] $values * @param mixed[] $values
* @param Promise|mixed|null $initialValue * @param Promise|mixed|null $initialValue
* *
* @return mixed * @return mixed[]
*/ */
private function promiseReduce(array $values, callable $callback, $initialValue) private function promiseReduce(array $values, callable $callback, $initialValue)
{ {
@ -908,7 +912,7 @@ class ReferenceExecutor implements ExecutorImplementation
$values, $values,
function ($previous, $value) use ($callback) { function ($previous, $value) use ($callback) {
$promise = $this->getPromise($previous); $promise = $this->getPromise($previous);
if ($promise !== null) { if ($promise) {
return $promise->then(static function ($resolved) use ($callback, $value) { return $promise->then(static function ($resolved) use ($callback, $value) {
return $callback($resolved, $value); return $callback($resolved, $value);
}); });
@ -921,40 +925,38 @@ class ReferenceExecutor implements ExecutorImplementation
} }
/** /**
* Complete a list value by completing each item in the list with the inner type. * Complete a list value by completing each item in the list with the
* inner type
* *
* @param FieldNode[] $fieldNodes * @param FieldNode[] $fieldNodes
* @param mixed[] $path * @param mixed[] $path
* @param mixed[]|Traversable $results * @param mixed $result
* *
* @return mixed[]|Promise * @return mixed[]|Promise
* *
* @throws Exception * @throws Exception
*/ */
private function completeListValue(ListOfType $returnType, $fieldNodes, ResolveInfo $info, $path, &$results) private function completeListValue(ListOfType $returnType, $fieldNodes, ResolveInfo $info, $path, &$result)
{ {
$itemType = $returnType->getWrappedType(); $itemType = $returnType->getWrappedType();
Utils::invariant( Utils::invariant(
is_array($results) || $results instanceof Traversable, is_array($result) || $result instanceof Traversable,
'User Error: expected iterable, but did not find one for field ' . $info->parentType . '.' . $info->fieldName . '.' 'User Error: expected iterable, but did not find one for field ' . $info->parentType . '.' . $info->fieldName . '.'
); );
$containsPromise = false; $containsPromise = false;
$i = 0; $i = 0;
$completedItems = []; $completedItems = [];
foreach ($results as $item) { foreach ($result as $item) {
$fieldPath = $path; $fieldPath = $path;
$fieldPath[] = $i++; $fieldPath[] = $i++;
$info->path = $fieldPath;
$completedItem = $this->completeValueCatchingError($itemType, $fieldNodes, $info, $fieldPath, $item); $completedItem = $this->completeValueCatchingError($itemType, $fieldNodes, $info, $fieldPath, $item);
if (! $containsPromise && $this->getPromise($completedItem) !== null) { if (! $containsPromise && $this->getPromise($completedItem)) {
$containsPromise = true; $containsPromise = true;
} }
$completedItems[] = $completedItem; $completedItems[] = $completedItem;
} }
return $containsPromise return $containsPromise ? $this->exeContext->promises->all($completedItems) : $completedItems;
? $this->exeContext->promiseAdapter->all($completedItems)
: $completedItems;
} }
/** /**
@ -1005,7 +1007,7 @@ class ReferenceExecutor implements ExecutorImplementation
$runtimeType = self::defaultTypeResolver($result, $exeContext->contextValue, $info, $returnType); $runtimeType = self::defaultTypeResolver($result, $exeContext->contextValue, $info, $returnType);
} }
$promise = $this->getPromise($runtimeType); $promise = $this->getPromise($runtimeType);
if ($promise !== null) { if ($promise) {
return $promise->then(function ($resolvedRuntimeType) use ( return $promise->then(function ($resolvedRuntimeType) use (
$returnType, $returnType,
$fieldNodes, $fieldNodes,
@ -1067,8 +1069,7 @@ class ReferenceExecutor implements ExecutorImplementation
) { ) {
return $value['__typename']; return $value['__typename'];
} }
if ($abstractType instanceof InterfaceType && $info->schema->getConfig()->typeLoader) {
if ($abstractType instanceof InterfaceType && $info->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 ' .
@ -1090,14 +1091,14 @@ class ReferenceExecutor implements ExecutorImplementation
continue; continue;
} }
$promise = $this->getPromise($isTypeOfResult); $promise = $this->getPromise($isTypeOfResult);
if ($promise !== null) { if ($promise) {
$promisedIsTypeOfResults[$index] = $promise; $promisedIsTypeOfResults[$index] = $promise;
} elseif ($isTypeOfResult) { } elseif ($isTypeOfResult) {
return $type; return $type;
} }
} }
if (! empty($promisedIsTypeOfResults)) { if (! empty($promisedIsTypeOfResults)) {
return $this->exeContext->promiseAdapter->all($promisedIsTypeOfResults) return $this->exeContext->promises->all($promisedIsTypeOfResults)
->then(static function ($isTypeOfResults) use ($possibleTypes) { ->then(static function ($isTypeOfResults) use ($possibleTypes) {
foreach ($isTypeOfResults as $index => $result) { foreach ($isTypeOfResults as $index => $result) {
if ($result) { if ($result) {
@ -1131,7 +1132,7 @@ class ReferenceExecutor implements ExecutorImplementation
$isTypeOf = $returnType->isTypeOf($result, $this->exeContext->contextValue, $info); $isTypeOf = $returnType->isTypeOf($result, $this->exeContext->contextValue, $info);
if ($isTypeOf !== null) { if ($isTypeOf !== null) {
$promise = $this->getPromise($isTypeOf); $promise = $this->getPromise($isTypeOf);
if ($promise !== null) { if ($promise) {
return $promise->then(function ($isTypeOfResult) use ( return $promise->then(function ($isTypeOfResult) use (
$returnType, $returnType,
$fieldNodes, $fieldNodes,
@ -1183,7 +1184,7 @@ class ReferenceExecutor implements ExecutorImplementation
/** /**
* @param FieldNode[] $fieldNodes * @param FieldNode[] $fieldNodes
* @param mixed[] $path * @param mixed[] $path
* @param mixed $result * @param mixed[] $result
* *
* @return mixed[]|Promise|stdClass * @return mixed[]|Promise|stdClass
* *
@ -1230,24 +1231,24 @@ class ReferenceExecutor implements ExecutorImplementation
* Implements the "Evaluating selection sets" section of the spec * Implements the "Evaluating selection sets" section of the spec
* for "read" mode. * for "read" mode.
* *
* @param mixed $rootValue * @param mixed|null $source
* @param mixed[] $path * @param mixed[] $path
* @param ArrayObject $fields * @param ArrayObject $fields
* *
* @return Promise|stdClass|mixed[] * @return Promise|stdClass|mixed[]
*/ */
private function executeFields(ObjectType $parentType, $rootValue, $path, $fields) private function executeFields(ObjectType $parentType, $source, $path, $fields)
{ {
$containsPromise = false; $containsPromise = false;
$finalResults = []; $finalResults = [];
foreach ($fields as $responseName => $fieldNodes) { foreach ($fields as $responseName => $fieldNodes) {
$fieldPath = $path; $fieldPath = $path;
$fieldPath[] = $responseName; $fieldPath[] = $responseName;
$result = $this->resolveField($parentType, $rootValue, $fieldNodes, $fieldPath); $result = $this->resolveField($parentType, $source, $fieldNodes, $fieldPath);
if ($result === self::$UNDEFINED) { if ($result === self::$UNDEFINED) {
continue; continue;
} }
if (! $containsPromise && $this->getPromise($result) !== null) { if (! $containsPromise && $this->getPromise($result)) {
$containsPromise = true; $containsPromise = true;
} }
$finalResults[$responseName] = $result; $finalResults[$responseName] = $result;
@ -1295,7 +1296,7 @@ class ReferenceExecutor implements ExecutorImplementation
{ {
$keys = array_keys($assoc); $keys = array_keys($assoc);
$valuesAndPromises = array_values($assoc); $valuesAndPromises = array_values($assoc);
$promise = $this->exeContext->promiseAdapter->all($valuesAndPromises); $promise = $this->exeContext->promises->all($valuesAndPromises);
return $promise->then(static function ($values) use ($keys) { return $promise->then(static function ($values) use ($keys) {
$resolvedResults = []; $resolvedResults = [];
@ -1309,6 +1310,7 @@ class ReferenceExecutor implements ExecutorImplementation
/** /**
* @param string|ObjectType|null $runtimeTypeOrName * @param string|ObjectType|null $runtimeTypeOrName
* @param FieldNode[] $fieldNodes
* @param mixed $result * @param mixed $result
* *
* @return ObjectType * @return ObjectType
@ -1319,9 +1321,9 @@ class ReferenceExecutor implements ExecutorImplementation
ResolveInfo $info, ResolveInfo $info,
&$result &$result
) { ) {
$runtimeType = is_string($runtimeTypeOrName) $runtimeType = is_string($runtimeTypeOrName) ?
? $this->exeContext->schema->getType($runtimeTypeOrName) $this->exeContext->schema->getType($runtimeTypeOrName) :
: $runtimeTypeOrName; $runtimeTypeOrName;
if (! $runtimeType instanceof ObjectType) { if (! $runtimeType instanceof ObjectType) {
throw new InvariantViolation( throw new InvariantViolation(
sprintf( sprintf(

View file

@ -92,7 +92,7 @@ class Values
), ),
[$varDefNode] [$varDefNode]
); );
} elseif ($varDefNode->defaultValue !== null) { } elseif ($varDefNode->defaultValue) {
$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 === null) { if (! $argumentValueNode) {
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 !== null && array_key_exists($variableName, $variableValues)) { if ($variableValues && 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.
@ -273,7 +273,6 @@ class Values
return $error->getMessage(); return $error->getMessage();
}, },
$errors $errors
) ) : [];
: [];
} }
} }

View file

@ -64,7 +64,7 @@ class Collector
foreach ($documentNode->definitions as $definitionNode) { foreach ($documentNode->definitions as $definitionNode) {
/** @var DefinitionNode|Node $definitionNode */ /** @var DefinitionNode|Node $definitionNode */
if ($definitionNode instanceof OperationDefinitionNode) { if ($definitionNode->kind === NodeKind::OPERATION_DEFINITION) {
/** @var OperationDefinitionNode $definitionNode */ /** @var OperationDefinitionNode $definitionNode */
if ($operationName === null && $this->operation !== null) { if ($operationName === null && $this->operation !== null) {
$hasMultipleAssumedOperations = true; $hasMultipleAssumedOperations = true;
@ -74,7 +74,7 @@ class Collector
) { ) {
$this->operation = $definitionNode; $this->operation = $definitionNode;
} }
} elseif ($definitionNode instanceof FragmentDefinitionNode) { } elseif ($definitionNode->kind === NodeKind::FRAGMENT_DEFINITION) {
/** @var FragmentDefinitionNode $definitionNode */ /** @var FragmentDefinitionNode $definitionNode */
$this->fragments[$definitionNode->name->value] = $definitionNode; $this->fragments[$definitionNode->name->value] = $definitionNode;
} }
@ -196,17 +196,17 @@ class Collector
} }
} }
if ($selection instanceof FieldNode) { if ($selection->kind === NodeKind::FIELD) {
/** @var FieldNode $selection */ /** @var FieldNode $selection */
$resultName = $selection->alias === null ? $selection->name->value : $selection->alias->value; $resultName = $selection->alias ? $selection->alias->value : $selection->name->value;
if (! isset($this->fields[$resultName])) { if (! isset($this->fields[$resultName])) {
$this->fields[$resultName] = []; $this->fields[$resultName] = [];
} }
$this->fields[$resultName][] = $selection; $this->fields[$resultName][] = $selection;
} elseif ($selection instanceof FragmentSpreadNode) { } elseif ($selection->kind === NodeKind::FRAGMENT_SPREAD) {
/** @var FragmentSpreadNode $selection */ /** @var FragmentSpreadNode $selection */
$fragmentName = $selection->name->value; $fragmentName = $selection->name->value;
@ -249,7 +249,7 @@ class Collector
} }
$this->doCollectFields($runtimeType, $fragmentDefinition->selectionSet); $this->doCollectFields($runtimeType, $fragmentDefinition->selectionSet);
} elseif ($selection instanceof InlineFragmentNode) { } elseif ($selection->kind === NodeKind::INLINE_FRAGMENT) {
/** @var InlineFragmentNode $selection */ /** @var InlineFragmentNode $selection */
if ($selection->typeCondition !== null) { if ($selection->typeCondition !== null) {

View file

@ -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 !== null) { if ($this->schema->getConfig()->typeLoader) {
$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,
@ -620,9 +620,8 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation
foreach ($value as $itemValue) { foreach ($value as $itemValue) {
++$index; ++$index;
$itemPath = $path; $itemPath = $path;
$itemPath[] = $index; // !!! use arrays COW semantics $itemPath[] = $index; // !!! use arrays COW semantics
$ctx->resolveInfo->path = $itemPath;
try { try {
if (! $this->completeValueFast($ctx, $itemType, $itemValue, $itemPath, $itemReturnValue)) { if (! $this->completeValueFast($ctx, $itemType, $itemValue, $itemPath, $itemReturnValue)) {
@ -647,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 !== null) { if ($this->schema->getConfig()->typeLoader) {
$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,
@ -821,10 +820,7 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation
} else { } else {
$childContexts = []; $childContexts = [];
foreach ($this->collector->collectFields( foreach ($this->collector->collectFields($objectType, $ctx->shared->mergedSelectionSet ?? $this->mergeSelectionSets($ctx)) as $childShared) {
$objectType,
$ctx->shared->mergedSelectionSet ?? $this->mergeSelectionSets($ctx)
) as $childShared) {
/** @var CoroutineContextShared $childShared */ /** @var CoroutineContextShared $childShared */
$childPath = $path; $childPath = $path;
@ -908,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 !== null) { if ($abstractType instanceof InterfaceType && $this->schema->getConfig()->typeLoader) {
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 ' .

View file

@ -9,6 +9,6 @@ class ListTypeNode extends Node implements TypeNode
/** @var string */ /** @var string */
public $kind = NodeKind::LIST_TYPE; public $kind = NodeKind::LIST_TYPE;
/** @var TypeNode */ /** @var Node */
public $type; public $type;
} }

View file

@ -69,7 +69,7 @@ class Location
$this->endToken = $endToken; $this->endToken = $endToken;
$this->source = $source; $this->source = $source;
if ($startToken === null || $endToken === null) { if (! $startToken || ! $endToken) {
return; return;
} }

View file

@ -106,7 +106,7 @@ abstract class Node
$tmp = (array) $this; $tmp = (array) $this;
if ($this->loc !== null) { if ($this->loc) {
$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 !== null) { if ($node->loc) {
$result['loc'] = [ $result['loc'] = [
'start' => $node->loc->start, 'start' => $node->loc->start,
'end' => $node->loc->end, 'end' => $node->loc->end,

View file

@ -9,6 +9,6 @@ class NonNullTypeNode extends Node implements TypeNode
/** @var string */ /** @var string */
public $kind = NodeKind::NON_NULL_TYPE; public $kind = NodeKind::NON_NULL_TYPE;
/** @var NamedTypeNode | ListTypeNode */ /** @var NameNode | ListTypeNode */
public $type; public $type;
} }

View file

@ -314,11 +314,11 @@ class Lexer
$start = $this->position; $start = $this->position;
[$char, $code] = $this->readChar(); [$char, $code] = $this->readChar();
while ($code !== null && ( while ($code && (
$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 !== null && } while ($code &&
// SourceCharacter but not LineTerminator // SourceCharacter but not LineTerminator
($code > 0x001F || $code === 0x0009) ($code > 0x001F || $code === 0x0009)
); );

View file

@ -512,15 +512,15 @@ class Parser
*/ */
private function parseVariableDefinitions() private function parseVariableDefinitions()
{ {
return $this->peek(Token::PAREN_L) return $this->peek(Token::PAREN_L) ?
? $this->many( $this->many(
Token::PAREN_L, Token::PAREN_L,
function () { function () {
return $this->parseVariableDefinition(); return $this->parseVariableDefinition();
}, },
Token::PAREN_R Token::PAREN_R
) ) :
: new NodeList([]); new NodeList([]);
} }
/** /**
@ -592,9 +592,9 @@ class Parser
*/ */
private function parseSelection() private function parseSelection()
{ {
return $this->peek(Token::SPREAD) return $this->peek(Token::SPREAD) ?
? $this->parseFragment() $this->parseFragment() :
: $this->parseField(); $this->parseField();
} }
/** /**
@ -634,17 +634,17 @@ class Parser
*/ */
private function parseArguments($isConst) private function parseArguments($isConst)
{ {
$parseFn = $isConst $parseFn = $isConst ?
? function () { function () {
return $this->parseConstArgument(); return $this->parseConstArgument();
} } :
: function () { function () {
return $this->parseArgument(); return $this->parseArgument();
}; };
return $this->peek(Token::PAREN_L) return $this->peek(Token::PAREN_L) ?
? $this->many(Token::PAREN_L, $parseFn, Token::PAREN_R) $this->many(Token::PAREN_L, $parseFn, Token::PAREN_R) :
: new NodeList([]); new NodeList([]);
} }
/** /**
@ -1208,8 +1208,8 @@ class Parser
do { do {
$types[] = $this->parseNamedType(); $types[] = $this->parseNamedType();
} while ($this->skip(Token::AMP) || } while ($this->skip(Token::AMP) ||
// Legacy support for the SDL? // Legacy support for the SDL?
(! empty($this->lexer->options['allowLegacySDLImplementsInterfaces']) && $this->peek(Token::NAME)) (! empty($this->lexer->options['allowLegacySDLImplementsInterfaces']) && $this->peek(Token::NAME))
); );
} }
@ -1545,8 +1545,7 @@ class Parser
Token::BRACE_L, Token::BRACE_L,
[$this, 'parseOperationTypeDefinition'], [$this, 'parseOperationTypeDefinition'],
Token::BRACE_R Token::BRACE_R
) ) : [];
: [];
if (count($directives) === 0 && count($operationTypes) === 0) { if (count($directives) === 0 && count($operationTypes) === 0) {
$this->unexpected(); $this->unexpected();
} }
@ -1656,7 +1655,9 @@ 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 && count($types) === 0) { if (count($directives) === 0 &&
! $types
) {
throw $this->unexpected(); throw $this->unexpected();
} }

View file

@ -28,7 +28,6 @@ use GraphQL\Language\AST\IntValueNode;
use GraphQL\Language\AST\ListTypeNode; use GraphQL\Language\AST\ListTypeNode;
use GraphQL\Language\AST\ListValueNode; use GraphQL\Language\AST\ListValueNode;
use GraphQL\Language\AST\NamedTypeNode; use GraphQL\Language\AST\NamedTypeNode;
use GraphQL\Language\AST\NameNode;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind; use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\NonNullTypeNode; use GraphQL\Language\AST\NonNullTypeNode;
@ -48,7 +47,6 @@ use GraphQL\Language\AST\StringValueNode;
use GraphQL\Language\AST\UnionTypeDefinitionNode; use GraphQL\Language\AST\UnionTypeDefinitionNode;
use GraphQL\Language\AST\UnionTypeExtensionNode; use GraphQL\Language\AST\UnionTypeExtensionNode;
use GraphQL\Language\AST\VariableDefinitionNode; use GraphQL\Language\AST\VariableDefinitionNode;
use GraphQL\Language\AST\VariableNode;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
use function count; use function count;
use function implode; use function implode;
@ -99,11 +97,11 @@ class Printer
$ast, $ast,
[ [
'leave' => [ 'leave' => [
NodeKind::NAME => static function (NameNode $node) { NodeKind::NAME => static function (Node $node) {
return '' . $node->value; return '' . $node->value;
}, },
NodeKind::VARIABLE => static function (VariableNode $node) { NodeKind::VARIABLE => static function ($node) {
return '$' . $node->name; return '$' . $node->name;
}, },

View file

@ -11,28 +11,28 @@ namespace GraphQL\Language;
class Token class Token
{ {
// Each kind of token. // Each kind of token.
public const SOF = '<SOF>'; const SOF = '<SOF>';
public const EOF = '<EOF>'; const EOF = '<EOF>';
public const BANG = '!'; const BANG = '!';
public const DOLLAR = '$'; const DOLLAR = '$';
public const AMP = '&'; const AMP = '&';
public const PAREN_L = '('; const PAREN_L = '(';
public const PAREN_R = ')'; const PAREN_R = ')';
public const SPREAD = '...'; const SPREAD = '...';
public const COLON = ':'; const COLON = ':';
public const EQUALS = '='; const EQUALS = '=';
public const AT = '@'; const AT = '@';
public const BRACKET_L = '['; const BRACKET_L = '[';
public const BRACKET_R = ']'; const BRACKET_R = ']';
public const BRACE_L = '{'; const BRACE_L = '{';
public const PIPE = '|'; const PIPE = '|';
public const BRACE_R = '}'; const BRACE_R = '}';
public const NAME = 'Name'; const NAME = 'Name';
public const INT = 'Int'; const INT = 'Int';
public const FLOAT = 'Float'; const FLOAT = 'Float';
public const STRING = 'String'; const STRING = 'String';
public const BLOCK_STRING = 'BlockString'; const BLOCK_STRING = 'BlockString';
public const COMMENT = 'Comment'; const COMMENT = 'Comment';
/** /**
* The kind of Token (see one of constants above). * The kind of Token (see one of constants above).
@ -104,15 +104,18 @@ class Token
$this->value = $value; $this->value = $value;
} }
public function getDescription() : string /**
* @return string
*/
public function getDescription()
{ {
return $this->kind . ($this->value === null ? '' : ' "' . $this->value . '"'); return $this->kind . ($this->value ? ' "' . $this->value . '"' : '');
} }
/** /**
* @return (string|int|null)[] * @return (string|int|null)[]
*/ */
public function toArray() : array public function toArray()
{ {
return [ return [
'kind' => $this->kind, 'kind' => $this->kind,

View file

@ -251,18 +251,8 @@ class Visitor
$inArray = $stack['inArray']; $inArray = $stack['inArray'];
$stack = $stack['prev']; $stack = $stack['prev'];
} else { } else {
$key = $parent !== null $key = $parent !== null ? ($inArray ? $index : $keys[$index]) : $UNDEFINED;
? ($inArray $node = $parent !== null ? ($parent instanceof NodeList || is_array($parent) ? $parent[$key] : $parent->{$key}) : $newRoot;
? $index
: $keys[$index]
)
: $UNDEFINED;
$node = $parent !== null
? ($parent instanceof NodeList || is_array($parent)
? $parent[$key]
: $parent->{$key}
)
: $newRoot;
if ($node === null || $node === $UNDEFINED) { if ($node === null || $node === $UNDEFINED) {
continue; continue;
} }

22
src/Schema.php Normal file
View file

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace GraphQL;
use function trigger_error;
use const E_USER_DEPRECATED;
trigger_error(
'GraphQL\Schema is moved to GraphQL\Type\Schema',
E_USER_DEPRECATED
);
/**
* Schema Definition
*
* @deprecated moved to GraphQL\Type\Schema
*/
class Schema extends \GraphQL\Type\Schema
{
}

View file

@ -73,14 +73,10 @@ class Helper
} }
if (stripos($contentType, 'application/graphql') !== false) { if (stripos($contentType, 'application/graphql') !== false) {
$rawBody = $readRawBodyFn $rawBody = $readRawBodyFn ? $readRawBodyFn() : $this->readRawBody();
? $readRawBodyFn()
: $this->readRawBody();
$bodyParams = ['query' => $rawBody ?: '']; $bodyParams = ['query' => $rawBody ?: ''];
} elseif (stripos($contentType, 'application/json') !== false) { } elseif (stripos($contentType, 'application/json') !== false) {
$rawBody = $readRawBodyFn ? $rawBody = $readRawBodyFn ? $readRawBodyFn() : $this->readRawBody();
$readRawBodyFn()
: $this->readRawBody();
$bodyParams = json_decode($rawBody ?: '', true); $bodyParams = json_decode($rawBody ?: '', true);
if (json_last_error()) { if (json_last_error()) {
@ -276,9 +272,7 @@ class Helper
); );
} }
$doc = $op->queryId $doc = $op->queryId ? $this->loadPersistedQuery($config, $op) : $op->query;
? $this->loadPersistedQuery($config, $op)
: $op->query;
if (! $doc instanceof DocumentNode) { if (! $doc instanceof DocumentNode) {
$doc = Parser::parse($doc); $doc = Parser::parse($doc);
@ -391,13 +385,13 @@ class Helper
*/ */
private function resolveRootValue(ServerConfig $config, OperationParams $params, DocumentNode $doc, $operationType) private function resolveRootValue(ServerConfig $config, OperationParams $params, DocumentNode $doc, $operationType)
{ {
$rootValue = $config->getRootValue(); $root = $config->getRootValue();
if (is_callable($rootValue)) { if (is_callable($root)) {
$rootValue = $rootValue($params, $doc, $operationType); $root = $root($params, $doc, $operationType);
} }
return $rootValue; return $root;
} }
/** /**

View file

@ -9,7 +9,6 @@ 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
@ -94,7 +93,7 @@ class OperationParams
} }
$tmp = json_decode($params[$param], true); $tmp = json_decode($params[$param], true);
if (json_last_error() !== JSON_ERROR_NONE) { if (json_last_error()) {
continue; continue;
} }

View file

@ -20,14 +20,11 @@ class BooleanType extends ScalarType
public $description = 'The `Boolean` scalar type represents `true` or `false`.'; public $description = 'The `Boolean` scalar type represents `true` or `false`.';
/** /**
* Coerce the given value to a boolean.
*
* The GraphQL spec leaves this up to the implementations, so we just do what
* PHP does natively to make this intuitive for developers.
*
* @param mixed $value * @param mixed $value
*
* @return bool
*/ */
public function serialize($value) : bool public function serialize($value)
{ {
return (bool) $value; return (bool) $value;
} }

View file

@ -8,19 +8,21 @@ use GraphQL\Language\AST\DirectiveDefinitionNode;
use GraphQL\Language\DirectiveLocation; 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 in_array;
use function is_array; use function is_array;
class Directive class Directive
{ {
public const DEFAULT_DEPRECATION_REASON = 'No longer supported'; public const DEFAULT_DEPRECATION_REASON = 'No longer supported';
public const INCLUDE_NAME = 'include'; const INCLUDE_NAME = 'include';
public const IF_ARGUMENT_NAME = 'if'; const IF_ARGUMENT_NAME = 'if';
public const SKIP_NAME = 'skip'; const SKIP_NAME = 'skip';
public const DEPRECATED_NAME = 'deprecated'; const DEPRECATED_NAME = 'deprecated';
public const REASON_ARGUMENT_NAME = 'reason'; const REASON_ARGUMENT_NAME = 'reason';
/** @var Directive[]|null */ /** @var Directive[] */
public static $internalDirectives; public static $internalDirectives;
// Schema Definitions // Schema Definitions
@ -82,9 +84,9 @@ class Directive
/** /**
* @return Directive[] * @return Directive[]
*/ */
public static function getInternalDirectives() : array public static function getInternalDirectives()
{ {
if (self::$internalDirectives === null) { if (! self::$internalDirectives) {
self::$internalDirectives = [ self::$internalDirectives = [
'include' => new self([ 'include' => new self([
'name' => self::INCLUDE_NAME, 'name' => self::INCLUDE_NAME,

View file

@ -69,9 +69,7 @@ class InputObjectType extends Type implements InputType, NullableType, NamedType
if ($this->fields === null) { if ($this->fields === null) {
$this->fields = []; $this->fields = [];
$fields = $this->config['fields'] ?? []; $fields = $this->config['fields'] ?? [];
$fields = is_callable($fields) $fields = is_callable($fields) ? call_user_func($fields) : $fields;
? call_user_func($fields)
: $fields;
if (! is_array($fields)) { if (! is_array($fields)) {
throw new InvariantViolation( throw new InvariantViolation(

View file

@ -31,8 +31,6 @@ class ListOfType extends Type implements WrappingType, OutputType, NullableType,
{ {
$type = $this->ofType; $type = $this->ofType;
return $recurse && $type instanceof WrappingType return $recurse && $type instanceof WrappingType ? $type->getWrappedType($recurse) : $type;
? $type->getWrappedType($recurse)
: $type;
} }
} }

View file

@ -66,8 +66,6 @@ class NonNull extends Type implements WrappingType, OutputType, InputType
{ {
$type = $this->ofType; $type = $this->ofType;
return $recurse && $type instanceof WrappingType return $recurse && $type instanceof WrappingType ? $type->getWrappedType($recurse) : $type;
? $type->getWrappedType($recurse)
: $type;
} }
} }

View file

@ -168,7 +168,7 @@ class ObjectType extends Type implements OutputType, CompositeType, NullableType
private function getInterfaceMap() private function getInterfaceMap()
{ {
if ($this->interfaceMap === null) { if (! $this->interfaceMap) {
$this->interfaceMap = []; $this->interfaceMap = [];
foreach ($this->getInterfaces() as $interface) { foreach ($this->getInterfaces() as $interface) {
$this->interfaceMap[$interface->name] = $interface; $this->interfaceMap[$interface->name] = $interface;
@ -185,9 +185,7 @@ class ObjectType extends Type implements OutputType, CompositeType, NullableType
{ {
if ($this->interfaces === null) { if ($this->interfaces === null) {
$interfaces = $this->config['interfaces'] ?? []; $interfaces = $this->config['interfaces'] ?? [];
$interfaces = is_callable($interfaces) $interfaces = is_callable($interfaces) ? call_user_func($interfaces) : $interfaces;
? call_user_func($interfaces)
: $interfaces;
if ($interfaces !== null && ! is_array($interfaces)) { if ($interfaces !== null && ! is_array($interfaces)) {
throw new InvariantViolation( throw new InvariantViolation(
@ -202,21 +200,19 @@ class ObjectType extends Type implements OutputType, CompositeType, NullableType
} }
/** /**
* @param mixed $value * @param mixed[] $value
* @param mixed[]|null $context * @param mixed[]|null $context
* *
* @return bool|null * @return bool|null
*/ */
public function isTypeOf($value, $context, ResolveInfo $info) public function isTypeOf($value, $context, ResolveInfo $info)
{ {
return isset($this->config['isTypeOf']) return isset($this->config['isTypeOf']) ? call_user_func(
? call_user_func( $this->config['isTypeOf'],
$this->config['isTypeOf'], $value,
$value, $context,
$context, $info
$info ) : null;
)
: null;
} }
/** /**

View file

@ -140,11 +140,9 @@ class QueryPlan
/** /**
* @return mixed[] * @return mixed[]
* *
* $parentType InterfaceType|ObjectType.
*
* @throws Error * @throws Error
*/ */
private function analyzeSelectionSet(SelectionSetNode $selectionSet, Type $parentType) : array private function analyzeSelectionSet(SelectionSetNode $selectionSet, ObjectType $parentType) : array
{ {
$fields = []; $fields = [];
foreach ($selectionSet->selections as $selectionNode) { foreach ($selectionSet->selections as $selectionNode) {

View file

@ -15,13 +15,12 @@ use function array_merge_recursive;
/** /**
* Structure containing information useful for field resolution process. * Structure containing information useful for field resolution process.
* * Passed as 3rd argument to every field resolver. See [docs on field resolving (data fetching)](data-fetching.md).
* Passed as 4th argument to every field resolver. See [docs on field resolving (data fetching)](data-fetching.md).
*/ */
class ResolveInfo class ResolveInfo
{ {
/** /**
* The name of the field being resolved. * The name of the field being resolved
* *
* @api * @api
* @var string * @var string
@ -37,7 +36,7 @@ class ResolveInfo
public $fieldNodes = []; public $fieldNodes = [];
/** /**
* Expected return type of the field being resolved. * Expected return type of the field being resolved
* *
* @api * @api
* @var ScalarType|ObjectType|InterfaceType|UnionType|EnumType|ListOfType|NonNull * @var ScalarType|ObjectType|InterfaceType|UnionType|EnumType|ListOfType|NonNull
@ -45,7 +44,7 @@ class ResolveInfo
public $returnType; public $returnType;
/** /**
* Parent type of the field being resolved. * Parent type of the field being resolved
* *
* @api * @api
* @var ObjectType * @var ObjectType
@ -53,7 +52,7 @@ class ResolveInfo
public $parentType; public $parentType;
/** /**
* Path to this field from the very root value. * Path to this field from the very root value
* *
* @api * @api
* @var string[][] * @var string[][]
@ -61,7 +60,7 @@ class ResolveInfo
public $path; public $path;
/** /**
* Instance of a schema used for execution. * Instance of a schema used for execution
* *
* @api * @api
* @var Schema * @var Schema
@ -69,7 +68,7 @@ class ResolveInfo
public $schema; public $schema;
/** /**
* AST of all fragments defined in query. * AST of all fragments defined in query
* *
* @api * @api
* @var FragmentDefinitionNode[] * @var FragmentDefinitionNode[]
@ -77,15 +76,15 @@ class ResolveInfo
public $fragments = []; public $fragments = [];
/** /**
* Root value passed to query execution. * Root value passed to query execution
* *
* @api * @api
* @var mixed * @var mixed|null
*/ */
public $rootValue; public $rootValue;
/** /**
* AST of operation definition node (query, mutation). * AST of operation definition node (query, mutation)
* *
* @api * @api
* @var OperationDefinitionNode|null * @var OperationDefinitionNode|null
@ -93,7 +92,7 @@ class ResolveInfo
public $operation; public $operation;
/** /**
* Array of variables passed to query execution. * Array of variables passed to query execution
* *
* @api * @api
* @var mixed[] * @var mixed[]
@ -137,7 +136,7 @@ class ResolveInfo
/** /**
* Helper method that returns names of all fields selected in query for * Helper method that returns names of all fields selected in query for
* $this->fieldName up to $depth levels. * $this->fieldName up to $depth levels
* *
* Example: * Example:
* query MyQuery{ * query MyQuery{
@ -178,10 +177,7 @@ class ResolveInfo
/** @var FieldNode $fieldNode */ /** @var FieldNode $fieldNode */
foreach ($this->fieldNodes as $fieldNode) { foreach ($this->fieldNodes as $fieldNode) {
$fields = array_merge_recursive( $fields = array_merge_recursive($fields, $this->foldSelectionSet($fieldNode->selectionSet, $depth));
$fields,
$this->foldSelectionSet($fieldNode->selectionSet, $depth)
);
} }
return $fields; return $fields;

View file

@ -345,9 +345,7 @@ abstract class Type implements JsonSerializable
*/ */
public static function getNullableType($type) public static function getNullableType($type)
{ {
return $type instanceof NonNull return $type instanceof NonNull ? $type->getWrappedType() : $type;
? $type->getWrappedType()
: $type;
} }
/** /**

View file

@ -488,9 +488,7 @@ EOD;
'type' => [ 'type' => [
'type' => Type::nonNull(self::_type()), 'type' => Type::nonNull(self::_type()),
'resolve' => static function ($value) { 'resolve' => static function ($value) {
return method_exists($value, 'getType') return method_exists($value, 'getType') ? $value->getType() : $value->type;
? $value->getType()
: $value->type;
}, },
], ],
'defaultValue' => [ 'defaultValue' => [
@ -695,7 +693,7 @@ EOD;
return self::$map['__DirectiveLocation']; return self::$map['__DirectiveLocation'];
} }
public static function schemaMetaFieldDef() : FieldDefinition public static function schemaMetaFieldDef()
{ {
if (! isset(self::$map[self::SCHEMA_FIELD_NAME])) { if (! isset(self::$map[self::SCHEMA_FIELD_NAME])) {
self::$map[self::SCHEMA_FIELD_NAME] = FieldDefinition::create([ self::$map[self::SCHEMA_FIELD_NAME] = FieldDefinition::create([
@ -717,7 +715,7 @@ EOD;
return self::$map[self::SCHEMA_FIELD_NAME]; return self::$map[self::SCHEMA_FIELD_NAME];
} }
public static function typeMetaFieldDef() : FieldDefinition public static function typeMetaFieldDef()
{ {
if (! isset(self::$map[self::TYPE_FIELD_NAME])) { if (! isset(self::$map[self::TYPE_FIELD_NAME])) {
self::$map[self::TYPE_FIELD_NAME] = FieldDefinition::create([ self::$map[self::TYPE_FIELD_NAME] = FieldDefinition::create([
@ -736,7 +734,7 @@ EOD;
return self::$map[self::TYPE_FIELD_NAME]; return self::$map[self::TYPE_FIELD_NAME];
} }
public static function typeNameMetaFieldDef() : FieldDefinition public static function typeNameMetaFieldDef()
{ {
if (! isset(self::$map[self::TYPE_NAME_FIELD_NAME])) { if (! isset(self::$map[self::TYPE_NAME_FIELD_NAME])) {
self::$map[self::TYPE_NAME_FIELD_NAME] = FieldDefinition::create([ self::$map[self::TYPE_NAME_FIELD_NAME] = FieldDefinition::create([

View file

@ -42,7 +42,7 @@ class SchemaConfig
/** @var Directive[] */ /** @var Directive[] */
public $directives; public $directives;
/** @var callable|null */ /** @var callable */
public $typeLoader; public $typeLoader;
/** @var SchemaDefinitionNode */ /** @var SchemaDefinitionNode */

View file

@ -29,7 +29,6 @@ use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Validation\InputObjectCircularRefs;
use GraphQL\Utils\TypeComparators; use GraphQL\Utils\TypeComparators;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
use function array_filter; use function array_filter;
@ -49,13 +48,9 @@ class SchemaValidationContext
/** @var Schema */ /** @var Schema */
private $schema; private $schema;
/** @var InputObjectCircularRefs */
private $inputObjectCircularRefs;
public function __construct(Schema $schema) public function __construct(Schema $schema)
{ {
$this->schema = $schema; $this->schema = $schema;
$this->inputObjectCircularRefs = new InputObjectCircularRefs($this);
} }
/** /**
@ -104,7 +99,7 @@ class SchemaValidationContext
* @param string $message * @param string $message
* @param Node[]|Node|TypeNode|TypeDefinitionNode|null $nodes * @param Node[]|Node|TypeNode|TypeDefinitionNode|null $nodes
*/ */
public function reportError($message, $nodes = null) private function reportError($message, $nodes = null)
{ {
$nodes = array_filter($nodes && is_array($nodes) ? $nodes : [$nodes]); $nodes = array_filter($nodes && is_array($nodes) ? $nodes : [$nodes]);
$this->addError(new Error($message, $nodes)); $this->addError(new Error($message, $nodes));
@ -252,7 +247,7 @@ class SchemaValidationContext
if (! $type instanceof NamedType) { if (! $type instanceof NamedType) {
$this->reportError( $this->reportError(
'Expected GraphQL named type but got: ' . Utils::printSafe($type) . '.', 'Expected GraphQL named type but got: ' . Utils::printSafe($type) . '.',
$type instanceof Type ? $type->astNode : null is_object($type) ? $type->astNode : null
); );
continue; continue;
} }
@ -280,9 +275,6 @@ class SchemaValidationContext
} elseif ($type instanceof InputObjectType) { } elseif ($type instanceof InputObjectType) {
// Ensure Input Object fields are valid. // Ensure Input Object fields are valid.
$this->validateInputFields($type); $this->validateInputFields($type);
// Ensure Input Objects do not contain non-nullable circular references
$this->inputObjectCircularRefs->validate($type);
} }
} }
} }
@ -768,9 +760,8 @@ class SchemaValidationContext
); );
} }
return $union->astNode return $union->astNode ?
? $union->astNode->types $union->astNode->types : null;
: null;
} }
private function validateEnumValues(EnumType $enumType) private function validateEnumValues(EnumType $enumType)
@ -825,9 +816,8 @@ class SchemaValidationContext
); );
} }
return $enum->astNode return $enum->astNode ?
? $enum->astNode->values $enum->astNode->values : null;
: null;
} }
private function validateInputFields(InputObjectType $inputObj) private function validateInputFields(InputObjectType $inputObj)

View file

@ -1,105 +0,0 @@
<?php
declare(strict_types=1);
namespace GraphQL\Type\Validation;
use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Type\Definition\InputObjectField;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\SchemaValidationContext;
use function array_map;
use function array_pop;
use function array_slice;
use function count;
use function implode;
class InputObjectCircularRefs
{
/** @var SchemaValidationContext */
private $schemaValidationContext;
/**
* Tracks already visited types to maintain O(N) and to ensure that cycles
* are not redundantly reported.
*
* @var InputObjectType[]
*/
private $visitedTypes = [];
/** @var InputObjectField[] */
private $fieldPath = [];
/**
* Position in the type path.
*
* [string $typeName => int $index]
*
* @var int[]
*/
private $fieldPathIndexByTypeName = [];
public function __construct(SchemaValidationContext $schemaValidationContext)
{
$this->schemaValidationContext = $schemaValidationContext;
}
/**
* This does a straight-forward DFS to find cycles.
* It does not terminate when a cycle was found but continues to explore
* the graph to find all possible cycles.
*/
public function validate(InputObjectType $inputObj) : void
{
if (isset($this->visitedTypes[$inputObj->name])) {
return;
}
$this->visitedTypes[$inputObj->name] = true;
$this->fieldPathIndexByTypeName[$inputObj->name] = count($this->fieldPath);
$fieldMap = $inputObj->getFields();
foreach ($fieldMap as $fieldName => $field) {
$type = $field->getType();
if ($type instanceof NonNull) {
$fieldType = $type->getWrappedType();
// If the type of the field is anything else then a non-nullable input object,
// there is no chance of an unbreakable cycle
if ($fieldType instanceof InputObjectType) {
$this->fieldPath[] = $field;
if (! isset($this->fieldPathIndexByTypeName[$fieldType->name])) {
$this->validate($fieldType);
} else {
$cycleIndex = $this->fieldPathIndexByTypeName[$fieldType->name];
$cyclePath = array_slice($this->fieldPath, $cycleIndex);
$fieldNames = array_map(
static function (InputObjectField $field) : string {
return $field->name;
},
$cyclePath
);
$this->schemaValidationContext->reportError(
'Cannot reference Input Object "' . $fieldType->name . '" within itself '
. 'through a series of non-null fields: "' . implode('.', $fieldNames) . '".',
array_map(
static function (InputObjectField $field) : ?InputValueDefinitionNode {
return $field->astNode;
},
$cyclePath
)
);
}
}
}
array_pop($this->fieldPath);
}
unset($this->fieldPathIndexByTypeName[$inputObj->name]);
}
}

View file

@ -183,7 +183,7 @@ class AST
$valuesNodes[] = $itemNode; $valuesNodes[] = $itemNode;
} }
return new ListValueNode(['values' => new NodeList($valuesNodes)]); return new ListValueNode(['values' => $valuesNodes]);
} }
return self::astFromValue($value, $itemType); return self::astFromValue($value, $itemType);
@ -235,7 +235,7 @@ class AST
]); ]);
} }
return new ObjectValueNode(['fields' => new NodeList($fieldNodes)]); return new ObjectValueNode(['fields' => $fieldNodes]);
} }
if ($type instanceof ScalarType || $type instanceof EnumType) { if ($type instanceof ScalarType || $type instanceof EnumType) {
@ -322,7 +322,7 @@ class AST
* *
* @api * @api
*/ */
public static function valueFromAST($valueNode, Type $type, ?array $variables = null) public static function valueFromAST($valueNode, InputType $type, ?array $variables = null)
{ {
$undefined = Utils::undefined(); $undefined = Utils::undefined();

View file

@ -17,6 +17,7 @@ use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\ListTypeNode; use GraphQL\Language\AST\ListTypeNode;
use GraphQL\Language\AST\NamedTypeNode; use GraphQL\Language\AST\NamedTypeNode;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\NonNullTypeNode; use GraphQL\Language\AST\NonNullTypeNode;
use GraphQL\Language\AST\ObjectTypeDefinitionNode; use GraphQL\Language\AST\ObjectTypeDefinitionNode;
use GraphQL\Language\AST\ScalarTypeDefinitionNode; use GraphQL\Language\AST\ScalarTypeDefinitionNode;
@ -244,20 +245,23 @@ class ASTDefinitionBuilder
* *
* @throws Error * @throws Error
*/ */
private function makeSchemaDef(Node $def) private function makeSchemaDef($def)
{ {
switch (true) { if (! $def) {
case $def instanceof ObjectTypeDefinitionNode: throw new Error('def must be defined.');
}
switch ($def->kind) {
case NodeKind::OBJECT_TYPE_DEFINITION:
return $this->makeTypeDef($def); return $this->makeTypeDef($def);
case $def instanceof InterfaceTypeDefinitionNode: case NodeKind::INTERFACE_TYPE_DEFINITION:
return $this->makeInterfaceDef($def); return $this->makeInterfaceDef($def);
case $def instanceof EnumTypeDefinitionNode: case NodeKind::ENUM_TYPE_DEFINITION:
return $this->makeEnumDef($def); return $this->makeEnumDef($def);
case $def instanceof UnionTypeDefinitionNode: case NodeKind::UNION_TYPE_DEFINITION:
return $this->makeUnionDef($def); return $this->makeUnionDef($def);
case $def instanceof ScalarTypeDefinitionNode: case NodeKind::SCALAR_TYPE_DEFINITION:
return $this->makeScalarDef($def); return $this->makeScalarDef($def);
case $def instanceof InputObjectTypeDefinitionNode: case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
return $this->makeInputObjectDef($def); return $this->makeInputObjectDef($def);
default: default:
throw new Error(sprintf('Type kind of %s not supported.', $def->kind)); throw new Error(sprintf('Type kind of %s not supported.', $def->kind));
@ -394,8 +398,8 @@ class ASTDefinitionBuilder
function ($typeNode) { function ($typeNode) {
return $this->buildType($typeNode); return $this->buildType($typeNode);
} }
) ) :
: [], [],
'astNode' => $def, 'astNode' => $def,
]); ]);
} }
@ -427,48 +431,62 @@ class ASTDefinitionBuilder
} }
/** /**
* @param mixed[] $config * @param ObjectTypeDefinitionNode|InterfaceTypeDefinitionNode|EnumTypeExtensionNode|ScalarTypeDefinitionNode|InputObjectTypeDefinitionNode $def
* @param mixed[] $config
* *
* @return CustomScalarType|EnumType|InputObjectType|InterfaceType|ObjectType|UnionType * @return CustomScalarType|EnumType|InputObjectType|InterfaceType|ObjectType|UnionType
* *
* @throws Error * @throws Error
*/ */
private function makeSchemaDefFromConfig(Node $def, array $config) private function makeSchemaDefFromConfig($def, array $config)
{ {
switch (true) { if (! $def) {
case $def instanceof ObjectTypeDefinitionNode: throw new Error('def must be defined.');
}
switch ($def->kind) {
case NodeKind::OBJECT_TYPE_DEFINITION:
return new ObjectType($config); return new ObjectType($config);
case $def instanceof InterfaceTypeDefinitionNode: case NodeKind::INTERFACE_TYPE_DEFINITION:
return new InterfaceType($config); return new InterfaceType($config);
case $def instanceof EnumTypeDefinitionNode: case NodeKind::ENUM_TYPE_DEFINITION:
return new EnumType($config); return new EnumType($config);
case $def instanceof UnionTypeDefinitionNode: case NodeKind::UNION_TYPE_DEFINITION:
return new UnionType($config); return new UnionType($config);
case $def instanceof ScalarTypeDefinitionNode: case NodeKind::SCALAR_TYPE_DEFINITION:
return new CustomScalarType($config); return new CustomScalarType($config);
case $def instanceof InputObjectTypeDefinitionNode: case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
return new InputObjectType($config); return new InputObjectType($config);
default: default:
throw new Error(sprintf('Type kind of %s not supported.', $def->kind)); throw new Error(sprintf('Type kind of %s not supported.', $def->kind));
} }
} }
private function getNamedTypeNode(TypeNode $typeNode) : TypeNode /**
* @param TypeNode|ListTypeNode|NonNullTypeNode $typeNode
*
* @return TypeNode
*/
private function getNamedTypeNode(TypeNode $typeNode)
{ {
$namedType = $typeNode; $namedType = $typeNode;
while ($namedType instanceof ListTypeNode || $namedType instanceof NonNullTypeNode) { while ($namedType->kind === NodeKind::LIST_TYPE || $namedType->kind === NodeKind::NON_NULL_TYPE) {
$namedType = $namedType->type; $namedType = $namedType->type;
} }
return $namedType; return $namedType;
} }
private function buildWrappedType(Type $innerType, TypeNode $inputTypeNode) : Type /**
* @param TypeNode|ListTypeNode|NonNullTypeNode $inputTypeNode
*
* @return Type
*/
private function buildWrappedType(Type $innerType, TypeNode $inputTypeNode)
{ {
if ($inputTypeNode instanceof ListTypeNode) { if ($inputTypeNode->kind === NodeKind::LIST_TYPE) {
return Type::listOf($this->buildWrappedType($innerType, $inputTypeNode->type)); return Type::listOf($this->buildWrappedType($innerType, $inputTypeNode->type));
} }
if ($inputTypeNode instanceof NonNullTypeNode) { if ($inputTypeNode->kind === NodeKind::NON_NULL_TYPE) {
$wrappedType = $this->buildWrappedType($innerType, $inputTypeNode->type); $wrappedType = $this->buildWrappedType($innerType, $inputTypeNode->type);
return Type::nonNull(NonNull::assertNullableType($wrappedType)); return Type::nonNull(NonNull::assertNullableType($wrappedType));

View file

@ -5,16 +5,10 @@ declare(strict_types=1);
namespace GraphQL\Utils; namespace GraphQL\Utils;
use GraphQL\Error\Error; use GraphQL\Error\Error;
use GraphQL\Language\AST\DirectiveDefinitionNode;
use GraphQL\Language\AST\DocumentNode; use GraphQL\Language\AST\DocumentNode;
use GraphQL\Language\AST\EnumTypeDefinitionNode;
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\ObjectTypeDefinitionNode; use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
use GraphQL\Language\AST\SchemaDefinitionNode; use GraphQL\Language\AST\SchemaDefinitionNode;
use GraphQL\Language\AST\UnionTypeDefinitionNode;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Language\Source; use GraphQL\Language\Source;
use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\Directive;
@ -101,38 +95,39 @@ class BuildSchema
public function buildSchema() public function buildSchema()
{ {
/** @var SchemaDefinitionNode $schemaDef */
$schemaDef = null; $schemaDef = null;
$typeDefs = []; $typeDefs = [];
$this->nodeMap = []; $this->nodeMap = [];
$directiveDefs = []; $directiveDefs = [];
foreach ($this->ast->definitions as $definition) { foreach ($this->ast->definitions as $d) {
switch (true) { switch ($d->kind) {
case $definition instanceof SchemaDefinitionNode: case NodeKind::SCHEMA_DEFINITION:
if ($schemaDef !== null) { if ($schemaDef) {
throw new Error('Must provide only one schema definition.'); throw new Error('Must provide only one schema definition.');
} }
$schemaDef = $definition; $schemaDef = $d;
break; break;
case $definition instanceof ScalarTypeDefinitionNode: case NodeKind::SCALAR_TYPE_DEFINITION:
case $definition instanceof ObjectTypeDefinitionNode: case NodeKind::OBJECT_TYPE_DEFINITION:
case $definition instanceof InterfaceTypeDefinitionNode: case NodeKind::INTERFACE_TYPE_DEFINITION:
case $definition instanceof EnumTypeDefinitionNode: case NodeKind::ENUM_TYPE_DEFINITION:
case $definition instanceof UnionTypeDefinitionNode: case NodeKind::UNION_TYPE_DEFINITION:
case $definition instanceof InputObjectTypeDefinitionNode: case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
$typeName = $definition->name->value; $typeName = $d->name->value;
if (! empty($this->nodeMap[$typeName])) { if (! empty($this->nodeMap[$typeName])) {
throw new Error(sprintf('Type "%s" was defined more than once.', $typeName)); throw new Error(sprintf('Type "%s" was defined more than once.', $typeName));
} }
$typeDefs[] = $definition; $typeDefs[] = $d;
$this->nodeMap[$typeName] = $definition; $this->nodeMap[$typeName] = $d;
break; break;
case $definition instanceof DirectiveDefinitionNode: case NodeKind::DIRECTIVE_DEFINITION:
$directiveDefs[] = $definition; $directiveDefs[] = $d;
break; break;
} }
} }
$operationTypes = $schemaDef !== null $operationTypes = $schemaDef
? $this->getOperationTypes($schemaDef) ? $this->getOperationTypes($schemaDef)
: [ : [
'query' => isset($this->nodeMap['Query']) ? 'Query' : null, 'query' => isset($this->nodeMap['Query']) ? 'Query' : null,
@ -159,10 +154,9 @@ class BuildSchema
// If specified directives were not explicitly declared, add them. // If specified directives were not explicitly declared, add them.
$skip = array_reduce( $skip = array_reduce(
$directives, $directives,
static function (bool $hasSkip, Directive $directive) : bool { static function ($hasSkip, $directive) {
return $hasSkip || $directive->name === 'skip'; return $hasSkip || $directive->name === 'skip';
}, }
false
); );
if (! $skip) { if (! $skip) {
$directives[] = Directive::skipDirective(); $directives[] = Directive::skipDirective();
@ -170,10 +164,9 @@ class BuildSchema
$include = array_reduce( $include = array_reduce(
$directives, $directives,
static function (bool $hasInclude, Directive $directive) : bool { static function ($hasInclude, $directive) {
return $hasInclude || $directive->name === 'include'; return $hasInclude || $directive->name === 'include';
}, }
false
); );
if (! $include) { if (! $include) {
$directives[] = Directive::includeDirective(); $directives[] = Directive::includeDirective();
@ -181,10 +174,9 @@ class BuildSchema
$deprecated = array_reduce( $deprecated = array_reduce(
$directives, $directives,
static function (bool $hasDeprecated, Directive $directive) : bool { static function ($hasDeprecated, $directive) {
return $hasDeprecated || $directive->name === 'deprecated'; return $hasDeprecated || $directive->name === 'deprecated';
}, }
false
); );
if (! $deprecated) { if (! $deprecated) {
$directives[] = Directive::deprecatedDirective(); $directives[] = Directive::deprecatedDirective();

View file

@ -21,7 +21,7 @@ use function is_string;
* Similar to PHP array, but allows any type of data to act as key (including arrays, objects, scalars) * Similar to PHP array, but allows any type of data to act as key (including arrays, objects, scalars)
* *
* Note: unfortunately when storing array as key - access and modification is O(N) * Note: unfortunately when storing array as key - access and modification is O(N)
* (yet this should rarely be the case and should be avoided when possible) * (yet this should be really rare case and should be avoided when possible)
*/ */
class MixedStore implements ArrayAccess class MixedStore implements ArrayAccess
{ {

View file

@ -7,16 +7,13 @@ namespace GraphQL\Utils;
use GraphQL\Error\Error; use GraphQL\Error\Error;
use GraphQL\Language\AST\DirectiveDefinitionNode; use GraphQL\Language\AST\DirectiveDefinitionNode;
use GraphQL\Language\AST\DocumentNode; use GraphQL\Language\AST\DocumentNode;
use GraphQL\Language\AST\EnumTypeExtensionNode;
use GraphQL\Language\AST\InputObjectTypeExtensionNode;
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\ObjectTypeExtensionNode; use GraphQL\Language\AST\ObjectTypeExtensionNode;
use GraphQL\Language\AST\SchemaDefinitionNode; use GraphQL\Language\AST\SchemaDefinitionNode;
use GraphQL\Language\AST\SchemaTypeExtensionNode; use GraphQL\Language\AST\SchemaTypeExtensionNode;
use GraphQL\Language\AST\TypeDefinitionNode; use GraphQL\Language\AST\TypeDefinitionNode;
use GraphQL\Language\AST\TypeExtensionNode; use GraphQL\Language\AST\TypeExtensionNode;
use GraphQL\Language\AST\UnionTypeExtensionNode;
use GraphQL\Type\Definition\CustomScalarType; use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\EnumType;
@ -78,8 +75,8 @@ class SchemaExtender
*/ */
protected static function checkExtensionNode(Type $type, Node $node) : void protected static function checkExtensionNode(Type $type, Node $node) : void
{ {
switch (true) { switch ($node->kind) {
case $node instanceof ObjectTypeExtensionNode: case NodeKind::OBJECT_TYPE_EXTENSION:
if (! ($type instanceof ObjectType)) { if (! ($type instanceof ObjectType)) {
throw new Error( throw new Error(
'Cannot extend non-object type "' . $type->name . '".', 'Cannot extend non-object type "' . $type->name . '".',
@ -87,7 +84,7 @@ class SchemaExtender
); );
} }
break; break;
case $node instanceof InterfaceTypeExtensionNode: case NodeKind::INTERFACE_TYPE_EXTENSION:
if (! ($type instanceof InterfaceType)) { if (! ($type instanceof InterfaceType)) {
throw new Error( throw new Error(
'Cannot extend non-interface type "' . $type->name . '".', 'Cannot extend non-interface type "' . $type->name . '".',
@ -95,7 +92,7 @@ class SchemaExtender
); );
} }
break; break;
case $node instanceof EnumTypeExtensionNode: case NodeKind::ENUM_TYPE_EXTENSION:
if (! ($type instanceof EnumType)) { if (! ($type instanceof EnumType)) {
throw new Error( throw new Error(
'Cannot extend non-enum type "' . $type->name . '".', 'Cannot extend non-enum type "' . $type->name . '".',
@ -103,7 +100,7 @@ class SchemaExtender
); );
} }
break; break;
case $node instanceof UnionTypeExtensionNode: case NodeKind::UNION_TYPE_EXTENSION:
if (! ($type instanceof UnionType)) { if (! ($type instanceof UnionType)) {
throw new Error( throw new Error(
'Cannot extend non-union type "' . $type->name . '".', 'Cannot extend non-union type "' . $type->name . '".',
@ -111,7 +108,7 @@ class SchemaExtender
); );
} }
break; break;
case $node instanceof InputObjectTypeExtensionNode: case NodeKind::INPUT_OBJECT_TYPE_EXTENSION:
if (! ($type instanceof InputObjectType)) { if (! ($type instanceof InputObjectType)) {
throw new Error( throw new Error(
'Cannot extend non-input object type "' . $type->name . '".', 'Cannot extend non-input object type "' . $type->name . '".',
@ -609,9 +606,7 @@ class SchemaExtender
} }
$schemaExtensionASTNodes = count($schemaExtensions) > 0 $schemaExtensionASTNodes = count($schemaExtensions) > 0
? ($schema->extensionASTNodes ? ($schema->extensionASTNodes ? array_merge($schema->extensionASTNodes, $schemaExtensions) : $schemaExtensions)
? array_merge($schema->extensionASTNodes, $schemaExtensions)
: $schemaExtensions)
: $schema->extensionASTNodes; : $schema->extensionASTNodes;
$types = array_merge( $types = array_merge(

View file

@ -353,8 +353,8 @@ class SchemaPrinter
private static function printObject(ObjectType $type, array $options) : string private static function printObject(ObjectType $type, array $options) : string
{ {
$interfaces = $type->getInterfaces(); $interfaces = $type->getInterfaces();
$implementedInterfaces = ! empty($interfaces) $implementedInterfaces = ! empty($interfaces) ?
? ' implements ' . implode( ' implements ' . implode(
' & ', ' & ',
array_map( array_map(
static function ($i) { static function ($i) {
@ -362,8 +362,7 @@ class SchemaPrinter
}, },
$interfaces $interfaces
) )
) ) : '';
: '';
return self::printDescription($options, $type) . return self::printDescription($options, $type) .
sprintf("type %s%s {\n%s\n}", $type->name, $implementedInterfaces, self::printFields($options, $type)); sprintf("type %s%s {\n%s\n}", $type->name, $implementedInterfaces, self::printFields($options, $type));

View file

@ -6,21 +6,12 @@ namespace GraphQL\Utils;
use GraphQL\Error\InvariantViolation; use GraphQL\Error\InvariantViolation;
use GraphQL\Error\Warning; use GraphQL\Error\Warning;
use GraphQL\Language\AST\ArgumentNode;
use GraphQL\Language\AST\DirectiveNode;
use GraphQL\Language\AST\EnumValueNode;
use GraphQL\Language\AST\FieldNode; use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\FragmentDefinitionNode;
use GraphQL\Language\AST\InlineFragmentNode;
use GraphQL\Language\AST\ListTypeNode; use GraphQL\Language\AST\ListTypeNode;
use GraphQL\Language\AST\ListValueNode;
use GraphQL\Language\AST\NamedTypeNode; use GraphQL\Language\AST\NamedTypeNode;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\NonNullTypeNode; use GraphQL\Language\AST\NonNullTypeNode;
use GraphQL\Language\AST\ObjectFieldNode;
use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Language\AST\SelectionSetNode;
use GraphQL\Language\AST\VariableDefinitionNode;
use GraphQL\Type\Definition\CompositeType; use GraphQL\Type\Definition\CompositeType;
use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\EnumType;
@ -263,13 +254,13 @@ class TypeInfo
// any assumptions of a valid schema to ensure runtime types are properly // any assumptions of a valid schema to ensure runtime types are properly
// checked before continuing since TypeInfo is used as part of validation // checked before continuing since TypeInfo is used as part of validation
// which occurs before guarantees of schema and document validity. // which occurs before guarantees of schema and document validity.
switch (true) { switch ($node->kind) {
case $node instanceof SelectionSetNode: case NodeKind::SELECTION_SET:
$namedType = Type::getNamedType($this->getType()); $namedType = Type::getNamedType($this->getType());
$this->parentTypeStack[] = Type::isCompositeType($namedType) ? $namedType : null; $this->parentTypeStack[] = Type::isCompositeType($namedType) ? $namedType : null;
break; break;
case $node instanceof FieldNode: case NodeKind::FIELD:
$parentType = $this->getParentType(); $parentType = $this->getParentType();
$fieldDef = null; $fieldDef = null;
if ($parentType) { if ($parentType) {
@ -283,11 +274,11 @@ class TypeInfo
$this->typeStack[] = Type::isOutputType($fieldType) ? $fieldType : null; $this->typeStack[] = Type::isOutputType($fieldType) ? $fieldType : null;
break; break;
case $node instanceof DirectiveNode: case NodeKind::DIRECTIVE:
$this->directive = $schema->getDirective($node->name->value); $this->directive = $schema->getDirective($node->name->value);
break; break;
case $node instanceof OperationDefinitionNode: case NodeKind::OPERATION_DEFINITION:
$type = null; $type = null;
if ($node->operation === 'query') { if ($node->operation === 'query') {
$type = $schema->getQueryType(); $type = $schema->getQueryType();
@ -299,24 +290,22 @@ class TypeInfo
$this->typeStack[] = Type::isOutputType($type) ? $type : null; $this->typeStack[] = Type::isOutputType($type) ? $type : null;
break; break;
case $node instanceof InlineFragmentNode: case NodeKind::INLINE_FRAGMENT:
case $node instanceof FragmentDefinitionNode: case NodeKind::FRAGMENT_DEFINITION:
$typeConditionNode = $node->typeCondition; $typeConditionNode = $node->typeCondition;
$outputType = $typeConditionNode $outputType = $typeConditionNode ? self::typeFromAST(
? self::typeFromAST( $schema,
$schema, $typeConditionNode
$typeConditionNode ) : Type::getNamedType($this->getType());
)
: Type::getNamedType($this->getType());
$this->typeStack[] = Type::isOutputType($outputType) ? $outputType : null; $this->typeStack[] = Type::isOutputType($outputType) ? $outputType : null;
break; break;
case $node instanceof VariableDefinitionNode: case NodeKind::VARIABLE_DEFINITION:
$inputType = self::typeFromAST($schema, $node->type); $inputType = self::typeFromAST($schema, $node->type);
$this->inputTypeStack[] = Type::isInputType($inputType) ? $inputType : null; // push $this->inputTypeStack[] = Type::isInputType($inputType) ? $inputType : null; // push
break; break;
case $node instanceof ArgumentNode: case NodeKind::ARGUMENT:
$fieldOrDirective = $this->getDirective() ?: $this->getFieldDef(); $fieldOrDirective = $this->getDirective() ?: $this->getFieldDef();
$argDef = $argType = null; $argDef = $argType = null;
if ($fieldOrDirective) { if ($fieldOrDirective) {
@ -334,7 +323,7 @@ class TypeInfo
$this->inputTypeStack[] = Type::isInputType($argType) ? $argType : null; $this->inputTypeStack[] = Type::isInputType($argType) ? $argType : null;
break; break;
case $node instanceof ListValueNode: case NodeKind::LST:
$listType = Type::getNullableType($this->getInputType()); $listType = Type::getNullableType($this->getInputType());
$itemType = $listType instanceof ListOfType $itemType = $listType instanceof ListOfType
? $listType->getWrappedType() ? $listType->getWrappedType()
@ -342,7 +331,7 @@ class TypeInfo
$this->inputTypeStack[] = Type::isInputType($itemType) ? $itemType : null; $this->inputTypeStack[] = Type::isInputType($itemType) ? $itemType : null;
break; break;
case $node instanceof ObjectFieldNode: case NodeKind::OBJECT_FIELD:
$objectType = Type::getNamedType($this->getInputType()); $objectType = Type::getNamedType($this->getInputType());
$fieldType = null; $fieldType = null;
$inputFieldType = null; $inputFieldType = null;
@ -354,7 +343,7 @@ class TypeInfo
$this->inputTypeStack[] = Type::isInputType($inputFieldType) ? $inputFieldType : null; $this->inputTypeStack[] = Type::isInputType($inputFieldType) ? $inputFieldType : null;
break; break;
case $node instanceof EnumValueNode: case NodeKind::ENUM:
$enumType = Type::getNamedType($this->getInputType()); $enumType = Type::getNamedType($this->getInputType());
$enumValue = null; $enumValue = null;
if ($enumType instanceof EnumType) { if ($enumType instanceof EnumType) {
@ -468,37 +457,37 @@ class TypeInfo
public function leave(Node $node) public function leave(Node $node)
{ {
switch (true) { switch ($node->kind) {
case $node instanceof SelectionSetNode: case NodeKind::SELECTION_SET:
array_pop($this->parentTypeStack); array_pop($this->parentTypeStack);
break; break;
case $node instanceof FieldNode: case NodeKind::FIELD:
array_pop($this->fieldDefStack); array_pop($this->fieldDefStack);
array_pop($this->typeStack); array_pop($this->typeStack);
break; break;
case $node instanceof DirectiveNode: case NodeKind::DIRECTIVE:
$this->directive = null; $this->directive = null;
break; break;
case $node instanceof OperationDefinitionNode: case NodeKind::OPERATION_DEFINITION:
case $node instanceof InlineFragmentNode: case NodeKind::INLINE_FRAGMENT:
case $node instanceof FragmentDefinitionNode: case NodeKind::FRAGMENT_DEFINITION:
array_pop($this->typeStack); array_pop($this->typeStack);
break; break;
case $node instanceof VariableDefinitionNode: case NodeKind::VARIABLE_DEFINITION:
array_pop($this->inputTypeStack); array_pop($this->inputTypeStack);
break; break;
case $node instanceof ArgumentNode: case NodeKind::ARGUMENT:
$this->argument = null; $this->argument = null;
array_pop($this->inputTypeStack); array_pop($this->inputTypeStack);
break; break;
case $node instanceof ListValueNode: case NodeKind::LST:
case $node instanceof ObjectFieldNode: case NodeKind::OBJECT_FIELD:
array_pop($this->inputTypeStack); array_pop($this->inputTypeStack);
break; break;
case $node instanceof EnumValueNode: case NodeKind::ENUM:
$this->enumValue = null; $this->enumValue = null;
break; break;
} }

View file

@ -199,7 +199,7 @@ class Value
} }
$suggestions = Utils::suggestionList( $suggestions = Utils::suggestionList(
(string) $fieldName, $fieldName,
array_keys($fields) array_keys($fields)
); );
$didYouMean = $suggestions $didYouMean = $suggestions

View file

@ -6,8 +6,6 @@ namespace GraphQL\Validator\Rules;
use GraphQL\Error\Error; use GraphQL\Error\Error;
use GraphQL\Language\AST\ArgumentNode; use GraphQL\Language\AST\ArgumentNode;
use GraphQL\Language\AST\DirectiveNode;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind; use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\NodeList; use GraphQL\Language\AST\NodeList;
@ -36,7 +34,7 @@ class KnownArgumentNames extends ValidationRule
} }
$argumentOf = $ancestors[count($ancestors) - 1]; $argumentOf = $ancestors[count($ancestors) - 1];
if ($argumentOf instanceof FieldNode) { if ($argumentOf->kind === NodeKind::FIELD) {
$fieldDef = $context->getFieldDef(); $fieldDef = $context->getFieldDef();
$parentType = $context->getParentType(); $parentType = $context->getParentType();
if ($fieldDef && $parentType) { if ($fieldDef && $parentType) {
@ -58,7 +56,7 @@ class KnownArgumentNames extends ValidationRule
[$node] [$node]
)); ));
} }
} elseif ($argumentOf instanceof DirectiveNode) { } elseif ($argumentOf->kind === NodeKind::DIRECTIVE) {
$directive = $context->getDirective(); $directive = $context->getDirective();
if ($directive) { if ($directive) {
$context->reportError(new Error( $context->reportError(new Error(

View file

@ -7,31 +7,10 @@ namespace GraphQL\Validator\Rules;
use GraphQL\Error\Error; use GraphQL\Error\Error;
use GraphQL\Language\AST\DirectiveDefinitionNode; use GraphQL\Language\AST\DirectiveDefinitionNode;
use GraphQL\Language\AST\DirectiveNode; use GraphQL\Language\AST\DirectiveNode;
use GraphQL\Language\AST\EnumTypeDefinitionNode;
use GraphQL\Language\AST\EnumTypeExtensionNode;
use GraphQL\Language\AST\EnumValueDefinitionNode;
use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\FragmentDefinitionNode;
use GraphQL\Language\AST\FragmentSpreadNode;
use GraphQL\Language\AST\InlineFragmentNode;
use GraphQL\Language\AST\InputObjectTypeDefinitionNode; use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
use GraphQL\Language\AST\InputObjectTypeExtensionNode;
use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind; use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\NodeList; use GraphQL\Language\AST\NodeList;
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
use GraphQL\Language\AST\ObjectTypeExtensionNode;
use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
use GraphQL\Language\AST\ScalarTypeExtensionNode;
use GraphQL\Language\AST\SchemaDefinitionNode;
use GraphQL\Language\AST\SchemaTypeExtensionNode;
use GraphQL\Language\AST\UnionTypeDefinitionNode;
use GraphQL\Language\AST\UnionTypeExtensionNode;
use GraphQL\Language\DirectiveLocation; use GraphQL\Language\DirectiveLocation;
use GraphQL\Validator\ValidationContext; use GraphQL\Validator\ValidationContext;
use function array_map; use function array_map;
@ -117,8 +96,8 @@ class KnownDirectives extends ValidationRule
private function getDirectiveLocationForASTPath(array $ancestors) private function getDirectiveLocationForASTPath(array $ancestors)
{ {
$appliedTo = $ancestors[count($ancestors) - 1]; $appliedTo = $ancestors[count($ancestors) - 1];
switch (true) { switch ($appliedTo->kind) {
case $appliedTo instanceof OperationDefinitionNode: case NodeKind::OPERATION_DEFINITION:
switch ($appliedTo->operation) { switch ($appliedTo->operation) {
case 'query': case 'query':
return DirectiveLocation::QUERY; return DirectiveLocation::QUERY;
@ -128,40 +107,40 @@ class KnownDirectives extends ValidationRule
return DirectiveLocation::SUBSCRIPTION; return DirectiveLocation::SUBSCRIPTION;
} }
break; break;
case $appliedTo instanceof FieldNode: case NodeKind::FIELD:
return DirectiveLocation::FIELD; return DirectiveLocation::FIELD;
case $appliedTo instanceof FragmentSpreadNode: case NodeKind::FRAGMENT_SPREAD:
return DirectiveLocation::FRAGMENT_SPREAD; return DirectiveLocation::FRAGMENT_SPREAD;
case $appliedTo instanceof InlineFragmentNode: case NodeKind::INLINE_FRAGMENT:
return DirectiveLocation::INLINE_FRAGMENT; return DirectiveLocation::INLINE_FRAGMENT;
case $appliedTo instanceof FragmentDefinitionNode: case NodeKind::FRAGMENT_DEFINITION:
return DirectiveLocation::FRAGMENT_DEFINITION; return DirectiveLocation::FRAGMENT_DEFINITION;
case $appliedTo instanceof SchemaDefinitionNode: case NodeKind::SCHEMA_DEFINITION:
case $appliedTo instanceof SchemaTypeExtensionNode: case NodeKind::SCHEMA_EXTENSION:
return DirectiveLocation::SCHEMA; return DirectiveLocation::SCHEMA;
case $appliedTo instanceof ScalarTypeDefinitionNode: case NodeKind::SCALAR_TYPE_DEFINITION:
case $appliedTo instanceof ScalarTypeExtensionNode: case NodeKind::SCALAR_TYPE_EXTENSION:
return DirectiveLocation::SCALAR; return DirectiveLocation::SCALAR;
case $appliedTo instanceof ObjectTypeDefinitionNode: case NodeKind::OBJECT_TYPE_DEFINITION:
case $appliedTo instanceof ObjectTypeExtensionNode: case NodeKind::OBJECT_TYPE_EXTENSION:
return DirectiveLocation::OBJECT; return DirectiveLocation::OBJECT;
case $appliedTo instanceof FieldDefinitionNode: case NodeKind::FIELD_DEFINITION:
return DirectiveLocation::FIELD_DEFINITION; return DirectiveLocation::FIELD_DEFINITION;
case $appliedTo instanceof InterfaceTypeDefinitionNode: case NodeKind::INTERFACE_TYPE_DEFINITION:
case $appliedTo instanceof InterfaceTypeExtensionNode: case NodeKind::INTERFACE_TYPE_EXTENSION:
return DirectiveLocation::IFACE; return DirectiveLocation::IFACE;
case $appliedTo instanceof UnionTypeDefinitionNode: case NodeKind::UNION_TYPE_DEFINITION:
case $appliedTo instanceof UnionTypeExtensionNode: case NodeKind::UNION_TYPE_EXTENSION:
return DirectiveLocation::UNION; return DirectiveLocation::UNION;
case $appliedTo instanceof EnumTypeDefinitionNode: case NodeKind::ENUM_TYPE_DEFINITION:
case $appliedTo instanceof EnumTypeExtensionNode: case NodeKind::ENUM_TYPE_EXTENSION:
return DirectiveLocation::ENUM; return DirectiveLocation::ENUM;
case $appliedTo instanceof EnumValueDefinitionNode: case NodeKind::ENUM_VALUE_DEFINITION:
return DirectiveLocation::ENUM_VALUE; return DirectiveLocation::ENUM_VALUE;
case $appliedTo instanceof InputObjectTypeDefinitionNode: case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
case $appliedTo instanceof InputObjectTypeExtensionNode: case NodeKind::INPUT_OBJECT_TYPE_EXTENSION:
return DirectiveLocation::INPUT_OBJECT; return DirectiveLocation::INPUT_OBJECT;
case $appliedTo instanceof InputValueDefinitionNode: case NodeKind::INPUT_VALUE_DEFINITION:
$parentNode = $ancestors[count($ancestors) - 3]; $parentNode = $ancestors[count($ancestors) - 3];
return $parentNode instanceof InputObjectTypeDefinitionNode return $parentNode instanceof InputObjectTypeDefinitionNode

View file

@ -30,7 +30,7 @@ class LoneAnonymousOperation extends ValidationRule
$tmp = Utils::filter( $tmp = Utils::filter(
$node->definitions, $node->definitions,
static function (Node $definition) { static function (Node $definition) {
return $definition instanceof OperationDefinitionNode; return $definition->kind === NodeKind::OPERATION_DEFINITION;
} }
); );

View file

@ -19,14 +19,12 @@ class LoneSchemaDefinition extends ValidationRule
public function getVisitor(ValidationContext $context) public function getVisitor(ValidationContext $context)
{ {
$oldSchema = $context->getSchema(); $oldSchema = $context->getSchema();
$alreadyDefined = $oldSchema !== null $alreadyDefined = $oldSchema !== null ? (
? ( $oldSchema->getAstNode() ||
$oldSchema->getAstNode() || $oldSchema->getQueryType() ||
$oldSchema->getQueryType() || $oldSchema->getMutationType() ||
$oldSchema->getMutationType() || $oldSchema->getSubscriptionType()
$oldSchema->getSubscriptionType() ) : false;
)
: false;
$schemaDefinitionsCount = 0; $schemaDefinitionsCount = 0;

View file

@ -473,24 +473,24 @@ class OverlappingFieldsCanBeMerged extends ValidationRule
private function doTypesConflict(OutputType $type1, OutputType $type2) private function doTypesConflict(OutputType $type1, OutputType $type2)
{ {
if ($type1 instanceof ListOfType) { if ($type1 instanceof ListOfType) {
return $type2 instanceof ListOfType return $type2 instanceof ListOfType ?
? $this->doTypesConflict($type1->getWrappedType(), $type2->getWrappedType()) $this->doTypesConflict($type1->getWrappedType(), $type2->getWrappedType()) :
: true; true;
} }
if ($type2 instanceof ListOfType) { if ($type2 instanceof ListOfType) {
return $type1 instanceof ListOfType return $type1 instanceof ListOfType ?
? $this->doTypesConflict($type1->getWrappedType(), $type2->getWrappedType()) $this->doTypesConflict($type1->getWrappedType(), $type2->getWrappedType()) :
: true; true;
} }
if ($type1 instanceof NonNull) { if ($type1 instanceof NonNull) {
return $type2 instanceof NonNull return $type2 instanceof NonNull ?
? $this->doTypesConflict($type1->getWrappedType(), $type2->getWrappedType()) $this->doTypesConflict($type1->getWrappedType(), $type2->getWrappedType()) :
: true; true;
} }
if ($type2 instanceof NonNull) { if ($type2 instanceof NonNull) {
return $type1 instanceof NonNull return $type1 instanceof NonNull ?
? $this->doTypesConflict($type1->getWrappedType(), $type2->getWrappedType()) $this->doTypesConflict($type1->getWrappedType(), $type2->getWrappedType()) :
: true; true;
} }
if (Type::isLeafType($type1) || Type::isLeafType($type2)) { if (Type::isLeafType($type1) || Type::isLeafType($type2)) {
return $type1 !== $type2; return $type1 !== $type2;

View file

@ -66,15 +66,13 @@ class ProvidedRequiredArgumentsOnDirectives extends ValidationRule
} }
$requiredArgsMap[$def->name->value] = Utils::keyMap( $requiredArgsMap[$def->name->value] = Utils::keyMap(
$arguments $arguments ? array_filter($arguments, static function (Node $argument) : bool {
? array_filter($arguments, static function (Node $argument) : bool { return $argument instanceof NonNullTypeNode &&
return $argument instanceof NonNullTypeNode && (
( ! isset($argument->defaultValue) ||
! isset($argument->defaultValue) || $argument->defaultValue === null
$argument->defaultValue === null );
); }) : [],
})
: [],
static function (NamedTypeNode $argument) : string { static function (NamedTypeNode $argument) : string {
return $argument->name->value; return $argument->name->value;
} }

View file

@ -110,8 +110,9 @@ class QueryComplexity extends QuerySecurityRule
private function nodeComplexity(Node $node, $complexity = 0) private function nodeComplexity(Node $node, $complexity = 0)
{ {
switch (true) { switch ($node->kind) {
case $node instanceof FieldNode: case NodeKind::FIELD:
/** @var FieldNode $node */
// default values // default values
$args = []; $args = [];
$complexityFn = FieldDefinition::DEFAULT_COMPLEXITY_FN; $complexityFn = FieldDefinition::DEFAULT_COMPLEXITY_FN;
@ -142,14 +143,16 @@ class QueryComplexity extends QuerySecurityRule
$complexity += call_user_func_array($complexityFn, [$childrenComplexity, $args]); $complexity += call_user_func_array($complexityFn, [$childrenComplexity, $args]);
break; break;
case $node instanceof InlineFragmentNode: case NodeKind::INLINE_FRAGMENT:
/** @var InlineFragmentNode $node */
// node has children? // node has children?
if (isset($node->selectionSet)) { if (isset($node->selectionSet)) {
$complexity = $this->fieldComplexity($node, $complexity); $complexity = $this->fieldComplexity($node, $complexity);
} }
break; break;
case $node instanceof FragmentSpreadNode: case NodeKind::FRAGMENT_SPREAD:
/** @var FragmentSpreadNode $node */
$fragment = $this->getFragment($node); $fragment = $this->getFragment($node);
if ($fragment !== null) { if ($fragment !== null) {

View file

@ -5,9 +5,6 @@ declare(strict_types=1);
namespace GraphQL\Validator\Rules; namespace GraphQL\Validator\Rules;
use GraphQL\Error\Error; use GraphQL\Error\Error;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\FragmentSpreadNode;
use GraphQL\Language\AST\InlineFragmentNode;
use GraphQL\Language\AST\Node; use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind; use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\OperationDefinitionNode; use GraphQL\Language\AST\OperationDefinitionNode;
@ -60,8 +57,9 @@ class QueryDepth extends QuerySecurityRule
private function nodeDepth(Node $node, $depth = 0, $maxDepth = 0) private function nodeDepth(Node $node, $depth = 0, $maxDepth = 0)
{ {
switch (true) { switch ($node->kind) {
case $node instanceof FieldNode: case NodeKind::FIELD:
/** @var FieldNode $node */
// node has children? // node has children?
if ($node->selectionSet !== null) { if ($node->selectionSet !== null) {
// update maxDepth if needed // update maxDepth if needed
@ -72,14 +70,16 @@ class QueryDepth extends QuerySecurityRule
} }
break; break;
case $node instanceof InlineFragmentNode: case NodeKind::INLINE_FRAGMENT:
/** @var InlineFragmentNode $node */
// node has children? // node has children?
if ($node->selectionSet !== null) { if ($node->selectionSet !== null) {
$maxDepth = $this->fieldDepth($node, $depth, $maxDepth); $maxDepth = $this->fieldDepth($node, $depth, $maxDepth);
} }
break; break;
case $node instanceof FragmentSpreadNode: case NodeKind::FRAGMENT_SPREAD:
/** @var FragmentSpreadNode $node */
$fragment = $this->getFragment($node); $fragment = $this->getFragment($node);
if ($fragment !== null) { if ($fragment !== null) {

View file

@ -9,6 +9,7 @@ use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\FragmentDefinitionNode; use GraphQL\Language\AST\FragmentDefinitionNode;
use GraphQL\Language\AST\FragmentSpreadNode; use GraphQL\Language\AST\FragmentSpreadNode;
use GraphQL\Language\AST\InlineFragmentNode; use GraphQL\Language\AST\InlineFragmentNode;
use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\SelectionSetNode; use GraphQL\Language\AST\SelectionSetNode;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Introspection; use GraphQL\Type\Introspection;
@ -113,8 +114,8 @@ abstract class QuerySecurityRule extends ValidationRule
$_astAndDefs = $astAndDefs ?: new ArrayObject(); $_astAndDefs = $astAndDefs ?: new ArrayObject();
foreach ($selectionSet->selections as $selection) { foreach ($selectionSet->selections as $selection) {
switch (true) { switch ($selection->kind) {
case $selection instanceof FieldNode: case NodeKind::FIELD:
/** @var FieldNode $selection */ /** @var FieldNode $selection */
$fieldName = $selection->name->value; $fieldName = $selection->name->value;
$fieldDef = null; $fieldDef = null;
@ -141,7 +142,7 @@ abstract class QuerySecurityRule extends ValidationRule
// create field context // create field context
$_astAndDefs[$responseName][] = [$selection, $fieldDef]; $_astAndDefs[$responseName][] = [$selection, $fieldDef];
break; break;
case $selection instanceof InlineFragmentNode: case NodeKind::INLINE_FRAGMENT:
/** @var InlineFragmentNode $selection */ /** @var InlineFragmentNode $selection */
$_astAndDefs = $this->collectFieldASTsAndDefs( $_astAndDefs = $this->collectFieldASTsAndDefs(
$context, $context,
@ -151,7 +152,7 @@ abstract class QuerySecurityRule extends ValidationRule
$_astAndDefs $_astAndDefs
); );
break; break;
case $selection instanceof FragmentSpreadNode: case NodeKind::FRAGMENT_SPREAD:
/** @var FragmentSpreadNode $selection */ /** @var FragmentSpreadNode $selection */
$fragName = $selection->name->value; $fragName = $selection->name->value;

View file

@ -199,7 +199,7 @@ class ValidationContext
for ($i = 0, $selectionCount = count($set->selections); $i < $selectionCount; $i++) { for ($i = 0, $selectionCount = count($set->selections); $i < $selectionCount; $i++) {
$selection = $set->selections[$i]; $selection = $set->selections[$i];
if ($selection instanceof FragmentSpreadNode) { if ($selection->kind === NodeKind::FRAGMENT_SPREAD) {
$spreads[] = $selection; $spreads[] = $selection;
} elseif ($selection->selectionSet) { } elseif ($selection->selectionSet) {
$setsToVisit[] = $selection->selectionSet; $setsToVisit[] = $selection->selectionSet;
@ -223,7 +223,7 @@ class ValidationContext
if (! $fragments) { if (! $fragments) {
$fragments = []; $fragments = [];
foreach ($this->getDocument()->definitions as $statement) { foreach ($this->getDocument()->definitions as $statement) {
if (! ($statement instanceof FragmentDefinitionNode)) { if ($statement->kind !== NodeKind::FRAGMENT_DEFINITION) {
continue; continue;
} }

View file

@ -1,18 +0,0 @@
<?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());
}
}

View file

@ -109,10 +109,10 @@ class DeferredFieldsTest extends TestCase
'fields' => [ 'fields' => [
'title' => [ 'title' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($story, $args, $context, ResolveInfo $info) { 'resolve' => function ($entry, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return $story['title']; return $entry['title'];
}, },
], ],
'author' => [ 'author' => [
@ -185,7 +185,7 @@ class DeferredFieldsTest extends TestCase
'fields' => [ 'fields' => [
'topStories' => [ 'topStories' => [
'type' => Type::listOf($this->storyType), 'type' => Type::listOf($this->storyType),
'resolve' => function ($rootValue, $args, $context, ResolveInfo $info) { 'resolve' => function ($val, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return Utils::filter( return Utils::filter(
@ -198,7 +198,7 @@ class DeferredFieldsTest extends TestCase
], ],
'featuredCategory' => [ 'featuredCategory' => [
'type' => $this->categoryType, 'type' => $this->categoryType,
'resolve' => function ($rootValue, $args, $context, ResolveInfo $info) { 'resolve' => function ($val, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return $this->categoryDataSource[0]; return $this->categoryDataSource[0];
@ -206,7 +206,7 @@ class DeferredFieldsTest extends TestCase
], ],
'categories' => [ 'categories' => [
'type' => Type::listOf($this->categoryType), 'type' => Type::listOf($this->categoryType),
'resolve' => function ($rootValue, $args, $context, ResolveInfo $info) { 'resolve' => function ($val, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return $this->categoryDataSource; return $this->categoryDataSource;
@ -401,7 +401,7 @@ class DeferredFieldsTest extends TestCase
return [ return [
'sync' => [ 'sync' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($complexType, $args, $context, ResolveInfo $info) { 'resolve' => function ($v, $a, $c, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return 'sync'; return 'sync';
@ -409,7 +409,7 @@ class DeferredFieldsTest extends TestCase
], ],
'deferred' => [ 'deferred' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($complexType, $args, $context, ResolveInfo $info) { 'resolve' => function ($v, $a, $c, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return new Deferred(function () use ($info) { return new Deferred(function () use ($info) {
@ -421,7 +421,7 @@ class DeferredFieldsTest extends TestCase
], ],
'nest' => [ 'nest' => [
'type' => $complexType, 'type' => $complexType,
'resolve' => function ($complexType, $args, $context, ResolveInfo $info) { 'resolve' => function ($v, $a, $c, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return []; return [];
@ -429,7 +429,7 @@ class DeferredFieldsTest extends TestCase
], ],
'deferredNest' => [ 'deferredNest' => [
'type' => $complexType, 'type' => $complexType,
'resolve' => function ($complexType, $args, $context, ResolveInfo $info) { 'resolve' => function ($v, $a, $c, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return new Deferred(function () use ($info) { return new Deferred(function () use ($info) {

View file

@ -75,7 +75,7 @@ class ExecutorSchemaTest extends TestCase
'article' => [ 'article' => [
'type' => $BlogArticle, 'type' => $BlogArticle,
'args' => ['id' => ['type' => Type::id()]], 'args' => ['id' => ['type' => Type::id()]],
'resolve' => function ($rootValue, $args) { 'resolve' => function ($_, $args) {
return $this->article($args['id']); return $this->article($args['id']);
}, },
], ],

View file

@ -273,7 +273,7 @@ class ExecutorTest extends TestCase
'fields' => [ 'fields' => [
'test' => [ 'test' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => static function ($test, $args, $ctx, $_info) use (&$info) { 'resolve' => static function ($val, $args, $ctx, $_info) use (&$info) {
$info = $_info; $info = $_info;
}, },
], ],

View file

@ -16,7 +16,7 @@ class Adder
{ {
$this->num = $num; $this->num = $num;
$this->test = function ($objectValue, $args, $context) { $this->test = function ($source, $args, $context) {
return $this->num + $args['addend1'] + $context['addend2']; return $this->num + $args['addend1'] + $context['addend2'];
}; };
} }

View file

@ -362,7 +362,7 @@ class CollectorTest extends TestCase
$operationName = null; $operationName = null;
foreach ($documentNode->definitions as $definitionNode) { foreach ($documentNode->definitions as $definitionNode) {
/** @var Node $definitionNode */ /** @var Node $definitionNode */
if ($definitionNode instanceof OperationDefinitionNode) { if ($definitionNode->kind === NodeKind::OPERATION_DEFINITION) {
/** @var OperationDefinitionNode $definitionNode */ /** @var OperationDefinitionNode $definitionNode */
self::assertNotNull($definitionNode->name); self::assertNotNull($definitionNode->name);
$operationName = $definitionNode->name->value; $operationName = $definitionNode->name->value;

View file

@ -30,7 +30,7 @@ class GraphQLTest extends TestCase
'type' => Type::nonNull(Type::string()), 'type' => Type::nonNull(Type::string()),
], ],
], ],
'resolve' => static function ($rootValue, $args) use ($promiseAdapter) { 'resolve' => static function ($value, $args) use ($promiseAdapter) {
return $promiseAdapter->createFulfilled(sprintf('Hi %s!', $args['name'])); return $promiseAdapter->createFulfilled(sprintf('Hi %s!', $args['name']));
}, },
], ],

View file

@ -70,12 +70,7 @@ class VisitorTest extends ValidatorTestCase
/** @var Node $node */ /** @var Node $node */
[$node, $key, $parent, $path, $ancestors] = $args; [$node, $key, $parent, $path, $ancestors] = $args;
$parentArray = $parent && ! is_array($parent) $parentArray = $parent && ! is_array($parent) ? ($parent instanceof NodeList ? iterator_to_array($parent) : $parent->toArray()) : $parent;
? ($parent instanceof NodeList
? iterator_to_array($parent)
: $parent->toArray()
)
: $parent;
self::assertInstanceOf(Node::class, $node); self::assertInstanceOf(Node::class, $node);
self::assertContains($node->kind, array_keys(NodeKind::$classMap)); self::assertContains($node->kind, array_keys(NodeKind::$classMap));
@ -119,9 +114,7 @@ class VisitorTest extends ValidatorTestCase
{ {
$result = $ast; $result = $ast;
foreach ($path as $key) { foreach ($path as $key) {
$resultArray = $result instanceof NodeList $resultArray = $result instanceof NodeList ? iterator_to_array($result) : $result->toArray();
? iterator_to_array($result)
: $result->toArray();
self::assertArrayHasKey($key, $resultArray); self::assertArrayHasKey($key, $resultArray);
$result = $resultArray[$key]; $result = $resultArray[$key];
} }
@ -391,7 +384,7 @@ class VisitorTest extends ValidatorTestCase
$this->checkVisitorFnArgs($ast, func_get_args()); $this->checkVisitorFnArgs($ast, func_get_args());
$visited[] = ['leave', $node->kind, $node->value ?? null]; $visited[] = ['leave', $node->kind, $node->value ?? null];
if ($node instanceof NameNode && $node->value === 'x') { if ($node->kind === NodeKind::NAME && $node->value === 'x') {
return Visitor::stop(); return Visitor::stop();
} }
}, },

View file

@ -1,156 +0,0 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Regression;
use GraphQL\GraphQL;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase;
use function stristr;
/**
* @see https://github.com/webonyx/graphql-php/issues/396
*/
class Issue396Test extends TestCase
{
public function testUnionResolveType()
{
$a = new ObjectType(['name' => 'A', 'fields' => ['name' => Type::string()]]);
$b = new ObjectType(['name' => 'B', 'fields' => ['name' => Type::string()]]);
$c = new ObjectType(['name' => 'C', 'fields' => ['name' => Type::string()]]);
$log = [];
$unionResult = new UnionType([
'name' => 'UnionResult',
'types' => [$a, $b, $c],
'resolveType' => static function ($result, $value, ResolveInfo $info) use ($a, $b, $c, &$log) : Type {
$log[] = [$result, $info->path];
if (stristr($result['name'], 'A')) {
return $a;
}
if (stristr($result['name'], 'B')) {
return $b;
}
if (stristr($result['name'], 'C')) {
return $c;
}
},
]);
$exampleType = new ObjectType([
'name' => 'Example',
'fields' => [
'field' => [
'type' => Type::nonNull(Type::listOf(Type::nonNull($unionResult))),
'resolve' => static function () : array {
return [
['name' => 'A 1'],
['name' => 'B 2'],
['name' => 'C 3'],
];
},
],
],
]);
$schema = new Schema(['query' => $exampleType]);
$query = '
query {
field {
... on A {
name
}
... on B {
name
}
... on C {
name
}
}
}
';
GraphQL::executeQuery($schema, $query);
$expected = [
[['name' => 'A 1'], ['field', 0]],
[['name' => 'B 2'], ['field', 1]],
[['name' => 'C 3'], ['field', 2]],
];
self::assertEquals($expected, $log);
}
public function testInterfaceResolveType()
{
$log = [];
$interfaceResult = new InterfaceType([
'name' => 'InterfaceResult',
'fields' => [
'name' => Type::string(),
],
'resolveType' => static function ($result, $value, ResolveInfo $info) use (&$a, &$b, &$c, &$log) : Type {
$log[] = [$result, $info->path];
if (stristr($result['name'], 'A')) {
return $a;
}
if (stristr($result['name'], 'B')) {
return $b;
}
if (stristr($result['name'], 'C')) {
return $c;
}
},
]);
$a = new ObjectType(['name' => 'A', 'fields' => ['name' => Type::string()], 'interfaces' => [$interfaceResult]]);
$b = new ObjectType(['name' => 'B', 'fields' => ['name' => Type::string()], 'interfaces' => [$interfaceResult]]);
$c = new ObjectType(['name' => 'C', 'fields' => ['name' => Type::string()], 'interfaces' => [$interfaceResult]]);
$exampleType = new ObjectType([
'name' => 'Example',
'fields' => [
'field' => [
'type' => Type::nonNull(Type::listOf(Type::nonNull($interfaceResult))),
'resolve' => static function () : array {
return [
['name' => 'A 1'],
['name' => 'B 2'],
['name' => 'C 3'],
];
},
],
],
]);
$schema = new Schema([
'query' => $exampleType,
'types' => [$a, $b, $c],
]);
$query = '
query {
field {
name
}
}
';
GraphQL::executeQuery($schema, $query);
$expected = [
[['name' => 'A 1'], ['field', 0]],
[['name' => 'B 2'], ['field', 1]],
[['name' => 'C 3'], ['field', 2]],
];
self::assertEquals($expected, $log);
}
}

View file

@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Regression;
use GraphQL\GraphQL;
use GraphQL\Utils\BuildSchema;
use PHPUnit\Framework\TestCase;
/**
* @see https://github.com/webonyx/graphql-php/issues/467
*/
class Issue467Test extends TestCase
{
public function testInputObjectValidation()
{
$schemaStr = '
input MsgInput {
msg: String
}
type Query {
echo(msg: MsgInput): String
}
schema {
query: Query
}
';
$query = '
query echo ($msg: MsgInput) {
echo (msg: $msg)
}';
$variables = ['msg' => ['my message']];
$schema = BuildSchema::build($schemaStr);
$result = GraphQL::executeQuery($schema, $query, null, null, $variables);
$expectedError = 'Variable "$msg" got invalid value ["my message"]; Field "0" is not defined by type MsgInput.';
self::assertCount(1, $result->errors);
self::assertEquals($expectedError, $result->errors[0]->getMessage());
}
}

View file

@ -25,13 +25,13 @@ abstract class ServerTestCase extends TestCase
'fields' => [ 'fields' => [
'f1' => [ 'f1' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => static function ($rootValue, $args, $context, $info) { 'resolve' => static function ($root, $args, $context, $info) {
return $info->fieldName; return $info->fieldName;
}, },
], ],
'fieldWithPhpError' => [ 'fieldWithPhpError' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => static function ($rootValue, $args, $context, $info) { 'resolve' => static function ($root, $args, $context, $info) {
trigger_error('deprecated', E_USER_DEPRECATED); trigger_error('deprecated', E_USER_DEPRECATED);
trigger_error('notice', E_USER_NOTICE); trigger_error('notice', E_USER_NOTICE);
trigger_error('warning', E_USER_WARNING); trigger_error('warning', E_USER_WARNING);
@ -55,8 +55,8 @@ abstract class ServerTestCase extends TestCase
], ],
'testContextAndRootValue' => [ 'testContextAndRootValue' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => static function ($rootValue, $args, $context, $info) { 'resolve' => static function ($root, $args, $context, $info) {
$context->testedRootValue = $rootValue; $context->testedRootValue = $root;
return $info->fieldName; return $info->fieldName;
}, },
@ -68,7 +68,7 @@ abstract class ServerTestCase extends TestCase
'type' => Type::nonNull(Type::string()), 'type' => Type::nonNull(Type::string()),
], ],
], ],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($root, $args) {
return $args['arg']; return $args['arg'];
}, },
], ],
@ -79,7 +79,7 @@ abstract class ServerTestCase extends TestCase
'type' => Type::nonNull(Type::int()), 'type' => Type::nonNull(Type::int()),
], ],
], ],
'resolve' => static function ($rootValue, $args, $context) { 'resolve' => static function ($root, $args, $context) {
$context['buffer']($args['num']); $context['buffer']($args['num']);
return new Deferred(static function () use ($args, $context) { return new Deferred(static function () use ($args, $context) {

View file

@ -273,7 +273,7 @@ class StarWarsSchema
'type' => $episodeEnum, 'type' => $episodeEnum,
], ],
], ],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($root, $args) {
return StarWarsData::getHero($args['episode'] ?? null); return StarWarsData::getHero($args['episode'] ?? null);
}, },
], ],
@ -286,7 +286,7 @@ class StarWarsSchema
'type' => Type::nonNull(Type::string()), 'type' => Type::nonNull(Type::string()),
], ],
], ],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($root, $args) {
$humans = StarWarsData::humans(); $humans = StarWarsData::humans();
return $humans[$args['id']] ?? null; return $humans[$args['id']] ?? null;
@ -301,7 +301,7 @@ class StarWarsSchema
'type' => Type::nonNull(Type::string()), 'type' => Type::nonNull(Type::string()),
], ],
], ],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($root, $args) {
$droids = StarWarsData::droids(); $droids = StarWarsData::droids();
return $droids[$args['id']] ?? null; return $droids[$args['id']] ?? null;

View file

@ -74,7 +74,7 @@ class EnumTypeTest extends TestCase
'fromInt' => ['type' => Type::int()], 'fromInt' => ['type' => Type::int()],
'fromString' => ['type' => Type::string()], 'fromString' => ['type' => Type::string()],
], ],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($value, $args) {
if (isset($args['fromInt'])) { if (isset($args['fromInt'])) {
return $args['fromInt']; return $args['fromInt'];
} }
@ -92,7 +92,7 @@ class EnumTypeTest extends TestCase
'fromName' => ['type' => Type::string()], 'fromName' => ['type' => Type::string()],
'fromValue' => ['type' => Type::string()], 'fromValue' => ['type' => Type::string()],
], ],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($value, $args) {
if (isset($args['fromName'])) { if (isset($args['fromName'])) {
return $args['fromName']; return $args['fromName'];
} }
@ -107,7 +107,7 @@ class EnumTypeTest extends TestCase
'fromEnum' => ['type' => $ColorType], 'fromEnum' => ['type' => $ColorType],
'fromInt' => ['type' => Type::int()], 'fromInt' => ['type' => Type::int()],
], ],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($value, $args) {
if (isset($args['fromInt'])) { if (isset($args['fromInt'])) {
return $args['fromInt']; return $args['fromInt'];
} }
@ -132,7 +132,7 @@ class EnumTypeTest extends TestCase
'type' => Type::boolean(), 'type' => Type::boolean(),
], ],
], ],
'resolve' => static function ($rootValue, $args) use ($Complex2) { 'resolve' => static function ($value, $args) use ($Complex2) {
if (! empty($args['provideGoodValue'])) { if (! empty($args['provideGoodValue'])) {
// Note: this is one of the references of the internal values which // Note: this is one of the references of the internal values which
// ComplexEnum allows. // ComplexEnum allows.
@ -156,7 +156,7 @@ class EnumTypeTest extends TestCase
'favoriteEnum' => [ 'favoriteEnum' => [
'type' => $ColorType, 'type' => $ColorType,
'args' => ['color' => ['type' => $ColorType]], 'args' => ['color' => ['type' => $ColorType]],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($value, $args) {
return $args['color'] ?? null; return $args['color'] ?? null;
}, },
], ],
@ -169,7 +169,7 @@ class EnumTypeTest extends TestCase
'subscribeToEnum' => [ 'subscribeToEnum' => [
'type' => $ColorType, 'type' => $ColorType,
'args' => ['color' => ['type' => $ColorType]], 'args' => ['color' => ['type' => $ColorType]],
'resolve' => static function ($rootValue, $args) { 'resolve' => static function ($value, $args) {
return $args['color'] ?? null; return $args['color'] ?? null;
}, },
], ],

View file

@ -1049,7 +1049,7 @@ class IntrospectionTest extends TestCase
'field' => [ 'field' => [
'type' => Type::string(), 'type' => Type::string(),
'args' => ['complex' => ['type' => $TestInputObject]], 'args' => ['complex' => ['type' => $TestInputObject]],
'resolve' => static function ($testType, $args) { 'resolve' => static function ($_, $args) {
return json_encode($args['complex']); return json_encode($args['complex']);
}, },
], ],

View file

@ -5,8 +5,6 @@ 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;
@ -297,117 +295,6 @@ 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;
}
},
]);
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([

View file

@ -13,9 +13,9 @@ class ScalarSerializationTest extends TestCase
{ {
// Type System: Scalar coercion // Type System: Scalar coercion
/** /**
* @see it('serializes output as Int') * @see it('serializes output int')
*/ */
public function testSerializesOutputAsInt() : void public function testSerializesOutputInt() : void
{ {
$intType = Type::int(); $intType = Type::int();
@ -114,9 +114,9 @@ class ScalarSerializationTest extends TestCase
} }
/** /**
* @see it('serializes output as Float') * @see it('serializes output float')
*/ */
public function testSerializesOutputAsFloat() : void public function testSerializesOutputFloat() : void
{ {
$floatType = Type::float(); $floatType = Type::float();
@ -149,9 +149,9 @@ class ScalarSerializationTest extends TestCase
} }
/** /**
* @see it('serializes output as String') * @see it('serializes output strings')
*/ */
public function testSerializesOutputAsString() : void public function testSerializesOutputStrings() : void
{ {
$stringType = Type::string(); $stringType = Type::string();
@ -181,27 +181,23 @@ class ScalarSerializationTest extends TestCase
} }
/** /**
* @see it('serializes output as Boolean') * @see it('serializes output boolean')
*/ */
public function testSerializesOutputAsBoolean() : void public function testSerializesOutputBoolean() : void
{ {
$boolType = Type::boolean(); $boolType = Type::boolean();
self::assertTrue($boolType->serialize(true));
self::assertTrue($boolType->serialize(1));
self::assertTrue($boolType->serialize('1'));
self::assertTrue($boolType->serialize('string')); self::assertTrue($boolType->serialize('string'));
self::assertFalse($boolType->serialize(false));
self::assertFalse($boolType->serialize(0));
self::assertFalse($boolType->serialize('0'));
self::assertFalse($boolType->serialize('')); self::assertFalse($boolType->serialize(''));
self::assertTrue($boolType->serialize('1'));
self::assertTrue($boolType->serialize(1));
self::assertFalse($boolType->serialize(0));
self::assertTrue($boolType->serialize(true));
self::assertFalse($boolType->serialize(false));
// TODO: how should it behave on '0'?
} }
/** public function testSerializesOutputID() : void
* @see it('serializes output as ID')
*/
public function testSerializesOutputAsID() : void
{ {
$idType = Type::id(); $idType = Type::id();

View file

@ -879,144 +879,6 @@ class ValidationTest extends TestCase
); );
} }
/**
* @see it('accepts an Input Object with breakable circular reference')
*/
public function testAcceptsAnInputObjectWithBreakableCircularReference() : void
{
$schema = BuildSchema::build('
input AnotherInputObject {
parent: SomeInputObject
}
type Query {
field(arg: SomeInputObject): String
}
input SomeInputObject {
self: SomeInputObject
arrayOfSelf: [SomeInputObject]
nonNullArrayOfSelf: [SomeInputObject]!
nonNullArrayOfNonNullSelf: [SomeInputObject!]!
intermediateSelf: AnotherInputObject
}
');
self::assertEquals([], $schema->validate());
}
/**
* @see it('rejects an Input Object with non-breakable circular reference')
*/
public function testRejectsAnInputObjectWithNonBreakableCircularReference() : void
{
$schema = BuildSchema::build('
type Query {
field(arg: SomeInputObject): String
}
input SomeInputObject {
nonNullSelf: SomeInputObject!
}
');
$this->assertMatchesValidationMessage(
$schema->validate(),
[
[
'message' => 'Cannot reference Input Object "SomeInputObject" within itself through a series of non-null fields: "nonNullSelf".',
'locations' => [['line' => 7, 'column' => 9]],
],
]
);
}
/**
* @see it('rejects Input Objects with non-breakable circular reference spread across them')
*/
public function testRejectsInputObjectsWithNonBreakableCircularReferenceSpreadAcrossThem() : void
{
$schema = BuildSchema::build('
type Query {
field(arg: SomeInputObject): String
}
input SomeInputObject {
startLoop: AnotherInputObject!
}
input AnotherInputObject {
nextInLoop: YetAnotherInputObject!
}
input YetAnotherInputObject {
closeLoop: SomeInputObject!
}
');
$this->assertMatchesValidationMessage(
$schema->validate(),
[
[
'message' => 'Cannot reference Input Object "SomeInputObject" within itself through a series of non-null fields: "startLoop.nextInLoop.closeLoop".',
'locations' => [
['line' => 7, 'column' => 9],
['line' => 11, 'column' => 9],
['line' => 15, 'column' => 9],
],
],
]
);
}
/**
* @see it('rejects Input Objects with multiple non-breakable circular reference')
*/
public function testRejectsInputObjectsWithMultipleNonBreakableCircularReferences() : void
{
$schema = BuildSchema::build('
type Query {
field(arg: SomeInputObject): String
}
input SomeInputObject {
startLoop: AnotherInputObject!
}
input AnotherInputObject {
closeLoop: SomeInputObject!
startSecondLoop: YetAnotherInputObject!
}
input YetAnotherInputObject {
closeSecondLoop: AnotherInputObject!
nonNullSelf: YetAnotherInputObject!
}
');
$this->assertMatchesValidationMessage(
$schema->validate(),
[
[
'message' => 'Cannot reference Input Object "SomeInputObject" within itself through a series of non-null fields: "startLoop.closeLoop".',
'locations' => [
['line' => 7, 'column' => 9],
['line' => 11, 'column' => 9],
],
],
[
'message' => 'Cannot reference Input Object "AnotherInputObject" within itself through a series of non-null fields: "startSecondLoop.closeSecondLoop".',
'locations' => [
['line' => 12, 'column' => 9],
['line' => 16, 'column' => 9],
],
],
[
'message' => 'Cannot reference Input Object "YetAnotherInputObject" within itself through a series of non-null fields: "nonNullSelf".',
'locations' => [
['line' => 17, 'column' => 9],
],
],
]
);
}
/** /**
* @see it('rejects an Input Object type with incorrectly typed fields') * @see it('rejects an Input Object type with incorrectly typed fields')
*/ */

View file

@ -10,7 +10,6 @@ use GraphQL\Language\AST\FloatValueNode;
use GraphQL\Language\AST\IntValueNode; use GraphQL\Language\AST\IntValueNode;
use GraphQL\Language\AST\ListValueNode; use GraphQL\Language\AST\ListValueNode;
use GraphQL\Language\AST\NameNode; use GraphQL\Language\AST\NameNode;
use GraphQL\Language\AST\NodeList;
use GraphQL\Language\AST\NullValueNode; use GraphQL\Language\AST\NullValueNode;
use GraphQL\Language\AST\ObjectFieldNode; use GraphQL\Language\AST\ObjectFieldNode;
use GraphQL\Language\AST\ObjectValueNode; use GraphQL\Language\AST\ObjectValueNode;
@ -180,18 +179,18 @@ class AstFromValueTest extends TestCase
public function testConvertsArrayValuesToListASTs() : void public function testConvertsArrayValuesToListASTs() : void
{ {
$value1 = new ListValueNode([ $value1 = new ListValueNode([
'values' => new NodeList([ 'values' => [
new StringValueNode(['value' => 'FOO']), new StringValueNode(['value' => 'FOO']),
new StringValueNode(['value' => 'BAR']), new StringValueNode(['value' => 'BAR']),
]), ],
]); ]);
self::assertEquals($value1, AST::astFromValue(['FOO', 'BAR'], Type::listOf(Type::string()))); self::assertEquals($value1, AST::astFromValue(['FOO', 'BAR'], Type::listOf(Type::string())));
$value2 = new ListValueNode([ $value2 = new ListValueNode([
'values' => new NodeList([ 'values' => [
new EnumValueNode(['value' => 'HELLO']), new EnumValueNode(['value' => 'HELLO']),
new EnumValueNode(['value' => 'GOODBYE']), new EnumValueNode(['value' => 'GOODBYE']),
]), ],
]); ]);
self::assertEquals($value2, AST::astFromValue(['HELLO', 'GOODBYE'], Type::listOf($this->myEnum()))); self::assertEquals($value2, AST::astFromValue(['HELLO', 'GOODBYE'], Type::listOf($this->myEnum())));
} }
@ -221,10 +220,10 @@ class AstFromValueTest extends TestCase
]); ]);
$expected = new ObjectValueNode([ $expected = new ObjectValueNode([
'fields' => new NodeList([ 'fields' => [
$this->objectField('foo', new IntValueNode(['value' => '3'])), $this->objectField('foo', new IntValueNode(['value' => '3'])),
$this->objectField('bar', new EnumValueNode(['value' => 'HELLO'])), $this->objectField('bar', new EnumValueNode(['value' => 'HELLO'])),
]), ],
]); ]);
$data = ['foo' => 3, 'bar' => 'HELLO']; $data = ['foo' => 3, 'bar' => 'HELLO'];
@ -260,9 +259,9 @@ class AstFromValueTest extends TestCase
self::assertEquals( self::assertEquals(
new ObjectValueNode([ new ObjectValueNode([
'fields' => new NodeList([ 'fields' => [
$this->objectField('foo', new NullValueNode([])), $this->objectField('foo', new NullValueNode([])),
]), ],
]), ]),
AST::astFromValue(['foo' => null], $inputObj) AST::astFromValue(['foo' => null], $inputObj)
); );

View file

@ -51,7 +51,7 @@ class BuildSchemaTest extends TestCase
'); ');
$root = [ $root = [
'add' => static function ($rootValue, $args) { 'add' => static function ($root, $args) {
return $args['x'] + $args['y']; return $args['x'] + $args['y'];
}, },
]; ];
@ -437,7 +437,7 @@ type WorldTwo {
*/ */
public function testSpecifyingUnionTypeUsingTypename() : void public function testSpecifyingUnionTypeUsingTypename() : void
{ {
$schema = BuildSchema::buildAST(Parser::parse(' $schema = BuildSchema::buildAST(Parser::parse('
type Query { type Query {
fruits: [Fruit] fruits: [Fruit]
} }
@ -452,7 +452,7 @@ type WorldTwo {
length: Int length: Int
} }
')); '));
$query = ' $query = '
{ {
fruits { fruits {
... on Apple { ... on Apple {
@ -464,7 +464,7 @@ type WorldTwo {
} }
} }
'; ';
$rootValue = [ $root = [
'fruits' => [ 'fruits' => [
[ [
'color' => 'green', 'color' => 'green',
@ -476,7 +476,7 @@ type WorldTwo {
], ],
], ],
]; ];
$expected = [ $expected = [
'data' => [ 'data' => [
'fruits' => [ 'fruits' => [
['color' => 'green'], ['color' => 'green'],
@ -485,7 +485,7 @@ type WorldTwo {
], ],
]; ];
$result = GraphQL::executeQuery($schema, $query, $rootValue); $result = GraphQL::executeQuery($schema, $query, $root);
self::assertEquals($expected, $result->toArray(true)); self::assertEquals($expected, $result->toArray(true));
} }
@ -494,7 +494,7 @@ type WorldTwo {
*/ */
public function testSpecifyingInterfaceUsingTypename() : void public function testSpecifyingInterfaceUsingTypename() : void
{ {
$schema = BuildSchema::buildAST(Parser::parse(' $schema = BuildSchema::buildAST(Parser::parse('
type Query { type Query {
characters: [Character] characters: [Character]
} }
@ -513,7 +513,7 @@ type WorldTwo {
primaryFunction: String primaryFunction: String
} }
')); '));
$query = ' $query = '
{ {
characters { characters {
name name
@ -526,7 +526,7 @@ type WorldTwo {
} }
} }
'; ';
$rootValue = [ $root = [
'characters' => [ 'characters' => [
[ [
'name' => 'Han Solo', 'name' => 'Han Solo',
@ -540,7 +540,7 @@ type WorldTwo {
], ],
], ],
]; ];
$expected = [ $expected = [
'data' => [ 'data' => [
'characters' => [ 'characters' => [
['name' => 'Han Solo', 'totalCredits' => 10], ['name' => 'Han Solo', 'totalCredits' => 10],
@ -549,7 +549,7 @@ type WorldTwo {
], ],
]; ];
$result = GraphQL::executeQuery($schema, $query, $rootValue); $result = GraphQL::executeQuery($schema, $query, $root);
self::assertEquals($expected, $result->toArray(true)); self::assertEquals($expected, $result->toArray(true));
} }

View file

@ -219,9 +219,7 @@ class SchemaExtenderTest extends TestCase
{ {
$ast = Parser::parse(SchemaPrinter::doPrint($extendedSchema)); $ast = Parser::parse(SchemaPrinter::doPrint($extendedSchema));
$ast->definitions = array_values(array_filter( $ast->definitions = array_values(array_filter(
$ast->definitions instanceof NodeList $ast->definitions instanceof NodeList ? iterator_to_array($ast->definitions->getIterator()) : $ast->definitions,
? iterator_to_array($ast->definitions->getIterator())
: $ast->definitions,
function (Node $node) : bool { function (Node $node) : bool {
return ! in_array(Printer::doPrint($node), $this->testSchemaDefinitions, true); return ! in_array(Printer::doPrint($node), $this->testSchemaDefinitions, true);
} }

View file

@ -40,9 +40,7 @@ function renderClassMethod(ReflectionMethod $method) {
$def = $type . '$' . $p->getName(); $def = $type . '$' . $p->getName();
if ($p->isDefaultValueAvailable()) { if ($p->isDefaultValueAvailable()) {
$val = $p->isDefaultValueConstant() $val = $p->isDefaultValueConstant() ? $p->getDefaultValueConstantName() : $p->getDefaultValue();
? $p->getDefaultValueConstantName()
: $p->getDefaultValue();
$def .= " = " . Utils::printSafeJson($val); $def .= " = " . Utils::printSafeJson($val);
} }