Version 4.7.6
This commit is contained in:
1 parent
8ba7d43898
commit
7b7404e741
1085 files changed
+30590
-7837
No files matched your search
@@ -10,6 +10,7 @@ use GraphQL\Language\Source;
|
||||
use GraphQL\Language\SourceLocation;
|
||||
use GraphQL\Utils\Utils;
|
||||
use JsonSerializable;
|
||||
use ReturnTypeWillChange;
|
||||
use Throwable;
|
||||
use Traversable;
|
||||
use function array_filter;
|
||||
@@ -367,6 +368,7 @@ class Error extends Exception implements JsonSerializable, ClientAware
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toSerializableArray();
|
||||
|
||||
+5
-5
@@ -39,7 +39,7 @@ final class Warning
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public static function setWarningHandler(?callable $warningHandler = null)
|
||||
public static function setWarningHandler(?callable $warningHandler = null) : void
|
||||
{
|
||||
self::$warningHandler = $warningHandler;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ final class Warning
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public static function suppress($suppress = true)
|
||||
public static function suppress($suppress = true) : void
|
||||
{
|
||||
if ($suppress === true) {
|
||||
self::$enableWarnings = 0;
|
||||
@@ -81,7 +81,7 @@ final class Warning
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public static function enable($enable = true)
|
||||
public static function enable($enable = true) : void
|
||||
{
|
||||
if ($enable === true) {
|
||||
self::$enableWarnings = self::ALL;
|
||||
@@ -94,7 +94,7 @@ final class Warning
|
||||
}
|
||||
}
|
||||
|
||||
public static function warnOnce(string $errorMessage, int $warningId, ?int $messageLevel = null)
|
||||
public static function warnOnce(string $errorMessage, int $warningId, ?int $messageLevel = null) : void
|
||||
{
|
||||
$messageLevel = $messageLevel ?? E_USER_WARNING;
|
||||
|
||||
@@ -107,7 +107,7 @@ final class Warning
|
||||
}
|
||||
}
|
||||
|
||||
public static function warn(string $errorMessage, int $warningId, ?int $messageLevel = null)
|
||||
public static function warn(string $errorMessage, int $warningId, ?int $messageLevel = null) : void
|
||||
{
|
||||
$messageLevel = $messageLevel ?? E_USER_WARNING;
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ class ExecutionResult implements JsonSerializable
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
+48
-49
@@ -55,18 +55,18 @@ use function sprintf;
|
||||
class ReferenceExecutor implements ExecutorImplementation
|
||||
{
|
||||
/** @var object */
|
||||
private static $UNDEFINED;
|
||||
protected static $UNDEFINED;
|
||||
|
||||
/** @var ExecutionContext */
|
||||
private $exeContext;
|
||||
protected $exeContext;
|
||||
|
||||
/** @var SplObjectStorage */
|
||||
private $subFieldCache;
|
||||
protected $subFieldCache;
|
||||
|
||||
private function __construct(ExecutionContext $context)
|
||||
protected function __construct(ExecutionContext $context)
|
||||
{
|
||||
if (! self::$UNDEFINED) {
|
||||
self::$UNDEFINED = Utils::undefined();
|
||||
if (! static::$UNDEFINED) {
|
||||
static::$UNDEFINED = Utils::undefined();
|
||||
}
|
||||
$this->exeContext = $context;
|
||||
$this->subFieldCache = new SplObjectStorage();
|
||||
@@ -87,7 +87,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
?string $operationName,
|
||||
callable $fieldResolver
|
||||
) : ExecutorImplementation {
|
||||
$exeContext = self::buildExecutionContext(
|
||||
$exeContext = static::buildExecutionContext(
|
||||
$schema,
|
||||
$documentNode,
|
||||
$rootValue,
|
||||
@@ -116,7 +116,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
};
|
||||
}
|
||||
|
||||
return new self($exeContext);
|
||||
return new static($exeContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +129,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return ExecutionContext|array<Error>
|
||||
*/
|
||||
private static function buildExecutionContext(
|
||||
protected static function buildExecutionContext(
|
||||
Schema $schema,
|
||||
DocumentNode $documentNode,
|
||||
$rootValue,
|
||||
@@ -227,7 +227,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return ExecutionResult|Promise
|
||||
*/
|
||||
private function buildResponse($data)
|
||||
protected function buildResponse($data)
|
||||
{
|
||||
if ($this->isPromise($data)) {
|
||||
return $data->then(function ($resolved) {
|
||||
@@ -248,7 +248,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return array<mixed>|Promise|stdClass|null
|
||||
*/
|
||||
private function executeOperation(OperationDefinitionNode $operation, $rootValue)
|
||||
protected function executeOperation(OperationDefinitionNode $operation, $rootValue)
|
||||
{
|
||||
$type = $this->getOperationRootType($this->exeContext->schema, $operation);
|
||||
$fields = $this->collectFields($type, $operation->selectionSet, new ArrayObject(), new ArrayObject());
|
||||
@@ -290,7 +290,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @throws Error
|
||||
*/
|
||||
private function getOperationRootType(Schema $schema, OperationDefinitionNode $operation) : ObjectType
|
||||
protected function getOperationRootType(Schema $schema, OperationDefinitionNode $operation) : ObjectType
|
||||
{
|
||||
switch ($operation->operation) {
|
||||
case 'query':
|
||||
@@ -339,7 +339,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
* returns an Interface or Union type, the "runtime type" will be the actual
|
||||
* Object type returned by that field.
|
||||
*/
|
||||
private function collectFields(
|
||||
protected function collectFields(
|
||||
ObjectType $runtimeType,
|
||||
SelectionSetNode $selectionSet,
|
||||
ArrayObject $fields,
|
||||
@@ -352,7 +352,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
if (! $this->shouldIncludeNode($selection)) {
|
||||
break;
|
||||
}
|
||||
$name = self::getFieldEntryKey($selection);
|
||||
$name = static::getFieldEntryKey($selection);
|
||||
if (! isset($fields[$name])) {
|
||||
$fields[$name] = new ArrayObject();
|
||||
}
|
||||
@@ -402,7 +402,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @param FragmentSpreadNode|FieldNode|InlineFragmentNode $node
|
||||
*/
|
||||
private function shouldIncludeNode(SelectionNode $node) : bool
|
||||
protected function shouldIncludeNode(SelectionNode $node) : bool
|
||||
{
|
||||
$variableValues = $this->exeContext->variableValues;
|
||||
$skipDirective = Directive::skipDirective();
|
||||
@@ -427,7 +427,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
/**
|
||||
* Implements the logic to compute the key of a given fields entry
|
||||
*/
|
||||
private static function getFieldEntryKey(FieldNode $node) : string
|
||||
protected static function getFieldEntryKey(FieldNode $node) : string
|
||||
{
|
||||
return $node->alias === null ? $node->name->value : $node->alias->value;
|
||||
}
|
||||
@@ -437,7 +437,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @param FragmentDefinitionNode|InlineFragmentNode $fragment
|
||||
*/
|
||||
private function doesFragmentConditionMatch(Node $fragment, ObjectType $type) : bool
|
||||
protected function doesFragmentConditionMatch(Node $fragment, ObjectType $type) : bool
|
||||
{
|
||||
$typeConditionNode = $fragment->typeCondition;
|
||||
if ($typeConditionNode === null) {
|
||||
@@ -463,7 +463,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return array<mixed>|Promise|stdClass
|
||||
*/
|
||||
private function executeFieldsSerially(ObjectType $parentType, $rootValue, array $path, ArrayObject $fields)
|
||||
protected function executeFieldsSerially(ObjectType $parentType, $rootValue, array $path, ArrayObject $fields)
|
||||
{
|
||||
$result = $this->promiseReduce(
|
||||
array_keys($fields->getArrayCopy()),
|
||||
@@ -472,7 +472,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
$fieldPath = $path;
|
||||
$fieldPath[] = $responseName;
|
||||
$result = $this->resolveField($parentType, $rootValue, $fieldNodes, $fieldPath);
|
||||
if ($result === self::$UNDEFINED) {
|
||||
if ($result === static::$UNDEFINED) {
|
||||
return $results;
|
||||
}
|
||||
$promise = $this->getPromise($result);
|
||||
@@ -492,11 +492,11 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
|
||||
if ($this->isPromise($result)) {
|
||||
return $result->then(static function ($resolvedResults) {
|
||||
return self::fixResultsIfEmptyArray($resolvedResults);
|
||||
return static::fixResultsIfEmptyArray($resolvedResults);
|
||||
});
|
||||
}
|
||||
|
||||
return self::fixResultsIfEmptyArray($result);
|
||||
return static::fixResultsIfEmptyArray($result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -511,14 +511,14 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return array<mixed>|Throwable|mixed|null
|
||||
*/
|
||||
private function resolveField(ObjectType $parentType, $rootValue, ArrayObject $fieldNodes, array $path)
|
||||
protected function resolveField(ObjectType $parentType, $rootValue, ArrayObject $fieldNodes, array $path)
|
||||
{
|
||||
$exeContext = $this->exeContext;
|
||||
$fieldNode = $fieldNodes[0];
|
||||
$fieldName = $fieldNode->name->value;
|
||||
$fieldDef = $this->getFieldDef($exeContext->schema, $parentType, $fieldName);
|
||||
if ($fieldDef === null) {
|
||||
return self::$UNDEFINED;
|
||||
return static::$UNDEFINED;
|
||||
}
|
||||
$returnType = $fieldDef->getType();
|
||||
// The resolve function's optional 3rd argument is a context value that
|
||||
@@ -574,7 +574,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
* added to the query type, but that would require mutating type
|
||||
* definitions, which would cause issues.
|
||||
*/
|
||||
private function getFieldDef(Schema $schema, ObjectType $parentType, string $fieldName) : ?FieldDefinition
|
||||
protected function getFieldDef(Schema $schema, ObjectType $parentType, string $fieldName) : ?FieldDefinition
|
||||
{
|
||||
static $schemaMetaFieldDef, $typeMetaFieldDef, $typeNameMetaFieldDef;
|
||||
$schemaMetaFieldDef = $schemaMetaFieldDef ?? Introspection::schemaMetaFieldDef();
|
||||
@@ -591,9 +591,8 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
if ($fieldName === $typeNameMetaFieldDef->name) {
|
||||
return $typeNameMetaFieldDef;
|
||||
}
|
||||
$tmp = $parentType->getFields();
|
||||
|
||||
return $tmp[$fieldName] ?? null;
|
||||
return $parentType->findField($fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,7 +603,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return Throwable|Promise|mixed
|
||||
*/
|
||||
private function resolveFieldValueOrError(
|
||||
protected function resolveFieldValueOrError(
|
||||
FieldDefinition $fieldDef,
|
||||
FieldNode $fieldNode,
|
||||
callable $resolveFn,
|
||||
@@ -636,7 +635,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return array<mixed>|Promise|stdClass|null
|
||||
*/
|
||||
private function completeValueCatchingError(
|
||||
protected function completeValueCatchingError(
|
||||
Type $returnType,
|
||||
ArrayObject $fieldNodes,
|
||||
ResolveInfo $info,
|
||||
@@ -676,7 +675,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @throws Error
|
||||
*/
|
||||
private function handleFieldError($rawError, ArrayObject $fieldNodes, array $path, Type $returnType) : void
|
||||
protected function handleFieldError($rawError, ArrayObject $fieldNodes, array $path, Type $returnType) : void
|
||||
{
|
||||
$error = Error::createLocatedError(
|
||||
$rawError,
|
||||
@@ -723,7 +722,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
* @throws Error
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function completeValue(
|
||||
protected function completeValue(
|
||||
Type $returnType,
|
||||
ArrayObject $fieldNodes,
|
||||
ResolveInfo $info,
|
||||
@@ -799,7 +798,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function isPromise($value) : bool
|
||||
protected function isPromise($value) : bool
|
||||
{
|
||||
return $value instanceof Promise || $this->exeContext->promiseAdapter->isThenable($value);
|
||||
}
|
||||
@@ -810,7 +809,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function getPromise($value) : ?Promise
|
||||
protected function getPromise($value) : ?Promise
|
||||
{
|
||||
if ($value === null || $value instanceof Promise) {
|
||||
return $value;
|
||||
@@ -843,7 +842,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return Promise|mixed|null
|
||||
*/
|
||||
private function promiseReduce(array $values, callable $callback, $initialValue)
|
||||
protected function promiseReduce(array $values, callable $callback, $initialValue)
|
||||
{
|
||||
return array_reduce(
|
||||
$values,
|
||||
@@ -871,7 +870,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function completeListValue(ListOfType $returnType, ArrayObject $fieldNodes, ResolveInfo $info, array $path, &$results)
|
||||
protected function completeListValue(ListOfType $returnType, ArrayObject $fieldNodes, ResolveInfo $info, array $path, &$results)
|
||||
{
|
||||
$itemType = $returnType->getWrappedType();
|
||||
Utils::invariant(
|
||||
@@ -906,7 +905,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function completeLeafValue(LeafType $returnType, &$result)
|
||||
protected function completeLeafValue(LeafType $returnType, &$result)
|
||||
{
|
||||
try {
|
||||
return $returnType->serialize($result);
|
||||
@@ -930,7 +929,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @throws Error
|
||||
*/
|
||||
private function completeAbstractValue(
|
||||
protected function completeAbstractValue(
|
||||
AbstractType $returnType,
|
||||
ArrayObject $fieldNodes,
|
||||
ResolveInfo $info,
|
||||
@@ -941,7 +940,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
$typeCandidate = $returnType->resolveType($result, $exeContext->contextValue, $info);
|
||||
|
||||
if ($typeCandidate === null) {
|
||||
$runtimeType = self::defaultTypeResolver($result, $exeContext->contextValue, $info, $returnType);
|
||||
$runtimeType = static::defaultTypeResolver($result, $exeContext->contextValue, $info, $returnType);
|
||||
} elseif (is_callable($typeCandidate)) {
|
||||
$runtimeType = Schema::resolveType($typeCandidate);
|
||||
} else {
|
||||
@@ -1001,7 +1000,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return Promise|Type|string|null
|
||||
*/
|
||||
private function defaultTypeResolver($value, $contextValue, ResolveInfo $info, AbstractType $abstractType)
|
||||
protected function defaultTypeResolver($value, $contextValue, ResolveInfo $info, AbstractType $abstractType)
|
||||
{
|
||||
// First, look for `__typename`.
|
||||
if ($value !== null &&
|
||||
@@ -1066,7 +1065,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @throws Error
|
||||
*/
|
||||
private function completeObjectValue(
|
||||
protected function completeObjectValue(
|
||||
ObjectType $returnType,
|
||||
ArrayObject $fieldNodes,
|
||||
ResolveInfo $info,
|
||||
@@ -1116,7 +1115,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return Error
|
||||
*/
|
||||
private function invalidReturnTypeError(
|
||||
protected function invalidReturnTypeError(
|
||||
ObjectType $returnType,
|
||||
$result,
|
||||
ArrayObject $fieldNodes
|
||||
@@ -1135,7 +1134,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @throws Error
|
||||
*/
|
||||
private function collectAndExecuteSubfields(
|
||||
protected function collectAndExecuteSubfields(
|
||||
ObjectType $returnType,
|
||||
ArrayObject $fieldNodes,
|
||||
array $path,
|
||||
@@ -1151,7 +1150,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
* type. Memoizing ensures the subfields are not repeatedly calculated, which
|
||||
* saves overhead when resolving lists of values.
|
||||
*/
|
||||
private function collectSubFields(ObjectType $returnType, ArrayObject $fieldNodes) : ArrayObject
|
||||
protected function collectSubFields(ObjectType $returnType, ArrayObject $fieldNodes) : ArrayObject
|
||||
{
|
||||
if (! isset($this->subFieldCache[$returnType])) {
|
||||
$this->subFieldCache[$returnType] = new SplObjectStorage();
|
||||
@@ -1186,7 +1185,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return Promise|stdClass|array<mixed>
|
||||
*/
|
||||
private function executeFields(ObjectType $parentType, $rootValue, array $path, ArrayObject $fields)
|
||||
protected function executeFields(ObjectType $parentType, $rootValue, array $path, ArrayObject $fields)
|
||||
{
|
||||
$containsPromise = false;
|
||||
$results = [];
|
||||
@@ -1194,7 +1193,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
$fieldPath = $path;
|
||||
$fieldPath[] = $responseName;
|
||||
$result = $this->resolveField($parentType, $rootValue, $fieldNodes, $fieldPath);
|
||||
if ($result === self::$UNDEFINED) {
|
||||
if ($result === static::$UNDEFINED) {
|
||||
continue;
|
||||
}
|
||||
if (! $containsPromise && $this->isPromise($result)) {
|
||||
@@ -1204,7 +1203,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
}
|
||||
// If there are no promises, we can just return the object
|
||||
if (! $containsPromise) {
|
||||
return self::fixResultsIfEmptyArray($results);
|
||||
return static::fixResultsIfEmptyArray($results);
|
||||
}
|
||||
|
||||
// Otherwise, results is a map from field name to the result of resolving that
|
||||
@@ -1222,7 +1221,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @return array<mixed>|stdClass|mixed
|
||||
*/
|
||||
private static function fixResultsIfEmptyArray($results)
|
||||
protected static function fixResultsIfEmptyArray($results)
|
||||
{
|
||||
if ($results === []) {
|
||||
return new stdClass();
|
||||
@@ -1237,7 +1236,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
*
|
||||
* @param array<string, Promise|mixed> $assoc
|
||||
*/
|
||||
private function promiseForAssocArray(array $assoc) : Promise
|
||||
protected function promiseForAssocArray(array $assoc) : Promise
|
||||
{
|
||||
$keys = array_keys($assoc);
|
||||
$valuesAndPromises = array_values($assoc);
|
||||
@@ -1249,7 +1248,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
$resolvedResults[$keys[$i]] = $value;
|
||||
}
|
||||
|
||||
return self::fixResultsIfEmptyArray($resolvedResults);
|
||||
return static::fixResultsIfEmptyArray($resolvedResults);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1258,7 +1257,7 @@ class ReferenceExecutor implements ExecutorImplementation
|
||||
* @param InterfaceType|UnionType $returnType
|
||||
* @param mixed $result
|
||||
*/
|
||||
private function ensureValidRuntimeType(
|
||||
protected function ensureValidRuntimeType(
|
||||
$runtimeTypeOrName,
|
||||
AbstractType $returnType,
|
||||
ResolveInfo $info,
|
||||
|
||||
@@ -9,6 +9,7 @@ use Countable;
|
||||
use GraphQL\Utils\AST;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
use function array_merge;
|
||||
use function array_splice;
|
||||
@@ -69,6 +70,7 @@ class NodeList implements ArrayAccess, IteratorAggregate, Countable
|
||||
*
|
||||
* @phpstan-return T
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($offset)// : Node
|
||||
{
|
||||
$item = $this->nodes[$offset];
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace GraphQL\Language;
|
||||
|
||||
use JsonSerializable;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
class SourceLocation implements JsonSerializable
|
||||
{
|
||||
@@ -46,6 +47,7 @@ class SourceLocation implements JsonSerializable
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toSerializableArray();
|
||||
|
||||
+21
-3
@@ -559,10 +559,12 @@ class Helper
|
||||
);
|
||||
}
|
||||
} else {
|
||||
parse_str((string) $request->getBody(), $bodyParams);
|
||||
if ($request instanceof ServerRequestInterface) {
|
||||
$bodyParams = $request->getParsedBody();
|
||||
}
|
||||
|
||||
if (! is_array($bodyParams)) {
|
||||
throw new RequestError('Unexpected content type: ' . Utils::printSafeJson($contentType[0]));
|
||||
if (! isset($bodyParams)) {
|
||||
$bodyParams = $this->decodeContent((string) $request->getBody(), $contentType[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -576,6 +578,22 @@ class Helper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*
|
||||
* @throws RequestError
|
||||
*/
|
||||
protected function decodeContent(string $rawBody, string $contentType) : array
|
||||
{
|
||||
parse_str($rawBody, $bodyParams);
|
||||
|
||||
if (! is_array($bodyParams)) {
|
||||
throw new RequestError('Unexpected content type: ' . Utils::printSafeJson($contentType));
|
||||
}
|
||||
|
||||
return $bodyParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts query execution result to PSR-7 response
|
||||
*
|
||||
|
||||
@@ -12,6 +12,7 @@ use GraphQL\Type\Schema;
|
||||
use GraphQL\Utils\Utils;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
use function is_iterable;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
|
||||
@@ -95,9 +96,9 @@ class FieldDefinition
|
||||
if (is_callable($fields)) {
|
||||
$fields = $fields();
|
||||
}
|
||||
if (! is_array($fields)) {
|
||||
if (! is_iterable($fields)) {
|
||||
throw new InvariantViolation(
|
||||
sprintf('%s fields must be an array or a callable which returns such an array.', $type->name)
|
||||
sprintf('%s fields must be an iterable or a callable which returns such an iterable.', $type->name)
|
||||
);
|
||||
}
|
||||
$map = [];
|
||||
@@ -123,6 +124,17 @@ class FieldDefinition
|
||||
$fieldDef = self::create($field);
|
||||
} elseif ($field instanceof self) {
|
||||
$fieldDef = $field;
|
||||
} elseif (is_callable($field)) {
|
||||
if (! is_string($name)) {
|
||||
throw new InvariantViolation(
|
||||
sprintf(
|
||||
'%s lazy fields must be an associative array with field names as keys.',
|
||||
$type->name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$fieldDef = new UnresolvedFieldDefinition($type, $name, $field);
|
||||
} else {
|
||||
if (! is_string($name) || ! $field) {
|
||||
throw new InvariantViolation(
|
||||
@@ -137,7 +149,8 @@ class FieldDefinition
|
||||
|
||||
$fieldDef = self::create(['name' => $name, 'type' => $field]);
|
||||
}
|
||||
$map[$fieldDef->name] = $fieldDef;
|
||||
|
||||
$map[$fieldDef->getName()] = $fieldDef;
|
||||
}
|
||||
|
||||
return $map;
|
||||
@@ -180,6 +193,11 @@ class FieldDefinition
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getName() : string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getType() : Type
|
||||
{
|
||||
if (! isset($this->type)) {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Type\Definition;
|
||||
|
||||
use GraphQL\Error\InvariantViolation;
|
||||
|
||||
interface HasFieldsType
|
||||
{
|
||||
/**
|
||||
* @throws InvariantViolation
|
||||
*/
|
||||
public function getField(string $name) : FieldDefinition;
|
||||
|
||||
public function hasField(string $name) : bool;
|
||||
|
||||
public function findField(string $name) : ?FieldDefinition;
|
||||
|
||||
/**
|
||||
* @return array<string, FieldDefinition>
|
||||
*
|
||||
* @throws InvariantViolation
|
||||
*/
|
||||
public function getFields() : array;
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*
|
||||
* @throws InvariantViolation
|
||||
*/
|
||||
public function getFieldNames() : array;
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use GraphQL\Utils\Utils;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
use function is_iterable;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
|
||||
@@ -80,14 +81,14 @@ class InputObjectType extends Type implements InputType, NullableType, NamedType
|
||||
$fields = $fields();
|
||||
}
|
||||
|
||||
if (! is_array($fields)) {
|
||||
if (! is_iterable($fields)) {
|
||||
throw new InvariantViolation(
|
||||
sprintf('%s fields must be an array or a callable which returns such an array.', $this->name)
|
||||
sprintf('%s fields must be an iterable or a callable which returns such an iterable.', $this->name)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($fields as $name => $field) {
|
||||
if ($field instanceof Type) {
|
||||
if ($field instanceof Type || is_callable($field)) {
|
||||
$field = ['type' => $field];
|
||||
}
|
||||
$field = new InputObjectField($field + ['name' => $name]);
|
||||
|
||||
@@ -24,8 +24,8 @@ class IntType extends ScalarType
|
||||
//
|
||||
// n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
|
||||
// they are internally represented as IEEE 754 doubles.
|
||||
private const MAX_INT = 2147483647;
|
||||
private const MIN_INT = -2147483648;
|
||||
public const MAX_INT = 2147483647;
|
||||
public const MIN_INT = -2147483648;
|
||||
|
||||
/** @var string */
|
||||
public $name = Type::INT;
|
||||
|
||||
@@ -15,7 +15,7 @@ use function is_callable;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
|
||||
class InterfaceType extends Type implements AbstractType, OutputType, CompositeType, NullableType, NamedType, ImplementingType
|
||||
class InterfaceType extends TypeWithFields implements AbstractType, OutputType, CompositeType, NullableType, NamedType, ImplementingType
|
||||
{
|
||||
/** @var InterfaceTypeDefinitionNode|null */
|
||||
public $astNode;
|
||||
@@ -23,13 +23,6 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
|
||||
/** @var array<int, InterfaceTypeExtensionNode> */
|
||||
public $extensionASTNodes;
|
||||
|
||||
/**
|
||||
* Lazily initialized.
|
||||
*
|
||||
* @var array<string, FieldDefinition>
|
||||
*/
|
||||
private $fields;
|
||||
|
||||
/**
|
||||
* Lazily initialized.
|
||||
*
|
||||
@@ -79,43 +72,6 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
|
||||
return $type;
|
||||
}
|
||||
|
||||
public function getField(string $name) : FieldDefinition
|
||||
{
|
||||
if (! isset($this->fields)) {
|
||||
$this->initializeFields();
|
||||
}
|
||||
Utils::invariant(isset($this->fields[$name]), 'Field "%s" is not defined for type "%s"', $name, $this->name);
|
||||
|
||||
return $this->fields[$name];
|
||||
}
|
||||
|
||||
public function hasField(string $name) : bool
|
||||
{
|
||||
if (! isset($this->fields)) {
|
||||
$this->initializeFields();
|
||||
}
|
||||
|
||||
return isset($this->fields[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FieldDefinition[]
|
||||
*/
|
||||
public function getFields() : array
|
||||
{
|
||||
if (! isset($this->fields)) {
|
||||
$this->initializeFields();
|
||||
}
|
||||
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
protected function initializeFields() : void
|
||||
{
|
||||
$fields = $this->config['fields'] ?? [];
|
||||
$this->fields = FieldDefinition::defineFieldMap($this, $fields);
|
||||
}
|
||||
|
||||
public function implementsInterface(InterfaceType $interfaceType) : bool
|
||||
{
|
||||
if (! isset($this->interfaceMap)) {
|
||||
|
||||
+1
-50
@@ -55,7 +55,7 @@ use function sprintf;
|
||||
* }
|
||||
* ]);
|
||||
*/
|
||||
class ObjectType extends Type implements OutputType, CompositeType, NullableType, NamedType, ImplementingType
|
||||
class ObjectType extends TypeWithFields implements OutputType, CompositeType, NullableType, NamedType, ImplementingType
|
||||
{
|
||||
/** @var ObjectTypeDefinitionNode|null */
|
||||
public $astNode;
|
||||
@@ -66,13 +66,6 @@ class ObjectType extends Type implements OutputType, CompositeType, NullableType
|
||||
/** @var ?callable */
|
||||
public $resolveFieldFn;
|
||||
|
||||
/**
|
||||
* Lazily initialized.
|
||||
*
|
||||
* @var FieldDefinition[]
|
||||
*/
|
||||
private $fields;
|
||||
|
||||
/**
|
||||
* Lazily initialized.
|
||||
*
|
||||
@@ -123,48 +116,6 @@ class ObjectType extends Type implements OutputType, CompositeType, NullableType
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvariantViolation
|
||||
*/
|
||||
public function getField(string $name) : FieldDefinition
|
||||
{
|
||||
if (! isset($this->fields)) {
|
||||
$this->initializeFields();
|
||||
}
|
||||
Utils::invariant(isset($this->fields[$name]), 'Field "%s" is not defined for type "%s"', $name, $this->name);
|
||||
|
||||
return $this->fields[$name];
|
||||
}
|
||||
|
||||
public function hasField(string $name) : bool
|
||||
{
|
||||
if (! isset($this->fields)) {
|
||||
$this->initializeFields();
|
||||
}
|
||||
|
||||
return isset($this->fields[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FieldDefinition[]
|
||||
*
|
||||
* @throws InvariantViolation
|
||||
*/
|
||||
public function getFields() : array
|
||||
{
|
||||
if (! isset($this->fields)) {
|
||||
$this->initializeFields();
|
||||
}
|
||||
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
protected function initializeFields() : void
|
||||
{
|
||||
$fields = $this->config['fields'] ?? [];
|
||||
$this->fields = FieldDefinition::defineFieldMap($this, $fields);
|
||||
}
|
||||
|
||||
public function implementsInterface(InterfaceType $interfaceType) : bool
|
||||
{
|
||||
if (! isset($this->interfaceMap)) {
|
||||
|
||||
@@ -11,6 +11,7 @@ use GraphQL\Language\AST\FragmentDefinitionNode;
|
||||
use GraphQL\Language\AST\FragmentSpreadNode;
|
||||
use GraphQL\Language\AST\InlineFragmentNode;
|
||||
use GraphQL\Language\AST\SelectionSetNode;
|
||||
use GraphQL\Type\Introspection;
|
||||
use GraphQL\Type\Schema;
|
||||
use function array_diff_key;
|
||||
use function array_filter;
|
||||
@@ -167,7 +168,12 @@ class QueryPlan
|
||||
$implementors = [];
|
||||
foreach ($selectionSet->selections as $selectionNode) {
|
||||
if ($selectionNode instanceof FieldNode) {
|
||||
$fieldName = $selectionNode->name->value;
|
||||
$fieldName = $selectionNode->name->value;
|
||||
|
||||
if ($fieldName === Introspection::TYPE_NAME_FIELD_NAME) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $parentType->getField($fieldName);
|
||||
$selectionType = $type->getType();
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use GraphQL\Type\Introspection;
|
||||
use GraphQL\Utils\Utils;
|
||||
use JsonSerializable;
|
||||
use ReflectionClass;
|
||||
use ReturnTypeWillChange;
|
||||
use function array_keys;
|
||||
use function array_merge;
|
||||
use function assert;
|
||||
@@ -308,6 +309,7 @@ abstract class Type implements JsonSerializable
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toString();
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Type\Definition;
|
||||
|
||||
use GraphQL\Utils\Utils;
|
||||
use function array_keys;
|
||||
|
||||
abstract class TypeWithFields extends Type implements HasFieldsType
|
||||
{
|
||||
/**
|
||||
* Lazily initialized.
|
||||
*
|
||||
* @var array<string, FieldDefinition>
|
||||
*/
|
||||
private $fields;
|
||||
|
||||
private function initializeFields() : void
|
||||
{
|
||||
if (isset($this->fields)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->config['fields'] ?? [];
|
||||
$this->fields = FieldDefinition::defineFieldMap($this, $fields);
|
||||
}
|
||||
|
||||
public function getField(string $name) : FieldDefinition
|
||||
{
|
||||
Utils::invariant($this->hasField($name), 'Field "%s" is not defined for type "%s"', $name, $this->name);
|
||||
|
||||
return $this->findField($name);
|
||||
}
|
||||
|
||||
public function findField(string $name) : ?FieldDefinition
|
||||
{
|
||||
$this->initializeFields();
|
||||
|
||||
if (! isset($this->fields[$name])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->fields[$name] instanceof UnresolvedFieldDefinition) {
|
||||
$this->fields[$name] = $this->fields[$name]->resolve();
|
||||
}
|
||||
|
||||
return $this->fields[$name];
|
||||
}
|
||||
|
||||
public function hasField(string $name) : bool
|
||||
{
|
||||
$this->initializeFields();
|
||||
|
||||
return isset($this->fields[$name]);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getFields() : array
|
||||
{
|
||||
$this->initializeFields();
|
||||
|
||||
foreach ($this->fields as $name => $field) {
|
||||
if (! ($field instanceof UnresolvedFieldDefinition)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->fields[$name] = $field->resolve();
|
||||
}
|
||||
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getFieldNames() : array
|
||||
{
|
||||
$this->initializeFields();
|
||||
|
||||
return array_keys($this->fields);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Type\Definition;
|
||||
|
||||
use GraphQL\Error\InvariantViolation;
|
||||
use function is_array;
|
||||
use function sprintf;
|
||||
|
||||
class UnresolvedFieldDefinition
|
||||
{
|
||||
/** @var Type $type */
|
||||
private $type;
|
||||
|
||||
/** @var string $name */
|
||||
private $name;
|
||||
|
||||
/** @var callable(): (FieldDefinition|array<string, mixed>|Type) $resolver */
|
||||
private $resolver;
|
||||
|
||||
/**
|
||||
* @param callable(): (FieldDefinition|array<string, mixed>|Type) $resolver
|
||||
*/
|
||||
public function __construct(Type $type, string $name, callable $resolver)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->name = $name;
|
||||
$this->resolver = $resolver;
|
||||
}
|
||||
|
||||
public function getName() : string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function resolve() : FieldDefinition
|
||||
{
|
||||
$field = ($this->resolver)();
|
||||
|
||||
if ($field instanceof FieldDefinition) {
|
||||
if ($field->name !== $this->name) {
|
||||
throw new InvariantViolation(
|
||||
sprintf('%s.%s should not dynamically change its name when resolved lazily.', $this->type->name, $this->name)
|
||||
);
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
if (! is_array($field)) {
|
||||
return FieldDefinition::create(['name' => $this->name, 'type' => $field]);
|
||||
}
|
||||
|
||||
if (! isset($field['name'])) {
|
||||
$field['name'] = $this->name;
|
||||
} elseif ($field['name'] !== $this->name) {
|
||||
throw new InvariantViolation(
|
||||
sprintf('%s.%s should not dynamically change its name when resolved lazily.', $this->type->name, $this->name)
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($field['args']) && ! is_array($field['args'])) {
|
||||
throw new InvariantViolation(
|
||||
sprintf('%s.%s args must be an array.', $this->type->name, $this->name)
|
||||
);
|
||||
}
|
||||
|
||||
return FieldDefinition::create($field);
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -293,7 +293,7 @@ EOD;
|
||||
'Object and Interface types provide the fields they describe. Abstract ' .
|
||||
'types, Union and Interface, provide the Object types possible ' .
|
||||
'at runtime. List and NonNull types compose other types.',
|
||||
'fields' => static function () {
|
||||
'fields' => static function () : array {
|
||||
return [
|
||||
'kind' => [
|
||||
'type' => Type::nonNull(self::_typeKind()),
|
||||
@@ -337,7 +337,7 @@ EOD;
|
||||
'args' => [
|
||||
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false],
|
||||
],
|
||||
'resolve' => static function (Type $type, $args) {
|
||||
'resolve' => static function (Type $type, $args) : ?array {
|
||||
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
|
||||
$fields = $type->getFields();
|
||||
|
||||
@@ -381,7 +381,7 @@ EOD;
|
||||
'args' => [
|
||||
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false],
|
||||
],
|
||||
'resolve' => static function ($type, $args) {
|
||||
'resolve' => static function ($type, $args) : ?array {
|
||||
if ($type instanceof EnumType) {
|
||||
$values = array_values($type->getValues());
|
||||
|
||||
@@ -484,7 +484,7 @@ EOD;
|
||||
'description' =>
|
||||
'Object and Interface types are described by a list of Fields, each of ' .
|
||||
'which has a name, potentially a list of arguments, and a return type.',
|
||||
'fields' => static function () {
|
||||
'fields' => static function () : array {
|
||||
return [
|
||||
'name' => [
|
||||
'type' => Type::nonNull(Type::string()),
|
||||
@@ -540,7 +540,7 @@ EOD;
|
||||
'Arguments provided to Fields or Directives and the input fields of an ' .
|
||||
'InputObject are represented as Input Values which describe their type ' .
|
||||
'and optionally a default value.',
|
||||
'fields' => static function () {
|
||||
'fields' => static function () : array {
|
||||
return [
|
||||
'name' => [
|
||||
'type' => Type::nonNull(Type::string()),
|
||||
@@ -661,7 +661,7 @@ EOD;
|
||||
],
|
||||
'args' => [
|
||||
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))),
|
||||
'resolve' => static function (Directive $directive) {
|
||||
'resolve' => static function (Directive $directive) : array {
|
||||
return $directive->args ?? [];
|
||||
},
|
||||
],
|
||||
|
||||
+9
-25
@@ -20,9 +20,10 @@ use GraphQL\Type\Definition\UnionType;
|
||||
use GraphQL\Utils\InterfaceImplementations;
|
||||
use GraphQL\Utils\TypeInfo;
|
||||
use GraphQL\Utils\Utils;
|
||||
use InvalidArgumentException;
|
||||
use Traversable;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_callable;
|
||||
@@ -60,13 +61,6 @@ class Schema
|
||||
*/
|
||||
private $resolvedTypes = [];
|
||||
|
||||
/**
|
||||
* Lazily initialized.
|
||||
*
|
||||
* @var array<string, array<string, ObjectType|UnionType>>
|
||||
*/
|
||||
private $subTypeMap;
|
||||
|
||||
/**
|
||||
* Lazily initialised.
|
||||
*
|
||||
@@ -507,25 +501,15 @@ class Schema
|
||||
*/
|
||||
public function isSubType(AbstractType $abstractType, ImplementingType $maybeSubType) : bool
|
||||
{
|
||||
if (! isset($this->subTypeMap[$abstractType->name])) {
|
||||
$this->subTypeMap[$abstractType->name] = [];
|
||||
|
||||
if ($abstractType instanceof UnionType) {
|
||||
foreach ($abstractType->getTypes() as $type) {
|
||||
$this->subTypeMap[$abstractType->name][$type->name] = true;
|
||||
}
|
||||
} else {
|
||||
$implementations = $this->getImplementations($abstractType);
|
||||
foreach ($implementations->objects() as $type) {
|
||||
$this->subTypeMap[$abstractType->name][$type->name] = true;
|
||||
}
|
||||
foreach ($implementations->interfaces() as $type) {
|
||||
$this->subTypeMap[$abstractType->name][$type->name] = true;
|
||||
}
|
||||
}
|
||||
if ($abstractType instanceof InterfaceType) {
|
||||
return $maybeSubType->implementsInterface($abstractType);
|
||||
}
|
||||
|
||||
return isset($this->subTypeMap[$abstractType->name][$maybeSubType->name]);
|
||||
if ($abstractType instanceof UnionType) {
|
||||
return $abstractType->isPossibleType($maybeSubType);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('$abstractType must be of type UnionType|InterfaceType got: %s.', get_class($abstractType)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -372,8 +372,9 @@ class SchemaValidationContext
|
||||
*/
|
||||
private function validateDirectivesAtLocation($directives, string $location)
|
||||
{
|
||||
$directivesNamed = [];
|
||||
$schema = $this->schema;
|
||||
/** @var array<string, array<int, DirectiveNode>> $potentiallyDuplicateDirectives */
|
||||
$potentiallyDuplicateDirectives = [];
|
||||
$schema = $this->schema;
|
||||
foreach ($directives as $directive) {
|
||||
$directiveName = $directive->name->value;
|
||||
|
||||
@@ -386,6 +387,7 @@ class SchemaValidationContext
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$includes = Utils::some(
|
||||
$schemaDirective->locations,
|
||||
static function ($schemaLocation) use ($location) : bool {
|
||||
@@ -402,17 +404,22 @@ class SchemaValidationContext
|
||||
);
|
||||
}
|
||||
|
||||
$existingNodes = $directivesNamed[$directiveName] ?? [];
|
||||
$existingNodes[] = $directive;
|
||||
$directivesNamed[$directiveName] = $existingNodes;
|
||||
if ($schemaDirective->isRepeatable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$existingNodes = $potentiallyDuplicateDirectives[$directiveName] ?? [];
|
||||
$existingNodes[] = $directive;
|
||||
$potentiallyDuplicateDirectives[$directiveName] = $existingNodes;
|
||||
}
|
||||
foreach ($directivesNamed as $directiveName => $directiveList) {
|
||||
|
||||
foreach ($potentiallyDuplicateDirectives as $directiveName => $directiveList) {
|
||||
if (count($directiveList) <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->reportError(
|
||||
sprintf('Directive @%s used twice at the same location.', $directiveName),
|
||||
sprintf('Non-repeatable directive @%s used more than once at the same location.', $directiveName),
|
||||
$directiveList
|
||||
);
|
||||
}
|
||||
@@ -426,7 +433,7 @@ class SchemaValidationContext
|
||||
$fieldMap = $type->getFields();
|
||||
|
||||
// Objects and Interfaces both must define one or more fields.
|
||||
if (! $fieldMap) {
|
||||
if ($fieldMap === []) {
|
||||
$this->reportError(
|
||||
sprintf('Type %s must define one or more fields.', $type->name),
|
||||
$this->getAllNodes($type)
|
||||
|
||||
+31
@@ -584,6 +584,8 @@ class AST
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use getOperationAST instead.
|
||||
*
|
||||
* Returns operation type ("query", "mutation" or "subscription") given a document and operation name
|
||||
*
|
||||
* @param string $operationName
|
||||
@@ -608,4 +610,33 @@ class AST
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operation within a document by name.
|
||||
*
|
||||
* If a name is not provided, an operation is only returned if the document has exactly one.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public static function getOperationAST(DocumentNode $document, ?string $operationName = null) : ?OperationDefinitionNode
|
||||
{
|
||||
$operation = null;
|
||||
foreach ($document->definitions->getIterator() as $node) {
|
||||
if (! $node instanceof OperationDefinitionNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($operationName === null) {
|
||||
// We found a second operation, so we bail instead of returning an ambiguous result.
|
||||
if ($operation !== null) {
|
||||
return null;
|
||||
}
|
||||
$operation = $node;
|
||||
} elseif ($node->name instanceof NameNode && $node->name->value === $operationName) {
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
||||
return $operation;
|
||||
}
|
||||
}
|
||||
@@ -489,7 +489,7 @@ class BuildClientSchema
|
||||
'name' => $directive['name'],
|
||||
'description' => $directive['description'],
|
||||
'args' => $this->buildInputValueDefMap($directive['args']),
|
||||
'isRepeatable' => $directive['isRepeatable'],
|
||||
'isRepeatable' => $directive['isRepeatable'] ?? false,
|
||||
'locations' => $directive['locations'],
|
||||
]);
|
||||
}
|
||||
|
||||
+5
-7
@@ -7,6 +7,7 @@ namespace GraphQL\Utils;
|
||||
use ArrayAccess;
|
||||
use GraphQL\Type\Definition\EnumValueDefinition;
|
||||
use InvalidArgumentException;
|
||||
use ReturnTypeWillChange;
|
||||
use SplObjectStorage;
|
||||
use function array_key_exists;
|
||||
use function array_search;
|
||||
@@ -90,7 +91,7 @@ class MixedStore implements ArrayAccess
|
||||
* <p>
|
||||
* The return value will be casted to boolean if non-boolean was returned.
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
public function offsetExists($offset) : bool
|
||||
{
|
||||
if ($offset === false) {
|
||||
return $this->falseValueIsSet;
|
||||
@@ -135,6 +136,7 @@ class MixedStore implements ArrayAccess
|
||||
*
|
||||
* @return mixed Can return all value types.
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if ($offset === true) {
|
||||
@@ -181,10 +183,8 @@ class MixedStore implements ArrayAccess
|
||||
* @param mixed $value <p>
|
||||
* The value to set.
|
||||
* </p>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
public function offsetSet($offset, $value) : void
|
||||
{
|
||||
if ($offset === false) {
|
||||
$this->falseValue = $value;
|
||||
@@ -217,10 +217,8 @@ class MixedStore implements ArrayAccess
|
||||
* @param mixed $offset <p>
|
||||
* The offset to unset.
|
||||
* </p>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
public function offsetUnset($offset) : void
|
||||
{
|
||||
if ($offset === true) {
|
||||
$this->trueValue = null;
|
||||
|
||||
+9
-3
@@ -494,14 +494,19 @@ class SchemaExtender
|
||||
'locations' => $directive->locations,
|
||||
'args' => static::extendArgs($directive->args),
|
||||
'astNode' => $directive->astNode,
|
||||
'isRepeatable' => $directive->isRepeatable,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, bool> $options
|
||||
*/
|
||||
public static function extend(Schema $schema, DocumentNode $documentAST, array $options = []) : Schema
|
||||
{
|
||||
public static function extend(
|
||||
Schema $schema,
|
||||
DocumentNode $documentAST,
|
||||
array $options = [],
|
||||
?callable $typeConfigDecorator = null
|
||||
) : Schema {
|
||||
if (! (isset($options['assumeValid']) || isset($options['assumeValidSDL']))) {
|
||||
DocumentValidator::assertValidSDLExtension($documentAST, $schema);
|
||||
}
|
||||
@@ -579,7 +584,8 @@ class SchemaExtender
|
||||
}
|
||||
|
||||
throw new Error('Unknown type: "' . $typeName . '". Ensure that this type exists either in the original schema, or is added in a type definition.', [$typeName]);
|
||||
}
|
||||
},
|
||||
$typeConfigDecorator
|
||||
);
|
||||
|
||||
static::$extendTypeCache = [];
|
||||
|
||||
+10
-8
@@ -25,6 +25,7 @@ use GraphQL\Type\Definition\Directive;
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use GraphQL\Type\Definition\FieldArgument;
|
||||
use GraphQL\Type\Definition\FieldDefinition;
|
||||
use GraphQL\Type\Definition\HasFieldsType;
|
||||
use GraphQL\Type\Definition\ImplementingType;
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
use GraphQL\Type\Definition\InputType;
|
||||
@@ -196,9 +197,10 @@ class TypeInfo
|
||||
if ($type instanceof ImplementingType) {
|
||||
$nestedTypes = array_merge($nestedTypes, $type->getInterfaces());
|
||||
}
|
||||
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
|
||||
foreach ($type->getFields() as $fieldName => $field) {
|
||||
if (count($field->args ?? []) > 0) {
|
||||
|
||||
if ($type instanceof HasFieldsType) {
|
||||
foreach ($type->getFields() as $field) {
|
||||
if (count($field->args) > 0) {
|
||||
$fieldArgTypes = array_map(
|
||||
static function (FieldArgument $arg) : Type {
|
||||
return $arg->getType();
|
||||
@@ -212,7 +214,7 @@ class TypeInfo
|
||||
}
|
||||
}
|
||||
if ($type instanceof InputObjectType) {
|
||||
foreach ($type->getFields() as $fieldName => $field) {
|
||||
foreach ($type->getFields() as $field) {
|
||||
$nestedTypes[] = $field->getType();
|
||||
}
|
||||
}
|
||||
@@ -414,11 +416,11 @@ class TypeInfo
|
||||
if ($name === $typeNameMeta->name && $parentType instanceof CompositeType) {
|
||||
return $typeNameMeta;
|
||||
}
|
||||
if ($parentType instanceof ObjectType ||
|
||||
$parentType instanceof InterfaceType) {
|
||||
$fields = $parentType->getFields();
|
||||
|
||||
return $fields[$name] ?? null;
|
||||
if ($parentType instanceof ObjectType ||
|
||||
$parentType instanceof InterfaceType
|
||||
) {
|
||||
return $parentType->findField($name);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -85,15 +85,13 @@ class FieldsOnCorrectType extends ValidationRule
|
||||
$interfaceUsageCount = [];
|
||||
|
||||
foreach ($schema->getPossibleTypes($type) as $possibleType) {
|
||||
$fields = $possibleType->getFields();
|
||||
if (! isset($fields[$fieldName])) {
|
||||
if (! $possibleType->hasField($fieldName)) {
|
||||
continue;
|
||||
}
|
||||
// This object type defines this field.
|
||||
$suggestedObjectTypes[] = $possibleType->name;
|
||||
foreach ($possibleType->getInterfaces() as $possibleInterface) {
|
||||
$fields = $possibleInterface->getFields();
|
||||
if (! isset($fields[$fieldName])) {
|
||||
if (! $possibleInterface->hasField($fieldName)) {
|
||||
continue;
|
||||
}
|
||||
// This interface type defines this field.
|
||||
@@ -128,7 +126,7 @@ class FieldsOnCorrectType extends ValidationRule
|
||||
private function getSuggestedFieldNames(Schema $schema, $type, $fieldName)
|
||||
{
|
||||
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
|
||||
$possibleFieldNames = array_keys($type->getFields());
|
||||
$possibleFieldNames = $type->getFieldNames();
|
||||
|
||||
return Utils::suggestionList($fieldName, $possibleFieldNames);
|
||||
}
|
||||
|
||||
+4
-4
@@ -250,10 +250,10 @@ class OverlappingFieldsCanBeMerged extends ValidationRule
|
||||
$fieldName = $selection->name->value;
|
||||
$fieldDef = null;
|
||||
if ($parentType instanceof ObjectType ||
|
||||
$parentType instanceof InterfaceType) {
|
||||
$tmp = $parentType->getFields();
|
||||
if (isset($tmp[$fieldName])) {
|
||||
$fieldDef = $tmp[$fieldName];
|
||||
$parentType instanceof InterfaceType
|
||||
) {
|
||||
if ($parentType->hasField($fieldName)) {
|
||||
$fieldDef = $parentType->getField($fieldName);
|
||||
}
|
||||
}
|
||||
$responseName = $selection->alias ? $selection->alias->value : $fieldName;
|
||||
|
||||
@@ -10,13 +10,14 @@ use GraphQL\Language\AST\FragmentDefinitionNode;
|
||||
use GraphQL\Language\AST\FragmentSpreadNode;
|
||||
use GraphQL\Language\AST\InlineFragmentNode;
|
||||
use GraphQL\Language\AST\SelectionSetNode;
|
||||
use GraphQL\Type\Definition\HasFieldsType;
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Introspection;
|
||||
use GraphQL\Utils\TypeInfo;
|
||||
use GraphQL\Validator\ValidationContext;
|
||||
use InvalidArgumentException;
|
||||
use function class_alias;
|
||||
use function method_exists;
|
||||
use function sprintf;
|
||||
|
||||
abstract class QuerySecurityRule extends ValidationRule
|
||||
@@ -117,8 +118,7 @@ abstract class QuerySecurityRule extends ValidationRule
|
||||
case $selection instanceof FieldNode:
|
||||
$fieldName = $selection->name->value;
|
||||
$fieldDef = null;
|
||||
if ($parentType && method_exists($parentType, 'getFields')) {
|
||||
$tmp = $parentType->getFields();
|
||||
if ($parentType instanceof HasFieldsType || $parentType instanceof InputObjectType) {
|
||||
$schemaMetaFieldDef = Introspection::schemaMetaFieldDef();
|
||||
$typeMetaFieldDef = Introspection::typeMetaFieldDef();
|
||||
$typeNameMetaFieldDef = Introspection::typeNameMetaFieldDef();
|
||||
@@ -129,8 +129,8 @@ abstract class QuerySecurityRule extends ValidationRule
|
||||
$fieldDef = $typeMetaFieldDef;
|
||||
} elseif ($fieldName === $typeNameMetaFieldDef->name) {
|
||||
$fieldDef = $typeNameMetaFieldDef;
|
||||
} elseif (isset($tmp[$fieldName])) {
|
||||
$fieldDef = $tmp[$fieldName];
|
||||
} elseif ($parentType->hasField($fieldName)) {
|
||||
$fieldDef = $parentType->getField($fieldName);
|
||||
}
|
||||
}
|
||||
$responseName = $this->getFieldName($selection);
|
||||
|
||||
+10
-3
@@ -34,6 +34,7 @@ class UniqueDirectivesPerLocation extends ValidationRule
|
||||
|
||||
public function getASTVisitor(ASTValidationContext $context)
|
||||
{
|
||||
/** @var array<string, true> $uniqueDirectiveMap */
|
||||
$uniqueDirectiveMap = [];
|
||||
|
||||
$schema = $context->getSchema();
|
||||
@@ -41,16 +42,22 @@ class UniqueDirectivesPerLocation extends ValidationRule
|
||||
? $schema->getDirectives()
|
||||
: Directive::getInternalDirectives();
|
||||
foreach ($definedDirectives as $directive) {
|
||||
$uniqueDirectiveMap[$directive->name] = ! $directive->isRepeatable;
|
||||
if ($directive->isRepeatable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$uniqueDirectiveMap[$directive->name] = true;
|
||||
}
|
||||
|
||||
$astDefinitions = $context->getDocument()->definitions;
|
||||
foreach ($astDefinitions as $definition) {
|
||||
if (! ($definition instanceof DirectiveDefinitionNode)) {
|
||||
if (! ($definition instanceof DirectiveDefinitionNode)
|
||||
|| $definition->repeatable
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$uniqueDirectiveMap[$definition->name->value] = $definition->repeatable;
|
||||
$uniqueDirectiveMap[$definition->name->value] = true;
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -81,7 +81,7 @@ class ValuesOfCorrectType extends ValidationRule
|
||||
|
||||
return null;
|
||||
},
|
||||
NodeKind::OBJECT => function (ObjectValueNode $node) use ($context, &$fieldName) {
|
||||
NodeKind::OBJECT => function (ObjectValueNode $node) use ($context, &$fieldName) : ?VisitorOperation {
|
||||
// Note: TypeInfo will traverse into a list's item type, so look to the
|
||||
// parent input type to check if it is a list.
|
||||
$type = Type::getNamedType($context->getInputType());
|
||||
@@ -116,6 +116,8 @@ class ValuesOfCorrectType extends ValidationRule
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
NodeKind::OBJECT_FIELD => static function (ObjectFieldNode $node) use ($context) : void {
|
||||
$parentType = Type::getNamedType($context->getParentInputType());
|
||||
|
||||
@@ -31,6 +31,7 @@ use GraphQL\Utils\TypeInfo;
|
||||
use SplObjectStorage;
|
||||
use function array_merge;
|
||||
use function array_pop;
|
||||
use function assert;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
@@ -180,16 +181,15 @@ class ValidationContext extends ASTValidationContext
|
||||
while (count($setsToVisit) > 0) {
|
||||
$set = array_pop($setsToVisit);
|
||||
|
||||
for ($i = 0, $selectionCount = count($set->selections); $i < $selectionCount; $i++) {
|
||||
$selection = $set->selections[$i];
|
||||
foreach ($set->selections as $selection) {
|
||||
if ($selection instanceof FragmentSpreadNode) {
|
||||
$spreads[] = $selection;
|
||||
} elseif ($selection instanceof FieldNode || $selection instanceof InlineFragmentNode) {
|
||||
if ($selection->selectionSet) {
|
||||
$setsToVisit[] = $selection->selectionSet;
|
||||
}
|
||||
} else {
|
||||
throw InvariantViolation::shouldNotHappen();
|
||||
assert($selection instanceof FieldNode || $selection instanceof InlineFragmentNode);
|
||||
$selectionSet = $selection->selectionSet;
|
||||
if ($selectionSet !== null) {
|
||||
$setsToVisit[] = $selectionSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user