Merge branch 'master' into root-value-consistent-naming

# Conflicts:
#	docs/data-fetching.md
#	src/Executor/Executor.php
#	tests/Executor/DeferredFieldsTest.php
This commit is contained in:
Benedikt Franke 2019-07-01 08:23:00 +02:00
commit d037ab7ec3
32 changed files with 232 additions and 158 deletions

View File

@ -118,7 +118,9 @@ function defaultFieldResolver($rootValue, $args, $context, \GraphQL\Type\Definit
} }
} }
return $property instanceof Closure ? $property($rootValue, $args, $context, $info) : $property; return $property instanceof Closure
? $property($rootValue, $args, $context, $info)
: $property;
} }
``` ```

View File

@ -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 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 Props:** **Class Props:**
```php ```php

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,7 +45,7 @@ class ExecutionContext
public $errors; public $errors;
/** @var PromiseAdapter */ /** @var PromiseAdapter */
public $promises; public $promiseAdapter;
public function __construct( public function __construct(
$schema, $schema,
@ -53,7 +53,7 @@ class ExecutionContext
$rootValue, $rootValue,
$contextValue, $contextValue,
$operation, $operation,
$variables, $variableValues,
$errors, $errors,
$fieldResolver, $fieldResolver,
$promiseAdapter $promiseAdapter
@ -63,10 +63,10 @@ class ExecutionContext
$this->rootValue = $rootValue; $this->rootValue = $rootValue;
$this->contextValue = $contextValue; $this->contextValue = $contextValue;
$this->operation = $operation; $this->operation = $operation;
$this->variableValues = $variables; $this->variableValues = $variableValues;
$this->errors = $errors ?: []; $this->errors = $errors ?: [];
$this->fieldResolver = $fieldResolver; $this->fieldResolver = $fieldResolver;
$this->promises = $promiseAdapter; $this->promiseAdapter = $promiseAdapter;
} }
public function addError(Error $error) public function addError(Error $error)

View File

@ -182,6 +182,8 @@ class Executor
} }
} }
return $property instanceof Closure ? $property($rootValue, $args, $context, $info) : $property; return $property instanceof Closure
? $property($rootValue, $args, $context, $info)
: $property;
} }
} }

View File

@ -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->promises->createFulfilled($result); : $this->exeContext->promiseAdapter->createFulfilled($result);
} }
/** /**
@ -252,9 +252,9 @@ 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,
@ -262,7 +262,7 @@ class ReferenceExecutor implements ExecutorImplementation
if ($error instanceof Error) { if ($error instanceof Error) {
$this->exeContext->addError($error); $this->exeContext->addError($error);
return $this->exeContext->promises->createFulfilled(null); return $this->exeContext->promiseAdapter->createFulfilled(null);
} }
} }
); );
@ -527,7 +527,11 @@ class ReferenceExecutor implements ExecutorImplementation
return self::$UNDEFINED; return self::$UNDEFINED;
} }
$returnType = $fieldDef->getType(); $returnType = $fieldDef->getType();
// The resolve function's optional third argument is a collection of // The resolve function's optional 3rd 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;
// 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,10 +552,6 @@ 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(
@ -575,6 +575,7 @@ 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
@ -609,8 +610,8 @@ class ReferenceExecutor implements ExecutorImplementation
} }
/** /**
* Isolates the "ReturnOrAbrupt" behavior to not de-opt the `resolveField` * Isolates the "ReturnOrAbrupt" behavior to not de-opt the `resolveField` function.
* function. Returns the result of resolveFn or the abrupt-return Error object. * Returns the result of resolveFn or the abrupt-return Error object.
* *
* @param FieldDefinition $fieldDef * @param FieldDefinition $fieldDef
* @param FieldNode $fieldNode * @param FieldNode $fieldNode
@ -624,7 +625,7 @@ class ReferenceExecutor implements ExecutorImplementation
private function resolveOrError($fieldDef, $fieldNode, $resolveFn, $rootValue, $context, $info) private function resolveOrError($fieldDef, $fieldNode, $resolveFn, $rootValue, $context, $info)
{ {
try { try {
// Build hash of arguments from the field.arguments AST, using the // Build a map 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,
@ -686,7 +687,7 @@ class ReferenceExecutor implements ExecutorImplementation
function ($error) use ($exeContext) { function ($error) use ($exeContext) {
$exeContext->addError($error); $exeContext->addError($error);
return $this->exeContext->promises->createFulfilled(null); return $this->exeContext->promiseAdapter->createFulfilled(null);
} }
); );
} }
@ -733,7 +734,7 @@ class ReferenceExecutor implements ExecutorImplementation
return $promise->then( return $promise->then(
null, null,
function ($error) use ($fieldNodes, $path) { function ($error) use ($fieldNodes, $path) {
return $this->exeContext->promises->createRejected(Error::createLocatedError( return $this->exeContext->promiseAdapter->createRejected(Error::createLocatedError(
$error, $error,
$fieldNodes, $fieldNodes,
$path $path
@ -865,7 +866,7 @@ class ReferenceExecutor implements ExecutorImplementation
*/ */
private function isPromise($value) private function isPromise($value)
{ {
return $value instanceof Promise || $this->exeContext->promises->isThenable($value); return $value instanceof Promise || $this->exeContext->promiseAdapter->isThenable($value);
} }
/** /**
@ -881,12 +882,12 @@ class ReferenceExecutor implements ExecutorImplementation
if ($value === null || $value instanceof Promise) { if ($value === null || $value instanceof Promise) {
return $value; return $value;
} }
if ($this->exeContext->promises->isThenable($value)) { if ($this->exeContext->promiseAdapter->isThenable($value)) {
$promise = $this->exeContext->promises->convertThenable($value); $promise = $this->exeContext->promiseAdapter->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->promises), get_class($this->exeContext->promiseAdapter),
Utils::printSafe($promise) Utils::printSafe($promise)
)); ));
} }
@ -928,28 +929,27 @@ class ReferenceExecutor implements ExecutorImplementation
} }
/** /**
* Complete a list value by completing each item in the list with the * Complete a list value by completing each item in the list with the inner type.
* inner type
* *
* @param FieldNode[] $fieldNodes * @param FieldNode[] $fieldNodes
* @param mixed[] $path * @param mixed[] $path
* @param mixed $result * @param mixed[]|Traversable $results
* *
* @return mixed[]|Promise * @return mixed[]|Promise
* *
* @throws Exception * @throws Exception
*/ */
private function completeListValue(ListOfType $returnType, $fieldNodes, ResolveInfo $info, $path, &$result) private function completeListValue(ListOfType $returnType, $fieldNodes, ResolveInfo $info, $path, &$results)
{ {
$itemType = $returnType->getWrappedType(); $itemType = $returnType->getWrappedType();
Utils::invariant( Utils::invariant(
is_array($result) || $result instanceof Traversable, is_array($results) || $results 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 ($result as $item) { foreach ($results as $item) {
$fieldPath = $path; $fieldPath = $path;
$fieldPath[] = $i++; $fieldPath[] = $i++;
$info->path = $fieldPath; $info->path = $fieldPath;
@ -960,7 +960,9 @@ class ReferenceExecutor implements ExecutorImplementation
$completedItems[] = $completedItem; $completedItems[] = $completedItem;
} }
return $containsPromise ? $this->exeContext->promises->all($completedItems) : $completedItems; return $containsPromise
? $this->exeContext->promiseAdapter->all($completedItems)
: $completedItems;
} }
/** /**
@ -1102,7 +1104,7 @@ class ReferenceExecutor implements ExecutorImplementation
} }
} }
if (! empty($promisedIsTypeOfResults)) { if (! empty($promisedIsTypeOfResults)) {
return $this->exeContext->promises->all($promisedIsTypeOfResults) return $this->exeContext->promiseAdapter->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) {
@ -1188,7 +1190,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
* *
@ -1300,7 +1302,7 @@ class ReferenceExecutor implements ExecutorImplementation
{ {
$keys = array_keys($assoc); $keys = array_keys($assoc);
$valuesAndPromises = array_values($assoc); $valuesAndPromises = array_values($assoc);
$promise = $this->exeContext->promises->all($valuesAndPromises); $promise = $this->exeContext->promiseAdapter->all($valuesAndPromises);
return $promise->then(static function ($values) use ($keys) { return $promise->then(static function ($values) use ($keys) {
$resolvedResults = []; $resolvedResults = [];
@ -1325,9 +1327,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

@ -273,6 +273,7 @@ class Values
return $error->getMessage(); return $error->getMessage();
}, },
$errors $errors
) : []; )
: [];
} }
} }

View File

@ -821,7 +821,10 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation
} else { } else {
$childContexts = []; $childContexts = [];
foreach ($this->collector->collectFields($objectType, $ctx->shared->mergedSelectionSet ?? $this->mergeSelectionSets($ctx)) as $childShared) { foreach ($this->collector->collectFields(
$objectType,
$ctx->shared->mergedSelectionSet ?? $this->mergeSelectionSets($ctx)
) as $childShared) {
/** @var CoroutineContextShared $childShared */ /** @var CoroutineContextShared $childShared */
$childPath = $path; $childPath = $path;

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([]);
} }
/** /**
@ -1545,7 +1545,8 @@ 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();
} }

View File

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

View File

@ -73,10 +73,14 @@ class Helper
} }
if (stripos($contentType, 'application/graphql') !== false) { if (stripos($contentType, 'application/graphql') !== false) {
$rawBody = $readRawBodyFn ? $readRawBodyFn() : $this->readRawBody(); $rawBody = $readRawBodyFn
? $readRawBodyFn()
: $this->readRawBody();
$bodyParams = ['query' => $rawBody ?: '']; $bodyParams = ['query' => $rawBody ?: ''];
} elseif (stripos($contentType, 'application/json') !== false) { } elseif (stripos($contentType, 'application/json') !== false) {
$rawBody = $readRawBodyFn ? $readRawBodyFn() : $this->readRawBody(); $rawBody = $readRawBodyFn ?
$readRawBodyFn()
: $this->readRawBody();
$bodyParams = json_decode($rawBody ?: '', true); $bodyParams = json_decode($rawBody ?: '', true);
if (json_last_error()) { if (json_last_error()) {
@ -272,7 +276,9 @@ class Helper
); );
} }
$doc = $op->queryId ? $this->loadPersistedQuery($config, $op) : $op->query; $doc = $op->queryId
? $this->loadPersistedQuery($config, $op)
: $op->query;
if (! $doc instanceof DocumentNode) { if (! $doc instanceof DocumentNode) {
$doc = Parser::parse($doc); $doc = Parser::parse($doc);

View File

@ -20,11 +20,14 @@ class BooleanType extends ScalarType
public $description = 'The `Boolean` scalar type represents `true` or `false`.'; public $description = 'The `Boolean` scalar type represents `true` or `false`.';
/** /**
* @param mixed $value * Coerce the given value to a boolean.
* *
* @return bool * 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
*/ */
public function serialize($value) public function serialize($value) : bool
{ {
return (bool) $value; return (bool) $value;
} }

View File

@ -69,7 +69,9 @@ 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) ? call_user_func($fields) : $fields; $fields = is_callable($fields)
? call_user_func($fields)
: $fields;
if (! is_array($fields)) { if (! is_array($fields)) {
throw new InvariantViolation( throw new InvariantViolation(

View File

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

View File

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

View File

@ -185,7 +185,9 @@ 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) ? call_user_func($interfaces) : $interfaces; $interfaces = is_callable($interfaces)
? call_user_func($interfaces)
: $interfaces;
if ($interfaces !== null && ! is_array($interfaces)) { if ($interfaces !== null && ! is_array($interfaces)) {
throw new InvariantViolation( throw new InvariantViolation(
@ -207,12 +209,14 @@ class ObjectType extends Type implements OutputType, CompositeType, NullableType
*/ */
public function isTypeOf($value, $context, ResolveInfo $info) public function isTypeOf($value, $context, ResolveInfo $info)
{ {
return isset($this->config['isTypeOf']) ? call_user_func( return isset($this->config['isTypeOf'])
? call_user_func(
$this->config['isTypeOf'], $this->config['isTypeOf'],
$value, $value,
$context, $context,
$info $info
) : null; )
: null;
} }
/** /**

View File

@ -15,12 +15,13 @@ 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
@ -36,7 +37,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
@ -44,7 +45,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
@ -52,7 +53,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[][]
@ -60,7 +61,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
@ -68,7 +69,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[]
@ -76,15 +77,15 @@ class ResolveInfo
public $fragments = []; public $fragments = [];
/** /**
* Root value passed to query execution * Root value passed to query execution.
* *
* @api * @api
* @var mixed|null * @var mixed
*/ */
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
@ -92,7 +93,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[]
@ -136,7 +137,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{
@ -177,7 +178,10 @@ class ResolveInfo
/** @var FieldNode $fieldNode */ /** @var FieldNode $fieldNode */
foreach ($this->fieldNodes as $fieldNode) { foreach ($this->fieldNodes as $fieldNode) {
$fields = array_merge_recursive($fields, $this->foldSelectionSet($fieldNode->selectionSet, $depth)); $fields = array_merge_recursive(
$fields,
$this->foldSelectionSet($fieldNode->selectionSet, $depth)
);
} }
return $fields; return $fields;

View File

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

View File

@ -488,7 +488,9 @@ 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') ? $value->getType() : $value->type; return method_exists($value, 'getType')
? $value->getType()
: $value->type;
}, },
], ],
'defaultValue' => [ 'defaultValue' => [

View File

@ -768,8 +768,9 @@ class SchemaValidationContext
); );
} }
return $union->astNode ? return $union->astNode
$union->astNode->types : null; ? $union->astNode->types
: null;
} }
private function validateEnumValues(EnumType $enumType) private function validateEnumValues(EnumType $enumType)
@ -824,8 +825,9 @@ class SchemaValidationContext
); );
} }
return $enum->astNode ? return $enum->astNode
$enum->astNode->values : null; ? $enum->astNode->values
: null;
} }
private function validateInputFields(InputObjectType $inputObj) private function validateInputFields(InputObjectType $inputObj)

View File

@ -394,8 +394,8 @@ class ASTDefinitionBuilder
function ($typeNode) { function ($typeNode) {
return $this->buildType($typeNode); return $this->buildType($typeNode);
} }
) : )
[], : [],
'astNode' => $def, 'astNode' => $def,
]); ]);
} }

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 be really rare case and should be avoided when possible) * (yet this should rarely be the case and should be avoided when possible)
*/ */
class MixedStore implements ArrayAccess class MixedStore implements ArrayAccess
{ {

View File

@ -609,7 +609,9 @@ class SchemaExtender
} }
$schemaExtensionASTNodes = count($schemaExtensions) > 0 $schemaExtensionASTNodes = count($schemaExtensions) > 0
? ($schema->extensionASTNodes ? array_merge($schema->extensionASTNodes, $schemaExtensions) : $schemaExtensions) ? ($schema->extensionASTNodes
? 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,7 +362,8 @@ 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

@ -302,10 +302,12 @@ class TypeInfo
case $node instanceof InlineFragmentNode: case $node instanceof InlineFragmentNode:
case $node instanceof FragmentDefinitionNode: case $node instanceof FragmentDefinitionNode:
$typeConditionNode = $node->typeCondition; $typeConditionNode = $node->typeCondition;
$outputType = $typeConditionNode ? self::typeFromAST( $outputType = $typeConditionNode
? 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;

View File

@ -19,12 +19,14 @@ 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,13 +66,15 @@ class ProvidedRequiredArgumentsOnDirectives extends ValidationRule
} }
$requiredArgsMap[$def->name->value] = Utils::keyMap( $requiredArgsMap[$def->name->value] = Utils::keyMap(
$arguments ? array_filter($arguments, static function (Node $argument) : bool { $arguments
? 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

@ -401,7 +401,7 @@ class DeferredFieldsTest extends TestCase
return [ return [
'sync' => [ 'sync' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($rootValue, $a, $c, ResolveInfo $info) { 'resolve' => function ($rootValue, $args, $context, 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 ($rootValue, $a, $c, ResolveInfo $info) { 'resolve' => function ($rootValue, $args, $context, 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 ($rootValue, $a, $c, ResolveInfo $info) { 'resolve' => function ($rootValue, $args, $context, 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 ($rootValue, $a, $c, ResolveInfo $info) { 'resolve' => function ($rootValue, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path; $this->paths[] = $info->path;
return new Deferred(function () use ($info) { return new Deferred(function () use ($info) {

View File

@ -70,7 +70,12 @@ 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) ? ($parent instanceof NodeList ? iterator_to_array($parent) : $parent->toArray()) : $parent; $parentArray = $parent && ! is_array($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));
@ -114,7 +119,9 @@ class VisitorTest extends ValidatorTestCase
{ {
$result = $ast; $result = $ast;
foreach ($path as $key) { foreach ($path as $key) {
$resultArray = $result instanceof NodeList ? iterator_to_array($result) : $result->toArray(); $resultArray = $result instanceof NodeList
? iterator_to_array($result)
: $result->toArray();
self::assertArrayHasKey($key, $resultArray); self::assertArrayHasKey($key, $resultArray);
$result = $resultArray[$key]; $result = $resultArray[$key];
} }

View File

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

View File

@ -219,7 +219,9 @@ 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 ? iterator_to_array($ast->definitions->getIterator()) : $ast->definitions, $ast->definitions instanceof NodeList
? 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,7 +40,9 @@ function renderClassMethod(ReflectionMethod $method) {
$def = $type . '$' . $p->getName(); $def = $type . '$' . $p->getName();
if ($p->isDefaultValueAvailable()) { if ($p->isDefaultValueAvailable()) {
$val = $p->isDefaultValueConstant() ? $p->getDefaultValueConstantName() : $p->getDefaultValue(); $val = $p->isDefaultValueConstant()
? $p->getDefaultValueConstantName()
: $p->getDefaultValue();
$def .= " = " . Utils::printSafeJson($val); $def .= " = " . Utils::printSafeJson($val);
} }