mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-22 21:06:05 +03:00
Fix CS in src/Type
This commit is contained in:
parent
8817e8e010
commit
00d547dc06
@ -52,35 +52,15 @@ class Values
|
|||||||
/** @var InputType|Type $varType */
|
/** @var InputType|Type $varType */
|
||||||
$varType = TypeInfo::typeFromAST($schema, $varDefNode->type);
|
$varType = TypeInfo::typeFromAST($schema, $varDefNode->type);
|
||||||
|
|
||||||
if (! Type::isInputType($varType)) {
|
if (Type::isInputType($varType)) {
|
||||||
$errors[] = new Error(
|
if (array_key_exists($varName, $inputs)) {
|
||||||
sprintf(
|
|
||||||
'Variable "$%s" expected value of type "%s" which cannot be used as an input type.',
|
|
||||||
$varName,
|
|
||||||
Printer::doPrint($varDefNode->type)
|
|
||||||
),
|
|
||||||
[$varDefNode->type]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (! array_key_exists($varName, $inputs)) {
|
|
||||||
if ($varType instanceof NonNull) {
|
|
||||||
$errors[] = new Error(
|
|
||||||
sprintf(
|
|
||||||
'Variable "$%s" of required type "%s" was not provided.',
|
|
||||||
$varName,
|
|
||||||
$varType
|
|
||||||
),
|
|
||||||
[$varDefNode]
|
|
||||||
);
|
|
||||||
} elseif ($varDefNode->defaultValue) {
|
|
||||||
$coercedValues[$varName] = AST::valueFromAST($varDefNode->defaultValue, $varType);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$value = $inputs[$varName];
|
$value = $inputs[$varName];
|
||||||
$coerced = Value::coerceValue($value, $varType, $varDefNode);
|
$coerced = Value::coerceValue($value, $varType, $varDefNode);
|
||||||
/** @var Error[] $coercionErrors */
|
/** @var Error[] $coercionErrors */
|
||||||
$coercionErrors = $coerced['errors'];
|
$coercionErrors = $coerced['errors'];
|
||||||
if (! empty($coercionErrors)) {
|
if (empty($coercionErrors)) {
|
||||||
|
$coercedValues[$varName] = $coerced['value'];
|
||||||
|
} else {
|
||||||
$messagePrelude = sprintf(
|
$messagePrelude = sprintf(
|
||||||
'Variable "$%s" got invalid value %s; ',
|
'Variable "$%s" got invalid value %s; ',
|
||||||
$varName,
|
$varName,
|
||||||
@ -98,10 +78,30 @@ class Values
|
|||||||
$error->getExtensions()
|
$error->getExtensions()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$coercedValues[$varName] = $coerced['value'];
|
if ($varType instanceof NonNull) {
|
||||||
|
$errors[] = new Error(
|
||||||
|
sprintf(
|
||||||
|
'Variable "$%s" of required type "%s" was not provided.',
|
||||||
|
$varName,
|
||||||
|
$varType
|
||||||
|
),
|
||||||
|
[$varDefNode]
|
||||||
|
);
|
||||||
|
} elseif ($varDefNode->defaultValue) {
|
||||||
|
$coercedValues[$varName] = AST::valueFromAST($varDefNode->defaultValue, $varType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$errors[] = new Error(
|
||||||
|
sprintf(
|
||||||
|
'Variable "$%s" expected value of type "%s" which cannot be used as an input type.',
|
||||||
|
$varName,
|
||||||
|
Printer::doPrint($varDefNode->type)
|
||||||
|
),
|
||||||
|
[$varDefNode->type]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,7 +470,7 @@ class Printer
|
|||||||
Utils::filter(
|
Utils::filter(
|
||||||
$maybeArray,
|
$maybeArray,
|
||||||
function ($x) {
|
function ($x) {
|
||||||
return ! ! $x;
|
return (bool) $x;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -6,14 +9,14 @@ export type GraphQLAbstractType =
|
|||||||
GraphQLInterfaceType |
|
GraphQLInterfaceType |
|
||||||
GraphQLUnionType;
|
GraphQLUnionType;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface AbstractType
|
interface AbstractType
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Resolves concrete ObjectType for given object value
|
* Resolves concrete ObjectType for given object value
|
||||||
*
|
*
|
||||||
* @param $objectValue
|
* @param object $objectValue
|
||||||
* @param $context
|
* @param mixed[] $context
|
||||||
* @param ResolveInfo $info
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function resolveType($objectValue, $context, ResolveInfo $info);
|
public function resolveType($objectValue, $context, ResolveInfo $info);
|
||||||
|
@ -1,25 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Language\AST\BooleanValueNode;
|
use GraphQL\Language\AST\BooleanValueNode;
|
||||||
use GraphQL\Language\AST\Node;
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class BooleanType
|
* Class BooleanType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class BooleanType extends ScalarType
|
class BooleanType extends ScalarType
|
||||||
{
|
{
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name = Type::BOOLEAN;
|
public $name = Type::BOOLEAN;
|
||||||
|
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $description = 'The `Boolean` scalar type represents `true` or `false`.';
|
public $description = 'The `Boolean` scalar type represents `true` or `false`.';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,7 +27,7 @@ class BooleanType extends ScalarType
|
|||||||
*/
|
*/
|
||||||
public function serialize($value)
|
public function serialize($value)
|
||||||
{
|
{
|
||||||
return !!$value;
|
return (bool) $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,16 +41,16 @@ class BooleanType extends ScalarType
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Cannot represent value as boolean: " . Utils::printSafe($value));
|
throw new Error('Cannot represent value as boolean: ' . Utils::printSafe($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Node $valueNode
|
* @param Node $valueNode
|
||||||
* @param array|null $variables
|
* @param mixed[]|null $variables
|
||||||
* @return bool|null
|
* @return bool|null
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function parseLiteral($valueNode, array $variables = null)
|
public function parseLiteral($valueNode, ?array $variables = null)
|
||||||
{
|
{
|
||||||
if ($valueNode instanceof BooleanValueNode) {
|
if ($valueNode instanceof BooleanValueNode) {
|
||||||
return (bool) $valueNode->value;
|
return (bool) $valueNode->value;
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -7,6 +10,7 @@ GraphQLObjectType |
|
|||||||
GraphQLInterfaceType |
|
GraphQLInterfaceType |
|
||||||
GraphQLUnionType;
|
GraphQLUnionType;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface CompositeType
|
interface CompositeType
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Language\AST\Node;
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Utils\AST;
|
use GraphQL\Utils\AST;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function call_user_func;
|
||||||
|
use function is_callable;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CustomScalarType
|
* Class CustomScalarType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class CustomScalarType extends ScalarType
|
class CustomScalarType extends ScalarType
|
||||||
{
|
{
|
||||||
@ -28,24 +33,26 @@ class CustomScalarType extends ScalarType
|
|||||||
{
|
{
|
||||||
if (isset($this->config['parseValue'])) {
|
if (isset($this->config['parseValue'])) {
|
||||||
return call_user_func($this->config['parseValue'], $value);
|
return call_user_func($this->config['parseValue'], $value);
|
||||||
} else {
|
|
||||||
return $value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Node $valueNode
|
* @param Node $valueNode
|
||||||
* @param array|null $variables
|
* @param mixed[]|null $variables
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function parseLiteral(/* GraphQL\Language\AST\ValueNode */ $valueNode, array $variables = null)
|
public function parseLiteral(/* GraphQL\Language\AST\ValueNode */
|
||||||
{
|
$valueNode,
|
||||||
|
?array $variables = null
|
||||||
|
) {
|
||||||
if (isset($this->config['parseLiteral'])) {
|
if (isset($this->config['parseLiteral'])) {
|
||||||
return call_user_func($this->config['parseLiteral'], $valueNode, $variables);
|
return call_user_func($this->config['parseLiteral'], $valueNode, $variables);
|
||||||
} else {
|
|
||||||
return AST::valueFromASTUntyped($valueNode, $variables);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return AST::valueFromASTUntyped($valueNode, $variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assertValid()
|
public function assertValid()
|
||||||
@ -54,16 +61,18 @@ class CustomScalarType extends ScalarType
|
|||||||
|
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
isset($this->config['serialize']) && is_callable($this->config['serialize']),
|
isset($this->config['serialize']) && is_callable($this->config['serialize']),
|
||||||
"{$this->name} must provide \"serialize\" function. If this custom Scalar " .
|
sprintf('%s must provide "serialize" function. If this custom Scalar ', $this->name) .
|
||||||
'is also used as an input type, ensure "parseValue" and "parseLiteral" ' .
|
'is also used as an input type, ensure "parseValue" and "parseLiteral" ' .
|
||||||
'functions are also provided.'
|
'functions are also provided.'
|
||||||
);
|
);
|
||||||
if (isset($this->config['parseValue']) || isset($this->config['parseLiteral'])) {
|
if (! isset($this->config['parseValue']) && ! isset($this->config['parseLiteral'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
isset($this->config['parseValue']) && isset($this->config['parseLiteral']) &&
|
isset($this->config['parseValue']) && isset($this->config['parseLiteral']) &&
|
||||||
is_callable($this->config['parseValue']) && is_callable($this->config['parseLiteral']),
|
is_callable($this->config['parseValue']) && is_callable($this->config['parseLiteral']),
|
||||||
"{$this->name} must provide both \"parseValue\" and \"parseLiteral\" functions."
|
sprintf('%s must provide both "parseValue" and "parseLiteral" functions.', $this->name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,159 +1,48 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Language\AST\DirectiveDefinitionNode;
|
use GraphQL\Language\AST\DirectiveDefinitionNode;
|
||||||
use GraphQL\Language\DirectiveLocation;
|
use GraphQL\Language\DirectiveLocation;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function array_key_exists;
|
||||||
|
use function is_array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Directive
|
* Class Directive
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class Directive
|
class Directive
|
||||||
{
|
{
|
||||||
const DEFAULT_DEPRECATION_REASON = 'No longer supported';
|
public const DEFAULT_DEPRECATION_REASON = 'No longer supported';
|
||||||
|
|
||||||
/**
|
/** @var Directive[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public static $internalDirectives;
|
public static $internalDirectives;
|
||||||
|
|
||||||
// Schema Definitions
|
// Schema Definitions
|
||||||
|
|
||||||
/**
|
/** @var string */
|
||||||
* @return Directive
|
|
||||||
*/
|
|
||||||
public static function includeDirective()
|
|
||||||
{
|
|
||||||
$internal = self::getInternalDirectives();
|
|
||||||
return $internal['include'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Directive
|
|
||||||
*/
|
|
||||||
public static function skipDirective()
|
|
||||||
{
|
|
||||||
$internal = self::getInternalDirectives();
|
|
||||||
return $internal['skip'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Directive
|
|
||||||
*/
|
|
||||||
public static function deprecatedDirective()
|
|
||||||
{
|
|
||||||
$internal = self::getInternalDirectives();
|
|
||||||
return $internal['deprecated'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Directive $directive
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function isSpecifiedDirective(Directive $directive)
|
|
||||||
{
|
|
||||||
return in_array($directive->name, array_keys(self::getInternalDirectives()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getInternalDirectives()
|
|
||||||
{
|
|
||||||
if (!self::$internalDirectives) {
|
|
||||||
self::$internalDirectives = [
|
|
||||||
'include' => new self([
|
|
||||||
'name' => 'include',
|
|
||||||
'description' => 'Directs the executor to include this field or fragment only when the `if` argument is true.',
|
|
||||||
'locations' => [
|
|
||||||
DirectiveLocation::FIELD,
|
|
||||||
DirectiveLocation::FRAGMENT_SPREAD,
|
|
||||||
DirectiveLocation::INLINE_FRAGMENT,
|
|
||||||
],
|
|
||||||
'args' => [
|
|
||||||
new FieldArgument([
|
|
||||||
'name' => 'if',
|
|
||||||
'type' => Type::nonNull(Type::boolean()),
|
|
||||||
'description' => 'Included when true.'
|
|
||||||
])
|
|
||||||
],
|
|
||||||
]),
|
|
||||||
'skip' => new self([
|
|
||||||
'name' => 'skip',
|
|
||||||
'description' => 'Directs the executor to skip this field or fragment when the `if` argument is true.',
|
|
||||||
'locations' => [
|
|
||||||
DirectiveLocation::FIELD,
|
|
||||||
DirectiveLocation::FRAGMENT_SPREAD,
|
|
||||||
DirectiveLocation::INLINE_FRAGMENT
|
|
||||||
],
|
|
||||||
'args' => [
|
|
||||||
new FieldArgument([
|
|
||||||
'name' => 'if',
|
|
||||||
'type' => Type::nonNull(Type::boolean()),
|
|
||||||
'description' => 'Skipped when true.'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
]),
|
|
||||||
'deprecated' => new self([
|
|
||||||
'name' => 'deprecated',
|
|
||||||
'description' => 'Marks an element of a GraphQL schema as no longer supported.',
|
|
||||||
'locations' => [
|
|
||||||
DirectiveLocation::FIELD_DEFINITION,
|
|
||||||
DirectiveLocation::ENUM_VALUE
|
|
||||||
],
|
|
||||||
'args' => [
|
|
||||||
new FieldArgument([
|
|
||||||
'name' => 'reason',
|
|
||||||
'type' => Type::string(),
|
|
||||||
'description' =>
|
|
||||||
'Explains why this element was deprecated, usually also including a ' .
|
|
||||||
'suggestion for how to access supported similar data. Formatted ' .
|
|
||||||
'in [Markdown](https://daringfireball.net/projects/markdown/).',
|
|
||||||
'defaultValue' => self::DEFAULT_DEPRECATION_REASON
|
|
||||||
])
|
|
||||||
]
|
|
||||||
])
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return self::$internalDirectives;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
public $description;
|
public $description;
|
||||||
|
|
||||||
/**
|
/** @var string[] */
|
||||||
* Values from self::$locationMap
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $locations;
|
public $locations;
|
||||||
|
|
||||||
/**
|
/** @var FieldArgument[] */
|
||||||
* @var FieldArgument[]
|
|
||||||
*/
|
|
||||||
public $args;
|
public $args;
|
||||||
|
|
||||||
/**
|
/** @var DirectiveDefinitionNode|null */
|
||||||
* @var DirectiveDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
/**
|
/** @var mixed[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $config;
|
public $config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive constructor.
|
*
|
||||||
* @param array $config
|
* @param mixed[] $config
|
||||||
*/
|
*/
|
||||||
public function __construct(array $config)
|
public function __construct(array $config)
|
||||||
{
|
{
|
||||||
@ -177,4 +66,103 @@ class Directive
|
|||||||
Utils::invariant(is_array($this->locations), 'Must provide locations for directive.');
|
Utils::invariant(is_array($this->locations), 'Must provide locations for directive.');
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Directive
|
||||||
|
*/
|
||||||
|
public static function includeDirective()
|
||||||
|
{
|
||||||
|
$internal = self::getInternalDirectives();
|
||||||
|
|
||||||
|
return $internal['include'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Directive[]
|
||||||
|
*/
|
||||||
|
public static function getInternalDirectives()
|
||||||
|
{
|
||||||
|
if (! self::$internalDirectives) {
|
||||||
|
self::$internalDirectives = [
|
||||||
|
'include' => new self([
|
||||||
|
'name' => 'include',
|
||||||
|
'description' => 'Directs the executor to include this field or fragment only when the `if` argument is true.',
|
||||||
|
'locations' => [
|
||||||
|
DirectiveLocation::FIELD,
|
||||||
|
DirectiveLocation::FRAGMENT_SPREAD,
|
||||||
|
DirectiveLocation::INLINE_FRAGMENT,
|
||||||
|
],
|
||||||
|
'args' => [new FieldArgument([
|
||||||
|
'name' => 'if',
|
||||||
|
'type' => Type::nonNull(Type::boolean()),
|
||||||
|
'description' => 'Included when true.',
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
'skip' => new self([
|
||||||
|
'name' => 'skip',
|
||||||
|
'description' => 'Directs the executor to skip this field or fragment when the `if` argument is true.',
|
||||||
|
'locations' => [
|
||||||
|
DirectiveLocation::FIELD,
|
||||||
|
DirectiveLocation::FRAGMENT_SPREAD,
|
||||||
|
DirectiveLocation::INLINE_FRAGMENT,
|
||||||
|
],
|
||||||
|
'args' => [new FieldArgument([
|
||||||
|
'name' => 'if',
|
||||||
|
'type' => Type::nonNull(Type::boolean()),
|
||||||
|
'description' => 'Skipped when true.',
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
'deprecated' => new self([
|
||||||
|
'name' => 'deprecated',
|
||||||
|
'description' => 'Marks an element of a GraphQL schema as no longer supported.',
|
||||||
|
'locations' => [
|
||||||
|
DirectiveLocation::FIELD_DEFINITION,
|
||||||
|
DirectiveLocation::ENUM_VALUE,
|
||||||
|
],
|
||||||
|
'args' => [new FieldArgument([
|
||||||
|
'name' => 'reason',
|
||||||
|
'type' => Type::string(),
|
||||||
|
'description' =>
|
||||||
|
'Explains why this element was deprecated, usually also including a ' .
|
||||||
|
'suggestion for how to access supported similar data. Formatted ' .
|
||||||
|
'in [Markdown](https://daringfireball.net/projects/markdown/).',
|
||||||
|
'defaultValue' => self::DEFAULT_DEPRECATION_REASON,
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$internalDirectives;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Directive
|
||||||
|
*/
|
||||||
|
public static function skipDirective()
|
||||||
|
{
|
||||||
|
$internal = self::getInternalDirectives();
|
||||||
|
|
||||||
|
return $internal['skip'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Directive
|
||||||
|
*/
|
||||||
|
public static function deprecatedDirective()
|
||||||
|
{
|
||||||
|
$internal = self::getInternalDirectives();
|
||||||
|
|
||||||
|
return $internal['deprecated'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isSpecifiedDirective(Directive $directive)
|
||||||
|
{
|
||||||
|
return array_key_exists($directive->name, self::getInternalDirectives());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\EnumTypeDefinitionNode;
|
use GraphQL\Language\AST\EnumTypeDefinitionNode;
|
||||||
use GraphQL\Language\AST\EnumValueNode;
|
use GraphQL\Language\AST\EnumValueNode;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Utils\MixedStore;
|
use GraphQL\Utils\MixedStore;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_array;
|
||||||
|
use function is_int;
|
||||||
|
use function is_string;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class EnumType
|
* Class EnumType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class EnumType extends Type implements InputType, OutputType, LeafType, NamedType
|
class EnumType extends Type implements InputType, OutputType, LeafType, NamedType
|
||||||
{
|
{
|
||||||
/**
|
/** @var EnumTypeDefinitionNode|null */
|
||||||
* @var EnumTypeDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
/**
|
/** @var EnumValueDefinition[] */
|
||||||
* @var EnumValueDefinition[]
|
|
||||||
*/
|
|
||||||
private $values;
|
private $values;
|
||||||
|
|
||||||
/**
|
/** @var MixedStore<mixed, EnumValueDefinition> */
|
||||||
* @var MixedStore<mixed, EnumValueDefinition>
|
|
||||||
*/
|
|
||||||
private $valueLookup;
|
private $valueLookup;
|
||||||
|
|
||||||
/**
|
/** @var \ArrayObject<string, EnumValueDefinition> */
|
||||||
* @var \ArrayObject<string, EnumValueDefinition>
|
|
||||||
*/
|
|
||||||
private $nameLookup;
|
private $nameLookup;
|
||||||
|
|
||||||
public function __construct($config)
|
public function __construct($config)
|
||||||
@ -43,11 +42,42 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
|||||||
Utils::invariant(is_string($config['name']), 'Must provide name.');
|
Utils::invariant(is_string($config['name']), 'Must provide name.');
|
||||||
|
|
||||||
$this->name = $config['name'];
|
$this->name = $config['name'];
|
||||||
$this->description = isset($config['description']) ? $config['description'] : null;
|
$this->description = $config['description'] ?? null;
|
||||||
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;
|
$this->astNode = $config['astNode'] ?? null;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|mixed[] $name
|
||||||
|
* @return EnumValueDefinition|null
|
||||||
|
*/
|
||||||
|
public function getValue($name)
|
||||||
|
{
|
||||||
|
$lookup = $this->getNameLookup();
|
||||||
|
|
||||||
|
if (! is_string($name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $lookup[$name] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \ArrayObject<string, EnumValueDefinition>
|
||||||
|
*/
|
||||||
|
private function getNameLookup()
|
||||||
|
{
|
||||||
|
if (! $this->nameLookup) {
|
||||||
|
$lookup = new \ArrayObject();
|
||||||
|
foreach ($this->getValues() as $value) {
|
||||||
|
$lookup[$value->name] = $value;
|
||||||
|
}
|
||||||
|
$this->nameLookup = $lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->nameLookup;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return EnumValueDefinition[]
|
* @return EnumValueDefinition[]
|
||||||
*/
|
*/
|
||||||
@ -59,19 +89,24 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
|||||||
|
|
||||||
if (isset($config['values'])) {
|
if (isset($config['values'])) {
|
||||||
if (! is_array($config['values'])) {
|
if (! is_array($config['values'])) {
|
||||||
throw new InvariantViolation("{$this->name} values must be an array");
|
throw new InvariantViolation(sprintf('%s values must be an array', $this->name));
|
||||||
}
|
}
|
||||||
foreach ($config['values'] as $name => $value) {
|
foreach ($config['values'] as $name => $value) {
|
||||||
if (is_string($name)) {
|
if (is_string($name)) {
|
||||||
if (!is_array($value)) {
|
if (is_array($value)) {
|
||||||
$value = ['name' => $name, 'value' => $value];
|
|
||||||
} else {
|
|
||||||
$value += ['name' => $name, 'value' => $name];
|
$value += ['name' => $name, 'value' => $name];
|
||||||
|
} else {
|
||||||
|
$value = ['name' => $name, 'value' => $value];
|
||||||
}
|
}
|
||||||
} elseif (is_int($name) && is_string($value)) {
|
} elseif (is_int($name) && is_string($value)) {
|
||||||
$value = ['name' => $value, 'value' => $value];
|
$value = ['name' => $value, 'value' => $value];
|
||||||
} else {
|
} else {
|
||||||
throw new InvariantViolation("{$this->name} values must be an array with value names as keys.");
|
throw new InvariantViolation(
|
||||||
|
sprintf(
|
||||||
|
'%s values must be an array with value names as keys.',
|
||||||
|
$this->name
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
$this->values[] = new EnumValueDefinition($value);
|
$this->values[] = new EnumValueDefinition($value);
|
||||||
}
|
}
|
||||||
@ -82,17 +117,7 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $name
|
* @param mixed $value
|
||||||
* @return EnumValueDefinition|null
|
|
||||||
*/
|
|
||||||
public function getValue($name)
|
|
||||||
{
|
|
||||||
$lookup = $this->getNameLookup();
|
|
||||||
return is_scalar($name) && isset($lookup[$name]) ? $lookup[$name] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $value
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws Error
|
* @throws Error
|
||||||
*/
|
*/
|
||||||
@ -103,11 +128,27 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
|||||||
return $lookup[$value]->name;
|
return $lookup[$value]->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Cannot serialize value as enum: " . Utils::printSafe($value));
|
throw new Error('Cannot serialize value as enum: ' . Utils::printSafe($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $value
|
* @return MixedStore<mixed, EnumValueDefinition>
|
||||||
|
*/
|
||||||
|
private function getValueLookup()
|
||||||
|
{
|
||||||
|
if ($this->valueLookup === null) {
|
||||||
|
$this->valueLookup = new MixedStore();
|
||||||
|
|
||||||
|
foreach ($this->getValues() as $valueName => $value) {
|
||||||
|
$this->valueLookup->offsetSet($value->value, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->valueLookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws Error
|
* @throws Error
|
||||||
*/
|
*/
|
||||||
@ -118,16 +159,16 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
|||||||
return $lookup[$value]->value;
|
return $lookup[$value]->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Cannot represent value as enum: " . Utils::printSafe($value));
|
throw new Error('Cannot represent value as enum: ' . Utils::printSafe($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $valueNode
|
* @param Node $valueNode
|
||||||
* @param array|null $variables
|
* @param mixed[]|null $variables
|
||||||
* @return null
|
* @return null
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function parseLiteral($valueNode, array $variables = null)
|
public function parseLiteral($valueNode, ?array $variables = null)
|
||||||
{
|
{
|
||||||
if ($valueNode instanceof EnumValueNode) {
|
if ($valueNode instanceof EnumValueNode) {
|
||||||
$lookup = $this->getNameLookup();
|
$lookup = $this->getNameLookup();
|
||||||
@ -143,37 +184,6 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
|||||||
throw new \Exception();
|
throw new \Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return MixedStore<mixed, EnumValueDefinition>
|
|
||||||
*/
|
|
||||||
private function getValueLookup()
|
|
||||||
{
|
|
||||||
if (null === $this->valueLookup) {
|
|
||||||
$this->valueLookup = new MixedStore();
|
|
||||||
|
|
||||||
foreach ($this->getValues() as $valueName => $value) {
|
|
||||||
$this->valueLookup->offsetSet($value->value, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->valueLookup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \ArrayObject<string, EnumValueDefinition>
|
|
||||||
*/
|
|
||||||
private function getNameLookup()
|
|
||||||
{
|
|
||||||
if (!$this->nameLookup) {
|
|
||||||
$lookup = new \ArrayObject();
|
|
||||||
foreach ($this->getValues() as $value) {
|
|
||||||
$lookup[$value->name] = $value;
|
|
||||||
}
|
|
||||||
$this->nameLookup = $lookup;
|
|
||||||
}
|
|
||||||
return $this->nameLookup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws InvariantViolation
|
* @throws InvariantViolation
|
||||||
*/
|
*/
|
||||||
@ -183,14 +193,18 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
|||||||
|
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
isset($this->config['values']),
|
isset($this->config['values']),
|
||||||
"{$this->name} values must be an array."
|
sprintf('%s values must be an array.', $this->name)
|
||||||
);
|
);
|
||||||
|
|
||||||
$values = $this->getValues();
|
$values = $this->getValues();
|
||||||
foreach ($values as $value) {
|
foreach ($values as $value) {
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
! isset($value->config['isDeprecated']),
|
! isset($value->config['isDeprecated']),
|
||||||
"{$this->name}.{$value->name} should provide \"deprecationReason\" instead of \"isDeprecated\"."
|
sprintf(
|
||||||
|
'%s.%s should provide "deprecationReason" instead of "isDeprecated".',
|
||||||
|
$this->name,
|
||||||
|
$value->name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,44 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Language\AST\EnumValueDefinitionNode;
|
use GraphQL\Language\AST\EnumValueDefinitionNode;
|
||||||
use GraphQL\Utils\Utils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class EnumValueDefinition
|
* Class EnumValueDefinition
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class EnumValueDefinition
|
class EnumValueDefinition
|
||||||
{
|
{
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
/**
|
/** @var mixed */
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
public $value;
|
public $value;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
public $deprecationReason;
|
public $deprecationReason;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
public $description;
|
public $description;
|
||||||
|
|
||||||
/**
|
/** @var EnumValueDefinitionNode|null */
|
||||||
* @var EnumValueDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
/**
|
/** @var mixed[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $config;
|
public $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $config
|
||||||
|
*/
|
||||||
public function __construct(array $config)
|
public function __construct(array $config)
|
||||||
{
|
{
|
||||||
$this->name = isset($config['name']) ? $config['name'] : null;
|
$this->name = $config['name'] ?? null;
|
||||||
$this->value = isset($config['value']) ? $config['value'] : null;
|
$this->value = $config['value'] ?? null;
|
||||||
$this->deprecationReason = isset($config['deprecationReason']) ? $config['deprecationReason'] : null;
|
$this->deprecationReason = $config['deprecationReason'] ?? null;
|
||||||
$this->description = isset($config['description']) ? $config['description'] : null;
|
$this->description = $config['description'] ?? null;
|
||||||
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;
|
$this->astNode = $config['astNode'] ?? null;
|
||||||
|
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
@ -55,6 +48,6 @@ class EnumValueDefinition
|
|||||||
*/
|
*/
|
||||||
public function isDeprecated()
|
public function isDeprecated()
|
||||||
{
|
{
|
||||||
return !!$this->deprecationReason;
|
return (bool) $this->deprecationReason;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,45 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\ArgumentNode;
|
|
||||||
use GraphQL\Language\AST\InputValueDefinitionNode;
|
use GraphQL\Language\AST\InputValueDefinitionNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_array;
|
||||||
|
use function is_string;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FieldArgument
|
* Class FieldArgument
|
||||||
*
|
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class FieldArgument
|
class FieldArgument
|
||||||
{
|
{
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
/**
|
/** @var mixed */
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
public $defaultValue;
|
public $defaultValue;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
public $description;
|
public $description;
|
||||||
|
|
||||||
/**
|
/** @var InputValueDefinitionNode|null */
|
||||||
* @var InputValueDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
/**
|
/** @var mixed[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $config;
|
public $config;
|
||||||
|
|
||||||
/**
|
/** @var InputType */
|
||||||
* @var InputType
|
|
||||||
*/
|
|
||||||
private $type;
|
private $type;
|
||||||
|
|
||||||
/**
|
/** @var bool */
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $defaultValueExists = false;
|
private $defaultValueExists = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $config
|
*
|
||||||
* @return array
|
* @param mixed[] $def
|
||||||
*/
|
|
||||||
public static function createMap(array $config)
|
|
||||||
{
|
|
||||||
$map = [];
|
|
||||||
foreach ($config as $name => $argConfig) {
|
|
||||||
if (!is_array($argConfig)) {
|
|
||||||
$argConfig = ['type' => $argConfig];
|
|
||||||
}
|
|
||||||
$map[] = new self($argConfig + ['name' => $name]);
|
|
||||||
}
|
|
||||||
return $map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FieldArgument constructor.
|
|
||||||
* @param array $def
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $def)
|
public function __construct(array $def)
|
||||||
{
|
{
|
||||||
@ -94,6 +66,23 @@ class FieldArgument
|
|||||||
$this->config = $def;
|
$this->config = $def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $config
|
||||||
|
* @return FieldArgument[]
|
||||||
|
*/
|
||||||
|
public static function createMap(array $config)
|
||||||
|
{
|
||||||
|
$map = [];
|
||||||
|
foreach ($config as $name => $argConfig) {
|
||||||
|
if (! is_array($argConfig)) {
|
||||||
|
$argConfig = ['type' => $argConfig];
|
||||||
|
}
|
||||||
|
$map[] = new self($argConfig + ['name' => $name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return InputType
|
* @return InputType
|
||||||
*/
|
*/
|
||||||
@ -116,8 +105,8 @@ class FieldArgument
|
|||||||
Utils::assertValidName($this->name);
|
Utils::assertValidName($this->name);
|
||||||
} catch (InvariantViolation $e) {
|
} catch (InvariantViolation $e) {
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"{$parentType->name}.{$parentField->name}({$this->name}:) {$e->getMessage()}")
|
sprintf('%s.%s(%s:) %s', $parentType->name, $parentField->name, $this->name, $e->getMessage())
|
||||||
;
|
);
|
||||||
}
|
}
|
||||||
$type = $this->type;
|
$type = $this->type;
|
||||||
if ($type instanceof WrappingType) {
|
if ($type instanceof WrappingType) {
|
||||||
@ -125,13 +114,23 @@ class FieldArgument
|
|||||||
}
|
}
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$type instanceof InputType,
|
$type instanceof InputType,
|
||||||
"{$parentType->name}.{$parentField->name}({$this->name}): argument type must be " .
|
sprintf(
|
||||||
"Input Type but got: " . Utils::printSafe($this->type)
|
'%s.%s(%s): argument type must be Input Type but got: %s',
|
||||||
|
$parentType->name,
|
||||||
|
$parentField->name,
|
||||||
|
$this->name,
|
||||||
|
Utils::printSafe($this->type)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$this->description === null || is_string($this->description),
|
$this->description === null || is_string($this->description),
|
||||||
"{$parentType->name}.{$parentField->name}({$this->name}): argument description type must be " .
|
sprintf(
|
||||||
"string but got: " . Utils::printSafe($this->description)
|
'%s.%s(%s): argument description type must be string but got: %s',
|
||||||
|
$parentType->name,
|
||||||
|
$parentField->name,
|
||||||
|
$this->name,
|
||||||
|
Utils::printSafe($this->description)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,29 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\FieldDefinitionNode;
|
use GraphQL\Language\AST\FieldDefinitionNode;
|
||||||
use GraphQL\Language\AST\TypeDefinitionNode;
|
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_array;
|
||||||
|
use function is_callable;
|
||||||
|
use function is_string;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FieldDefinition
|
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
* @todo Move complexity-related code to it's own place
|
* @todo Move complexity-related code to it's own place
|
||||||
*/
|
*/
|
||||||
class FieldDefinition
|
class FieldDefinition
|
||||||
{
|
{
|
||||||
const DEFAULT_COMPLEXITY_FN = 'GraphQL\Type\Definition\FieldDefinition::defaultComplexity';
|
public const DEFAULT_COMPLEXITY_FN = 'GraphQL\Type\Definition\FieldDefinition::defaultComplexity';
|
||||||
|
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
/**
|
/** @var FieldArgument[] */
|
||||||
* @var FieldArgument[]
|
|
||||||
*/
|
|
||||||
public $args;
|
public $args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,34 +42,48 @@ class FieldDefinition
|
|||||||
*/
|
*/
|
||||||
public $mapFn;
|
public $mapFn;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
public $description;
|
public $description;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
public $deprecationReason;
|
public $deprecationReason;
|
||||||
|
|
||||||
/**
|
/** @var FieldDefinitionNode|null */
|
||||||
* @var FieldDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Original field definition config
|
* Original field definition config
|
||||||
*
|
*
|
||||||
* @var array
|
* @var mixed[]
|
||||||
*/
|
*/
|
||||||
public $config;
|
public $config;
|
||||||
|
|
||||||
/**
|
/** @var OutputType */
|
||||||
* @var OutputType
|
|
||||||
*/
|
|
||||||
private $type;
|
private $type;
|
||||||
|
|
||||||
private static $def;
|
/** @var callable|string */
|
||||||
|
private $complexityFn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mixed[] $config
|
||||||
|
*/
|
||||||
|
protected function __construct(array $config)
|
||||||
|
{
|
||||||
|
$this->name = $config['name'];
|
||||||
|
$this->type = $config['type'];
|
||||||
|
$this->resolveFn = $config['resolve'] ?? null;
|
||||||
|
$this->mapFn = $config['map'] ?? null;
|
||||||
|
$this->args = isset($config['args']) ? FieldArgument::createMap($config['args']) : [];
|
||||||
|
|
||||||
|
$this->description = $config['description'] ?? null;
|
||||||
|
$this->deprecationReason = $config['deprecationReason'] ?? null;
|
||||||
|
$this->astNode = $config['astNode'] ?? null;
|
||||||
|
|
||||||
|
$this->config = $config;
|
||||||
|
|
||||||
|
$this->complexityFn = $config['complexity'] ?? static::DEFAULT_COMPLEXITY_FN;
|
||||||
|
}
|
||||||
|
|
||||||
public static function defineFieldMap(Type $type, $fields)
|
public static function defineFieldMap(Type $type, $fields)
|
||||||
{
|
{
|
||||||
@ -77,77 +92,72 @@ class FieldDefinition
|
|||||||
}
|
}
|
||||||
if (! is_array($fields)) {
|
if (! is_array($fields)) {
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"{$type->name} fields must be an array or a callable which returns such an array."
|
sprintf('%s fields must be an array or a callable which returns such an array.', $type->name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$map = [];
|
$map = [];
|
||||||
foreach ($fields as $name => $field) {
|
foreach ($fields as $name => $field) {
|
||||||
if (is_array($field)) {
|
if (is_array($field)) {
|
||||||
if (! isset($field['name'])) {
|
if (! isset($field['name'])) {
|
||||||
if (is_string($name)) {
|
if (! is_string($name)) {
|
||||||
$field['name'] = $name;
|
|
||||||
} else {
|
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"{$type->name} fields must be an associative array with field names as keys or a " .
|
sprintf(
|
||||||
"function which returns such an array."
|
'%s fields must be an associative array with field names as keys or a function which returns such an array.',
|
||||||
|
$type->name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$field['name'] = $name;
|
||||||
}
|
}
|
||||||
if (isset($field['args']) && ! is_array($field['args'])) {
|
if (isset($field['args']) && ! is_array($field['args'])) {
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"{$type->name}.{$name} args must be an array."
|
sprintf('%s.%s args must be an array.', $type->name, $name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$fieldDef = self::create($field);
|
$fieldDef = self::create($field);
|
||||||
} else if ($field instanceof FieldDefinition) {
|
} elseif ($field instanceof self) {
|
||||||
$fieldDef = $field;
|
$fieldDef = $field;
|
||||||
} else {
|
} else {
|
||||||
if (is_string($name) && $field) {
|
if (! is_string($name) || ! $field) {
|
||||||
$fieldDef = self::create(['name' => $name, 'type' => $field]);
|
|
||||||
} else {
|
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"{$type->name}.$name field config must be an array, but got: " . Utils::printSafe($field)
|
sprintf(
|
||||||
|
'%s.%s field config must be an array, but got: %s',
|
||||||
|
$type->name,
|
||||||
|
$name,
|
||||||
|
Utils::printSafe($field)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$fieldDef = self::create(['name' => $name, 'type' => $field]);
|
||||||
}
|
}
|
||||||
$map[$fieldDef->name] = $fieldDef;
|
$map[$fieldDef->name] = $fieldDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $map;
|
return $map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array|Config $field
|
* @param mixed[] $field
|
||||||
* @param string $typeName
|
|
||||||
* @return FieldDefinition
|
* @return FieldDefinition
|
||||||
*/
|
*/
|
||||||
public static function create($field, $typeName = null)
|
public static function create($field)
|
||||||
{
|
{
|
||||||
return new self($field);
|
return new self($field);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FieldDefinition constructor.
|
* @param int $childrenComplexity
|
||||||
* @param array $config
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
protected function __construct(array $config)
|
public static function defaultComplexity($childrenComplexity)
|
||||||
{
|
{
|
||||||
$this->name = $config['name'];
|
return $childrenComplexity + 1;
|
||||||
$this->type = $config['type'];
|
|
||||||
$this->resolveFn = isset($config['resolve']) ? $config['resolve'] : null;
|
|
||||||
$this->mapFn = isset($config['map']) ? $config['map'] : null;
|
|
||||||
$this->args = isset($config['args']) ? FieldArgument::createMap($config['args']) : [];
|
|
||||||
|
|
||||||
$this->description = isset($config['description']) ? $config['description'] : null;
|
|
||||||
$this->deprecationReason = isset($config['deprecationReason']) ? $config['deprecationReason'] : null;
|
|
||||||
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;
|
|
||||||
|
|
||||||
$this->config = $config;
|
|
||||||
|
|
||||||
$this->complexityFn = isset($config['complexity']) ? $config['complexity'] : static::DEFAULT_COMPLEXITY_FN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $name
|
* @param string $name
|
||||||
* @return FieldArgument|null
|
* @return FieldArgument|null
|
||||||
*/
|
*/
|
||||||
public function getArg($name)
|
public function getArg($name)
|
||||||
@ -158,6 +168,7 @@ class FieldDefinition
|
|||||||
return $arg;
|
return $arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +185,7 @@ class FieldDefinition
|
|||||||
*/
|
*/
|
||||||
public function isDeprecated()
|
public function isDeprecated()
|
||||||
{
|
{
|
||||||
return !!$this->deprecationReason;
|
return (bool) $this->deprecationReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,7 +197,6 @@ class FieldDefinition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Type $parentType
|
|
||||||
* @throws InvariantViolation
|
* @throws InvariantViolation
|
||||||
*/
|
*/
|
||||||
public function assertValid(Type $parentType)
|
public function assertValid(Type $parentType)
|
||||||
@ -194,11 +204,15 @@ class FieldDefinition
|
|||||||
try {
|
try {
|
||||||
Utils::assertValidName($this->name);
|
Utils::assertValidName($this->name);
|
||||||
} catch (Error $e) {
|
} catch (Error $e) {
|
||||||
throw new InvariantViolation("{$parentType->name}.{$this->name}: {$e->getMessage()}");
|
throw new InvariantViolation(sprintf('%s.%s: %s', $parentType->name, $this->name, $e->getMessage()));
|
||||||
}
|
}
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
! isset($this->config['isDeprecated']),
|
! isset($this->config['isDeprecated']),
|
||||||
"{$parentType->name}.{$this->name} should provide \"deprecationReason\" instead of \"isDeprecated\"."
|
sprintf(
|
||||||
|
'%s.%s should provide "deprecationReason" instead of "isDeprecated".',
|
||||||
|
$parentType->name,
|
||||||
|
$this->name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$type = $this->type;
|
$type = $this->type;
|
||||||
@ -207,21 +221,21 @@ class FieldDefinition
|
|||||||
}
|
}
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$type instanceof OutputType,
|
$type instanceof OutputType,
|
||||||
"{$parentType->name}.{$this->name} field type must be Output Type but got: " . Utils::printSafe($this->type)
|
sprintf(
|
||||||
|
'%s.%s field type must be Output Type but got: %s',
|
||||||
|
$parentType->name,
|
||||||
|
$this->name,
|
||||||
|
Utils::printSafe($this->type)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$this->resolveFn === null || is_callable($this->resolveFn),
|
$this->resolveFn === null || is_callable($this->resolveFn),
|
||||||
"{$parentType->name}.{$this->name} field resolver must be a function if provided, but got: %s",
|
sprintf(
|
||||||
|
'%s.%s field resolver must be a function if provided, but got: %s',
|
||||||
|
$parentType->name,
|
||||||
|
$this->name,
|
||||||
Utils::printSafe($this->resolveFn)
|
Utils::printSafe($this->resolveFn)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $childrenComplexity
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public static function defaultComplexity($childrenComplexity)
|
|
||||||
{
|
|
||||||
return $childrenComplexity + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Language\AST\FloatValueNode;
|
use GraphQL\Language\AST\FloatValueNode;
|
||||||
use GraphQL\Language\AST\IntValueNode;
|
use GraphQL\Language\AST\IntValueNode;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_numeric;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FloatType
|
* Class FloatType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class FloatType extends ScalarType
|
class FloatType extends ScalarType
|
||||||
{
|
{
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name = Type::FLOAT;
|
public $name = Type::FLOAT;
|
||||||
|
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $description =
|
public $description =
|
||||||
'The `Float` scalar type represents signed double-precision fractional
|
'The `Float` scalar type represents signed double-precision fractional
|
||||||
values as specified by
|
values as specified by
|
||||||
@ -35,33 +35,8 @@ values as specified by
|
|||||||
return $this->coerceFloat($value);
|
return $this->coerceFloat($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function coerceFloat($value)
|
||||||
* @param mixed $value
|
|
||||||
* @return float|null
|
|
||||||
* @throws Error
|
|
||||||
*/
|
|
||||||
public function parseValue($value)
|
|
||||||
{
|
{
|
||||||
return $this->coerceFloat($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $valueNode
|
|
||||||
* @param array|null $variables
|
|
||||||
* @return float|null
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function parseLiteral($valueNode, array $variables = null)
|
|
||||||
{
|
|
||||||
if ($valueNode instanceof FloatValueNode || $valueNode instanceof IntValueNode) {
|
|
||||||
return (float) $valueNode->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally without message, as all information already in wrapped Exception
|
|
||||||
throw new \Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function coerceFloat($value) {
|
|
||||||
if ($value === '') {
|
if ($value === '') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Float cannot represent non numeric value: (empty string)'
|
'Float cannot represent non numeric value: (empty string)'
|
||||||
@ -77,4 +52,30 @@ values as specified by
|
|||||||
|
|
||||||
return (float) $value;
|
return (float) $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
* @return float|null
|
||||||
|
* @throws Error
|
||||||
|
*/
|
||||||
|
public function parseValue($value)
|
||||||
|
{
|
||||||
|
return $this->coerceFloat($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Node $valueNode
|
||||||
|
* @param mixed[]|null $variables
|
||||||
|
* @return float|null
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function parseLiteral($valueNode, ?array $variables = null)
|
||||||
|
{
|
||||||
|
if ($valueNode instanceof FloatValueNode || $valueNode instanceof IntValueNode) {
|
||||||
|
return (float) $valueNode->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intentionally without message, as all information already in wrapped Exception
|
||||||
|
throw new \Exception();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
@ -6,21 +9,18 @@ use GraphQL\Language\AST\IntValueNode;
|
|||||||
use GraphQL\Language\AST\Node;
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Language\AST\StringValueNode;
|
use GraphQL\Language\AST\StringValueNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_int;
|
||||||
|
use function is_object;
|
||||||
|
use function is_scalar;
|
||||||
|
use function is_string;
|
||||||
|
use function method_exists;
|
||||||
|
|
||||||
/**
|
|
||||||
* Class IDType
|
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
|
||||||
class IDType extends ScalarType
|
class IDType extends ScalarType
|
||||||
{
|
{
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name = 'ID';
|
public $name = 'ID';
|
||||||
|
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $description =
|
public $description =
|
||||||
'The `ID` scalar type represents a unique identifier, often used to
|
'The `ID` scalar type represents a unique identifier, often used to
|
||||||
refetch an object or as key for a cache. The ID type appears in a JSON
|
refetch an object or as key for a cache. The ID type appears in a JSON
|
||||||
@ -45,8 +45,9 @@ When expected as an input type, any string (such as `"4"`) or integer
|
|||||||
return 'null';
|
return 'null';
|
||||||
}
|
}
|
||||||
if (! is_scalar($value) && (! is_object($value) || ! method_exists($value, '__toString'))) {
|
if (! is_scalar($value) && (! is_object($value) || ! method_exists($value, '__toString'))) {
|
||||||
throw new Error("ID type cannot represent non scalar value: " . Utils::printSafe($value));
|
throw new Error('ID type cannot represent non scalar value: ' . Utils::printSafe($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (string) $value;
|
return (string) $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,16 +62,16 @@ When expected as an input type, any string (such as `"4"`) or integer
|
|||||||
return (string) $value;
|
return (string) $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Cannot represent value as ID: " . Utils::printSafe($value));
|
throw new Error('Cannot represent value as ID: ' . Utils::printSafe($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Node $valueNode
|
* @param Node $valueNode
|
||||||
* @param array|null $variables
|
* @param mixed[]|null $variables
|
||||||
* @return null|string
|
* @return null|string
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function parseLiteral($valueNode, array $variables = null)
|
public function parseLiteral($valueNode, ?array $variables = null)
|
||||||
{
|
{
|
||||||
if ($valueNode instanceof StringValueNode || $valueNode instanceof IntValueNode) {
|
if ($valueNode instanceof StringValueNode || $valueNode instanceof IntValueNode) {
|
||||||
return $valueNode->value;
|
return $valueNode->value;
|
||||||
|
@ -1,44 +1,33 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\InputValueDefinitionNode;
|
use GraphQL\Language\AST\InputValueDefinitionNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InputObjectField
|
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
|
||||||
class InputObjectField
|
class InputObjectField
|
||||||
{
|
{
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
/**
|
/** @var mixed|null */
|
||||||
* @var mixed|null
|
|
||||||
*/
|
|
||||||
public $defaultValue;
|
public $defaultValue;
|
||||||
|
|
||||||
/**
|
/** @var string|null */
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
public $description;
|
public $description;
|
||||||
|
|
||||||
/**
|
/** @var callback|InputType */
|
||||||
* @var callback|InputType
|
|
||||||
*/
|
|
||||||
public $type;
|
public $type;
|
||||||
|
|
||||||
/**
|
/** @var InputValueDefinitionNode|null */
|
||||||
* @var InputValueDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
/**
|
/** @var mixed[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $config;
|
public $config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,8 +38,8 @@ class InputObjectField
|
|||||||
private $defaultValueExists = false;
|
private $defaultValueExists = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InputObjectField constructor.
|
*
|
||||||
* @param array $opts
|
* @param mixed[] $opts
|
||||||
*/
|
*/
|
||||||
public function __construct(array $opts)
|
public function __construct(array $opts)
|
||||||
{
|
{
|
||||||
@ -86,7 +75,6 @@ class InputObjectField
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Type $parentType
|
|
||||||
* @throws InvariantViolation
|
* @throws InvariantViolation
|
||||||
*/
|
*/
|
||||||
public function assertValid(Type $parentType)
|
public function assertValid(Type $parentType)
|
||||||
@ -94,7 +82,7 @@ class InputObjectField
|
|||||||
try {
|
try {
|
||||||
Utils::assertValidName($this->name);
|
Utils::assertValidName($this->name);
|
||||||
} catch (Error $e) {
|
} catch (Error $e) {
|
||||||
throw new InvariantViolation("{$parentType->name}.{$this->name}: {$e->getMessage()}");
|
throw new InvariantViolation(sprintf('%s.%s: %s', $parentType->name, $this->name, $e->getMessage()));
|
||||||
}
|
}
|
||||||
$type = $this->type;
|
$type = $this->type;
|
||||||
if ($type instanceof WrappingType) {
|
if ($type instanceof WrappingType) {
|
||||||
@ -102,12 +90,20 @@ class InputObjectField
|
|||||||
}
|
}
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$type instanceof InputType,
|
$type instanceof InputType,
|
||||||
"{$parentType->name}.{$this->name} field type must be Input Type but got: " . Utils::printSafe($this->type)
|
sprintf(
|
||||||
|
'%s.%s field type must be Input Type but got: %s',
|
||||||
|
$parentType->name,
|
||||||
|
$this->name,
|
||||||
|
Utils::printSafe($this->type)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
empty($this->config['resolve']),
|
empty($this->config['resolve']),
|
||||||
"{$parentType->name}.{$this->name} field type has a resolve property, " .
|
sprintf(
|
||||||
'but Input Types cannot define resolvers.'
|
'%s.%s field type has a resolve property, but Input Types cannot define resolvers.',
|
||||||
|
$parentType->name,
|
||||||
|
$this->name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,33 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
|
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function call_user_func;
|
||||||
|
use function is_array;
|
||||||
|
use function is_callable;
|
||||||
|
use function is_string;
|
||||||
|
use function sprintf;
|
||||||
|
use function spritnf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class InputObjectType
|
* Class InputObjectType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class InputObjectType extends Type implements InputType, NamedType
|
class InputObjectType extends Type implements InputType, NamedType
|
||||||
{
|
{
|
||||||
/**
|
/** @var InputObjectTypeDefinitionNode|null */
|
||||||
* @var InputObjectField[]
|
public $astNode;
|
||||||
*/
|
|
||||||
|
/** @var InputObjectField[] */
|
||||||
private $fields;
|
private $fields;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var InputObjectTypeDefinitionNode|null
|
*
|
||||||
*/
|
* @param mixed[] $config
|
||||||
public $astNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* InputObjectType constructor.
|
|
||||||
* @param array $config
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $config)
|
public function __construct(array $config)
|
||||||
{
|
{
|
||||||
@ -35,8 +39,23 @@ class InputObjectType extends Type implements InputType, NamedType
|
|||||||
|
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->name = $config['name'];
|
$this->name = $config['name'];
|
||||||
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;
|
$this->astNode = $config['astNode'] ?? null;
|
||||||
$this->description = isset($config['description']) ? $config['description'] : null;
|
$this->description = $config['description'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return InputObjectField
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function getField($name)
|
||||||
|
{
|
||||||
|
if ($this->fields === null) {
|
||||||
|
$this->getFields();
|
||||||
|
}
|
||||||
|
Utils::invariant(isset($this->fields[$name]), "Field '%s' is not defined for type '%s'", $name, $this->name);
|
||||||
|
|
||||||
|
return $this->fields[$name];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,14 +63,14 @@ class InputObjectType extends Type implements InputType, NamedType
|
|||||||
*/
|
*/
|
||||||
public function getFields()
|
public function getFields()
|
||||||
{
|
{
|
||||||
if (null === $this->fields) {
|
if ($this->fields === null) {
|
||||||
$this->fields = [];
|
$this->fields = [];
|
||||||
$fields = isset($this->config['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(
|
||||||
"{$this->name} fields must be an array or a callable which returns such an array."
|
spritnf('%s fields must be an array or a callable which returns such an array.', $this->name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,20 +86,6 @@ class InputObjectType extends Type implements InputType, NamedType
|
|||||||
return $this->fields;
|
return $this->fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
* @return InputObjectField
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function getField($name)
|
|
||||||
{
|
|
||||||
if (null === $this->fields) {
|
|
||||||
$this->getFields();
|
|
||||||
}
|
|
||||||
Utils::invariant(isset($this->fields[$name]), "Field '%s' is not defined for type '%s'", $name, $this->name);
|
|
||||||
return $this->fields[$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates type config and throws if one of type options is invalid.
|
* Validates type config and throws if one of type options is invalid.
|
||||||
* Note: this method is shallow, it won't validate object fields and their arguments.
|
* Note: this method is shallow, it won't validate object fields and their arguments.
|
||||||
@ -93,8 +98,10 @@ class InputObjectType extends Type implements InputType, NamedType
|
|||||||
|
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
! empty($this->getFields()),
|
! empty($this->getFields()),
|
||||||
"{$this->name} fields must be an associative array with field names as keys or a " .
|
sprintf(
|
||||||
"callable which returns such an array."
|
'%s fields must be an associative array with field names as keys or a callable which returns such an array.',
|
||||||
|
$this->name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($this->getFields() as $field) {
|
foreach ($this->getFields() as $field) {
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -14,6 +17,7 @@ export type GraphQLInputType =
|
|||||||
| GraphQLList<GraphQLInputType>,
|
| GraphQLList<GraphQLInputType>,
|
||||||
>;
|
>;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface InputType
|
interface InputType
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Language\AST\IntValueNode;
|
use GraphQL\Language\AST\IntValueNode;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function floatval;
|
||||||
|
use function intval;
|
||||||
|
use function is_bool;
|
||||||
|
use function is_numeric;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class IntType
|
* Class IntType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class IntType extends ScalarType
|
class IntType extends ScalarType
|
||||||
{
|
{
|
||||||
@ -16,17 +23,13 @@ class IntType extends ScalarType
|
|||||||
//
|
//
|
||||||
// n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
|
// n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
|
||||||
// they are internally represented as IEEE 754 doubles.
|
// they are internally represented as IEEE 754 doubles.
|
||||||
const MAX_INT = 2147483647;
|
private const MAX_INT = 2147483647;
|
||||||
const MIN_INT = -2147483648;
|
private const MIN_INT = -2147483648;
|
||||||
|
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name = Type::INT;
|
public $name = Type::INT;
|
||||||
|
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $description =
|
public $description =
|
||||||
'The `Int` scalar type represents non-fractional signed whole numeric
|
'The `Int` scalar type represents non-fractional signed whole numeric
|
||||||
values. Int can represent values between -(2^31) and 2^31 - 1. ';
|
values. Int can represent values between -(2^31) and 2^31 - 1. ';
|
||||||
@ -41,6 +44,38 @@ values. Int can represent values between -(2^31) and 2^31 - 1. ';
|
|||||||
return $this->coerceInt($value);
|
return $this->coerceInt($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function coerceInt($value)
|
||||||
|
{
|
||||||
|
if ($value === '') {
|
||||||
|
throw new Error(
|
||||||
|
'Int cannot represent non 32-bit signed integer value: (empty string)'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$num = floatval($value);
|
||||||
|
if ((! is_numeric($value) && ! is_bool($value)) || $num > self::MAX_INT || $num < self::MIN_INT) {
|
||||||
|
throw new Error(
|
||||||
|
'Int cannot represent non 32-bit signed integer value: ' .
|
||||||
|
Utils::printSafe($value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$int = intval($num);
|
||||||
|
// int cast with == used for performance reasons
|
||||||
|
// @codingStandardsIgnoreLine
|
||||||
|
if ($int != $num) {
|
||||||
|
throw new Error(
|
||||||
|
'Int cannot represent non-integer value: ' .
|
||||||
|
Utils::printSafe($value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $int;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
* @return int|null
|
* @return int|null
|
||||||
@ -52,12 +87,12 @@ values. Int can represent values between -(2^31) and 2^31 - 1. ';
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $valueNode
|
* @param Node $valueNode
|
||||||
* @param array|null $variables
|
* @param mixed[]|null $variables
|
||||||
* @return int|null
|
* @return int|null
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function parseLiteral($valueNode, array $variables = null)
|
public function parseLiteral($valueNode, ?array $variables = null)
|
||||||
{
|
{
|
||||||
if ($valueNode instanceof IntValueNode) {
|
if ($valueNode instanceof IntValueNode) {
|
||||||
$val = (int) $valueNode->value;
|
$val = (int) $valueNode->value;
|
||||||
@ -69,28 +104,4 @@ values. Int can represent values between -(2^31) and 2^31 - 1. ';
|
|||||||
// Intentionally without message, as all information already in wrapped Exception
|
// Intentionally without message, as all information already in wrapped Exception
|
||||||
throw new \Exception();
|
throw new \Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function coerceInt($value) {
|
|
||||||
if ($value === '') {
|
|
||||||
throw new Error(
|
|
||||||
'Int cannot represent non 32-bit signed integer value: (empty string)'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$num = floatval($value);
|
|
||||||
if (!is_numeric($value) && !is_bool($value) || $num > self::MAX_INT || $num < self::MIN_INT) {
|
|
||||||
throw new Error(
|
|
||||||
'Int cannot represent non 32-bit signed integer value: ' .
|
|
||||||
Utils::printSafe($value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$int = intval($num);
|
|
||||||
if ($int != $num) {
|
|
||||||
throw new Error(
|
|
||||||
'Int cannot represent non-integer value: ' .
|
|
||||||
Utils::printSafe($value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return $int;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,50 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
|
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
|
||||||
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
|
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_callable;
|
||||||
|
use function is_string;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class InterfaceType
|
* Class InterfaceType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class InterfaceType extends Type implements AbstractType, OutputType, CompositeType, NamedType
|
class InterfaceType extends Type implements AbstractType, OutputType, CompositeType, NamedType
|
||||||
{
|
{
|
||||||
|
/** @var InterfaceTypeDefinitionNode|null */
|
||||||
|
public $astNode;
|
||||||
|
|
||||||
|
/** @var InterfaceTypeExtensionNode[] */
|
||||||
|
public $extensionASTNodes;
|
||||||
|
|
||||||
|
/** @var FieldDefinition[] */
|
||||||
|
private $fields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mixed[] $config
|
||||||
|
*/
|
||||||
|
public function __construct(array $config)
|
||||||
|
{
|
||||||
|
if (! isset($config['name'])) {
|
||||||
|
$config['name'] = $this->tryInferName();
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::invariant(is_string($config['name']), 'Must provide name.');
|
||||||
|
|
||||||
|
$this->name = $config['name'];
|
||||||
|
$this->description = $config['description'] ?? null;
|
||||||
|
$this->astNode = $config['astNode'] ?? null;
|
||||||
|
$this->extensionASTNodes = $config['extensionASTNodes'] ?? null;
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $type
|
* @param mixed $type
|
||||||
* @return self
|
* @return self
|
||||||
@ -27,37 +60,17 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var FieldDefinition[]
|
* @param string $name
|
||||||
|
* @return FieldDefinition
|
||||||
*/
|
*/
|
||||||
private $fields;
|
public function getField($name)
|
||||||
|
|
||||||
/**
|
|
||||||
* @var InterfaceTypeDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var InterfaceTypeExtensionNode[]
|
|
||||||
*/
|
|
||||||
public $extensionASTNodes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* InterfaceType constructor.
|
|
||||||
* @param array $config
|
|
||||||
*/
|
|
||||||
public function __construct(array $config)
|
|
||||||
{
|
{
|
||||||
if (!isset($config['name'])) {
|
if ($this->fields === null) {
|
||||||
$config['name'] = $this->tryInferName();
|
$this->getFields();
|
||||||
}
|
}
|
||||||
|
Utils::invariant(isset($this->fields[$name]), 'Field "%s" is not defined for type "%s"', $name, $this->name);
|
||||||
|
|
||||||
Utils::invariant(is_string($config['name']), 'Must provide name.');
|
return $this->fields[$name];
|
||||||
|
|
||||||
$this->name = $config['name'];
|
|
||||||
$this->description = isset($config['description']) ? $config['description'] : null;
|
|
||||||
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;
|
|
||||||
$this->extensionASTNodes = isset($config['extensionASTNodes']) ? $config['extensionASTNodes'] : null;
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,41 +78,29 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
|
|||||||
*/
|
*/
|
||||||
public function getFields()
|
public function getFields()
|
||||||
{
|
{
|
||||||
if (null === $this->fields) {
|
if ($this->fields === null) {
|
||||||
$fields = isset($this->config['fields']) ? $this->config['fields'] : [];
|
$fields = $this->config['fields'] ?? [];
|
||||||
$this->fields = FieldDefinition::defineFieldMap($this, $fields);
|
$this->fields = FieldDefinition::defineFieldMap($this, $fields);
|
||||||
}
|
}
|
||||||
return $this->fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return $this->fields;
|
||||||
* @param $name
|
|
||||||
* @return FieldDefinition
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function getField($name)
|
|
||||||
{
|
|
||||||
if (null === $this->fields) {
|
|
||||||
$this->getFields();
|
|
||||||
}
|
|
||||||
Utils::invariant(isset($this->fields[$name]), 'Field "%s" is not defined for type "%s"', $name, $this->name);
|
|
||||||
return $this->fields[$name];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves concrete ObjectType for given object value
|
* Resolves concrete ObjectType for given object value
|
||||||
*
|
*
|
||||||
* @param $objectValue
|
* @param object $objectValue
|
||||||
* @param $context
|
* @param mixed[] $context
|
||||||
* @param ResolveInfo $info
|
|
||||||
* @return callable|null
|
* @return callable|null
|
||||||
*/
|
*/
|
||||||
public function resolveType($objectValue, $context, ResolveInfo $info)
|
public function resolveType($objectValue, $context, ResolveInfo $info)
|
||||||
{
|
{
|
||||||
if (isset($this->config['resolveType'])) {
|
if (isset($this->config['resolveType'])) {
|
||||||
$fn = $this->config['resolveType'];
|
$fn = $this->config['resolveType'];
|
||||||
|
|
||||||
return $fn($objectValue, $context, $info);
|
return $fn($objectValue, $context, $info);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +115,11 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
|
|||||||
|
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
! isset($resolveType) || is_callable($resolveType),
|
! isset($resolveType) || is_callable($resolveType),
|
||||||
"{$this->name} must provide \"resolveType\" as a function, but got: " . Utils::printSafe($resolveType)
|
sprintf(
|
||||||
|
'%s must provide "resolveType" as a function, but got: %s',
|
||||||
|
$this->name,
|
||||||
|
Utils::printSafe($resolveType)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use \GraphQL\Language\AST\Node;
|
use GraphQL\Language\AST\Node;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
export type GraphQLLeafType =
|
export type GraphQLLeafType =
|
||||||
GraphQLScalarType |
|
GraphQLScalarType |
|
||||||
GraphQLEnumType;
|
GraphQLEnumType;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface LeafType
|
interface LeafType
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -37,9 +41,9 @@ interface LeafType
|
|||||||
* In the case of an invalid node or value this method must throw an Exception
|
* In the case of an invalid node or value this method must throw an Exception
|
||||||
*
|
*
|
||||||
* @param Node $valueNode
|
* @param Node $valueNode
|
||||||
* @param array|null $variables
|
* @param mixed[]|null $variables
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function parseLiteral($valueNode, array $variables = null);
|
public function parseLiteral($valueNode, ?array $variables = null);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Type\Definition;
|
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
declare(strict_types=1);
|
||||||
use GraphQL\Utils\Utils;
|
|
||||||
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ListOfType
|
* Class ListOfType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class ListOfType extends Type implements WrappingType, OutputType, InputType
|
class ListOfType extends Type implements WrappingType, OutputType, InputType
|
||||||
{
|
{
|
||||||
/**
|
/** @var ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType */
|
||||||
* @var ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType
|
|
||||||
*/
|
|
||||||
public $ofType;
|
public $ofType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,6 +27,7 @@ class ListOfType extends Type implements WrappingType, OutputType, InputType
|
|||||||
{
|
{
|
||||||
$type = $this->ofType;
|
$type = $this->ofType;
|
||||||
$str = $type instanceof Type ? $type->toString() : (string) $type;
|
$str = $type instanceof Type ? $type->toString() : (string) $type;
|
||||||
|
|
||||||
return '[' . $str . ']';
|
return '[' . $str . ']';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +38,7 @@ class ListOfType extends Type implements WrappingType, OutputType, InputType
|
|||||||
public function getWrappedType($recurse = false)
|
public function getWrappedType($recurse = false)
|
||||||
{
|
{
|
||||||
$type = $this->ofType;
|
$type = $this->ofType;
|
||||||
|
|
||||||
return ($recurse && $type instanceof WrappingType) ? $type->getWrappedType($recurse) : $type;
|
return ($recurse && $type instanceof WrappingType) ? $type->getWrappedType($recurse) : $type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -10,6 +13,7 @@ export type GraphQLNamedType =
|
|||||||
| GraphQLEnumType
|
| GraphQLEnumType
|
||||||
| GraphQLInputObjectType;
|
| GraphQLInputObjectType;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface NamedType
|
interface NamedType
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
@ -6,22 +9,19 @@ use GraphQL\Utils\Utils;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class NonNull
|
* Class NonNull
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class NonNull extends Type implements WrappingType, OutputType, InputType
|
class NonNull extends Type implements WrappingType, OutputType, InputType
|
||||||
{
|
{
|
||||||
/**
|
/** @var ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType */
|
||||||
* @param mixed $type
|
private $ofType;
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public static function assertNullType($type)
|
|
||||||
{
|
|
||||||
Utils::invariant(
|
|
||||||
$type instanceof self,
|
|
||||||
'Expected ' . Utils::printSafe($type) . ' to be a GraphQL Non-Null type.'
|
|
||||||
);
|
|
||||||
|
|
||||||
return $type;
|
/**
|
||||||
|
* @param callable|Type $type
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function __construct($type)
|
||||||
|
{
|
||||||
|
$this->ofType = self::assertNullableType($type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,17 +39,25 @@ class NonNull extends Type implements WrappingType, OutputType, InputType
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType|ListOfType
|
* @param mixed $type
|
||||||
|
* @return self
|
||||||
*/
|
*/
|
||||||
private $ofType;
|
public static function assertNullType($type)
|
||||||
|
{
|
||||||
|
Utils::invariant(
|
||||||
|
$type instanceof self,
|
||||||
|
'Expected ' . Utils::printSafe($type) . ' to be a GraphQL Non-Null type.'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callable|Type $type
|
* @return string
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function __construct($type)
|
public function toString()
|
||||||
{
|
{
|
||||||
$this->ofType = self::assertNullableType($type);
|
return $this->getWrappedType()->toString() . '!';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,14 +68,7 @@ class NonNull extends Type implements WrappingType, OutputType, InputType
|
|||||||
public function getWrappedType($recurse = false)
|
public function getWrappedType($recurse = false)
|
||||||
{
|
{
|
||||||
$type = $this->ofType;
|
$type = $this->ofType;
|
||||||
|
|
||||||
return ($recurse && $type instanceof WrappingType) ? $type->getWrappedType($recurse) : $type;
|
return ($recurse && $type instanceof WrappingType) ? $type->getWrappedType($recurse) : $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function toString()
|
|
||||||
{
|
|
||||||
return $this->getWrappedType()->toString() . '!';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
|
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
|
||||||
use GraphQL\Language\AST\ObjectTypeExtensionNode;
|
use GraphQL\Language\AST\ObjectTypeExtensionNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function call_user_func;
|
||||||
|
use function is_array;
|
||||||
|
use function is_callable;
|
||||||
|
use function is_string;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object Type Definition
|
* Object Type Definition
|
||||||
@ -49,6 +56,44 @@ use GraphQL\Utils\Utils;
|
|||||||
*/
|
*/
|
||||||
class ObjectType extends Type implements OutputType, CompositeType, NamedType
|
class ObjectType extends Type implements OutputType, CompositeType, NamedType
|
||||||
{
|
{
|
||||||
|
/** @var ObjectTypeDefinitionNode|null */
|
||||||
|
public $astNode;
|
||||||
|
|
||||||
|
/** @var ObjectTypeExtensionNode[] */
|
||||||
|
public $extensionASTNodes;
|
||||||
|
|
||||||
|
/** @var callable */
|
||||||
|
public $resolveFieldFn;
|
||||||
|
|
||||||
|
/** @var FieldDefinition[] */
|
||||||
|
private $fields;
|
||||||
|
|
||||||
|
/** @var InterfaceType[] */
|
||||||
|
private $interfaces;
|
||||||
|
|
||||||
|
/** @var InterfaceType[]|null */
|
||||||
|
private $interfaceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mixed[] $config
|
||||||
|
*/
|
||||||
|
public function __construct(array $config)
|
||||||
|
{
|
||||||
|
if (! isset($config['name'])) {
|
||||||
|
$config['name'] = $this->tryInferName();
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::invariant(is_string($config['name']), 'Must provide name.');
|
||||||
|
|
||||||
|
$this->name = $config['name'];
|
||||||
|
$this->description = $config['description'] ?? null;
|
||||||
|
$this->resolveFieldFn = $config['resolveField'] ?? null;
|
||||||
|
$this->astNode = $config['astNode'] ?? null;
|
||||||
|
$this->extensionASTNodes = $config['extensionASTNodes'] ?? [];
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $type
|
* @param mixed $type
|
||||||
* @return self
|
* @return self
|
||||||
@ -64,53 +109,18 @@ class ObjectType extends Type implements OutputType, CompositeType, NamedType
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var FieldDefinition[]
|
* @param string $name
|
||||||
|
* @return FieldDefinition
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
private $fields;
|
public function getField($name)
|
||||||
|
|
||||||
/**
|
|
||||||
* @var InterfaceType[]
|
|
||||||
*/
|
|
||||||
private $interfaces;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $interfaceMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ObjectTypeDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ObjectTypeExtensionNode[]
|
|
||||||
*/
|
|
||||||
public $extensionASTNodes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $resolveFieldFn;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ObjectType constructor.
|
|
||||||
* @param array $config
|
|
||||||
*/
|
|
||||||
public function __construct(array $config)
|
|
||||||
{
|
{
|
||||||
if (!isset($config['name'])) {
|
if ($this->fields === null) {
|
||||||
$config['name'] = $this->tryInferName();
|
$this->getFields();
|
||||||
}
|
}
|
||||||
|
Utils::invariant(isset($this->fields[$name]), 'Field "%s" is not defined for type "%s"', $name, $this->name);
|
||||||
|
|
||||||
Utils::invariant(is_string($config['name']), 'Must provide name.');
|
return $this->fields[$name];
|
||||||
|
|
||||||
$this->name = $config['name'];
|
|
||||||
$this->description = isset($config['description']) ? $config['description'] : null;
|
|
||||||
$this->resolveFieldFn = isset($config['resolveField']) ? $config['resolveField'] : null;
|
|
||||||
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;
|
|
||||||
$this->extensionASTNodes = isset($config['extensionASTNodes']) ? $config['extensionASTNodes'] : [];
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,45 +129,23 @@ class ObjectType extends Type implements OutputType, CompositeType, NamedType
|
|||||||
*/
|
*/
|
||||||
public function getFields()
|
public function getFields()
|
||||||
{
|
{
|
||||||
if (null === $this->fields) {
|
if ($this->fields === null) {
|
||||||
$fields = isset($this->config['fields']) ? $this->config['fields'] : [];
|
$fields = $this->config['fields'] ?? [];
|
||||||
$this->fields = FieldDefinition::defineFieldMap($this, $fields);
|
$this->fields = FieldDefinition::defineFieldMap($this, $fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->fields;
|
return $this->fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $name
|
* @param InterfaceType $iface
|
||||||
* @return FieldDefinition
|
* @return bool
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function getField($name)
|
public function implementsInterface($iface)
|
||||||
{
|
{
|
||||||
if (null === $this->fields) {
|
$map = $this->getInterfaceMap();
|
||||||
$this->getFields();
|
|
||||||
}
|
|
||||||
Utils::invariant(isset($this->fields[$name]), 'Field "%s" is not defined for type "%s"', $name, $this->name);
|
|
||||||
return $this->fields[$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return isset($map[$iface->name]);
|
||||||
* @return InterfaceType[]
|
|
||||||
*/
|
|
||||||
public function getInterfaces()
|
|
||||||
{
|
|
||||||
if (null === $this->interfaces) {
|
|
||||||
$interfaces = isset($this->config['interfaces']) ? $this->config['interfaces'] : [];
|
|
||||||
$interfaces = is_callable($interfaces) ? call_user_func($interfaces) : $interfaces;
|
|
||||||
|
|
||||||
if ($interfaces && !is_array($interfaces)) {
|
|
||||||
throw new InvariantViolation(
|
|
||||||
"{$this->name} interfaces must be an Array or a callable which returns an Array."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->interfaces = $interfaces ?: [];
|
|
||||||
}
|
|
||||||
return $this->interfaces;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getInterfaceMap()
|
private function getInterfaceMap()
|
||||||
@ -168,28 +156,44 @@ class ObjectType extends Type implements OutputType, CompositeType, NamedType
|
|||||||
$this->interfaceMap[$interface->name] = $interface;
|
$this->interfaceMap[$interface->name] = $interface;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->interfaceMap;
|
return $this->interfaceMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param InterfaceType $iface
|
* @return InterfaceType[]
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function implementsInterface($iface)
|
public function getInterfaces()
|
||||||
{
|
{
|
||||||
$map = $this->getInterfaceMap();
|
if ($this->interfaces === null) {
|
||||||
return isset($map[$iface->name]);
|
$interfaces = $this->config['interfaces'] ?? [];
|
||||||
|
$interfaces = is_callable($interfaces) ? call_user_func($interfaces) : $interfaces;
|
||||||
|
|
||||||
|
if ($interfaces && ! is_array($interfaces)) {
|
||||||
|
throw new InvariantViolation(
|
||||||
|
sprintf('%s interfaces must be an Array or a callable which returns an Array.', $this->name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->interfaces = $interfaces ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->interfaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $value
|
* @param mixed[] $value
|
||||||
* @param $context
|
* @param mixed[]|null $context
|
||||||
* @param ResolveInfo $info
|
|
||||||
* @return bool|null
|
* @return bool|null
|
||||||
*/
|
*/
|
||||||
public function isTypeOf($value, $context, ResolveInfo $info)
|
public function isTypeOf($value, $context, ResolveInfo $info)
|
||||||
{
|
{
|
||||||
return isset($this->config['isTypeOf']) ? call_user_func($this->config['isTypeOf'], $value, $context, $info) : null;
|
return isset($this->config['isTypeOf']) ? call_user_func(
|
||||||
|
$this->config['isTypeOf'],
|
||||||
|
$value,
|
||||||
|
$context,
|
||||||
|
$info
|
||||||
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,15 +207,19 @@ class ObjectType extends Type implements OutputType, CompositeType, NamedType
|
|||||||
parent::assertValid();
|
parent::assertValid();
|
||||||
|
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
null === $this->description || is_string($this->description),
|
$this->description === null || is_string($this->description),
|
||||||
"{$this->name} description must be string if set, but it is: " . Utils::printSafe($this->description)
|
sprintf(
|
||||||
|
'%s description must be string if set, but it is: %s',
|
||||||
|
$this->name,
|
||||||
|
Utils::printSafe($this->description)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$isTypeOf = $this->config['isTypeOf'] ?? null;
|
$isTypeOf = $this->config['isTypeOf'] ?? null;
|
||||||
|
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
!isset($isTypeOf) || is_callable($isTypeOf),
|
$isTypeOf === null || is_callable($isTypeOf),
|
||||||
"{$this->name} must provide \"isTypeOf\" as a function, but got: " . Utils::printSafe($isTypeOf)
|
sprintf('%s must provide "isTypeOf" as a function, but got: %s', $this->name, Utils::printSafe($isTypeOf))
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($this->getFields() as $field) {
|
foreach ($this->getFields() as $field) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Type\Definition;
|
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GraphQLScalarType |
|
GraphQLScalarType |
|
||||||
@ -11,6 +13,7 @@ GraphQLEnumType |
|
|||||||
GraphQLList |
|
GraphQLList |
|
||||||
GraphQLNonNull;
|
GraphQLNonNull;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface OutputType
|
interface OutputType
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Language\AST\FieldNode;
|
use GraphQL\Language\AST\FieldNode;
|
||||||
@ -9,6 +12,7 @@ use GraphQL\Language\AST\OperationDefinitionNode;
|
|||||||
use GraphQL\Language\AST\SelectionSetNode;
|
use GraphQL\Language\AST\SelectionSetNode;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Type\Schema;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function array_merge_recursive;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure containing information useful for field resolution process.
|
* Structure containing information useful for field resolution process.
|
||||||
@ -20,7 +24,7 @@ class ResolveInfo
|
|||||||
* The name of the field being resolved
|
* The name of the field being resolved
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
public $fieldName;
|
public $fieldName;
|
||||||
|
|
||||||
@ -28,7 +32,7 @@ class ResolveInfo
|
|||||||
* AST of all nodes referencing this field in the query.
|
* AST of all nodes referencing this field in the query.
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var FieldNode[]
|
* @var FieldNode[]|null
|
||||||
*/
|
*/
|
||||||
public $fieldNodes;
|
public $fieldNodes;
|
||||||
|
|
||||||
@ -44,7 +48,7 @@ class ResolveInfo
|
|||||||
* Parent type of the field being resolved
|
* Parent type of the field being resolved
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var ObjectType
|
* @var ObjectType|null
|
||||||
*/
|
*/
|
||||||
public $parentType;
|
public $parentType;
|
||||||
|
|
||||||
@ -52,7 +56,7 @@ class ResolveInfo
|
|||||||
* Path to this field from the very root value
|
* Path to this field from the very root value
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var array
|
* @var mixed[]|null
|
||||||
*/
|
*/
|
||||||
public $path;
|
public $path;
|
||||||
|
|
||||||
@ -60,7 +64,7 @@ class ResolveInfo
|
|||||||
* Instance of a schema used for execution
|
* Instance of a schema used for execution
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var Schema
|
* @var Schema|null
|
||||||
*/
|
*/
|
||||||
public $schema;
|
public $schema;
|
||||||
|
|
||||||
@ -68,7 +72,7 @@ class ResolveInfo
|
|||||||
* AST of all fragments defined in query
|
* AST of all fragments defined in query
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var FragmentDefinitionNode[]
|
* @var FragmentDefinitionNode[]|null
|
||||||
*/
|
*/
|
||||||
public $fragments;
|
public $fragments;
|
||||||
|
|
||||||
@ -76,7 +80,7 @@ class ResolveInfo
|
|||||||
* Root value passed to query execution
|
* Root value passed to query execution
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var mixed
|
* @var mixed|null
|
||||||
*/
|
*/
|
||||||
public $rootValue;
|
public $rootValue;
|
||||||
|
|
||||||
@ -84,7 +88,7 @@ class ResolveInfo
|
|||||||
* AST of operation definition node (query, mutation)
|
* AST of operation definition node (query, mutation)
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var OperationDefinitionNode
|
* @var OperationDefinitionNode|null
|
||||||
*/
|
*/
|
||||||
public $operation;
|
public $operation;
|
||||||
|
|
||||||
@ -92,10 +96,13 @@ class ResolveInfo
|
|||||||
* Array of variables passed to query execution
|
* Array of variables passed to query execution
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @var array
|
* @var mixed[]|null
|
||||||
*/
|
*/
|
||||||
public $variableValues;
|
public $variableValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $values
|
||||||
|
*/
|
||||||
public function __construct(array $values)
|
public function __construct(array $values)
|
||||||
{
|
{
|
||||||
Utils::assign($this, $values);
|
Utils::assign($this, $values);
|
||||||
@ -134,7 +141,7 @@ class ResolveInfo
|
|||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @param int $depth How many levels to include in output
|
* @param int $depth How many levels to include in output
|
||||||
* @return array
|
* @return bool[]
|
||||||
*/
|
*/
|
||||||
public function getFieldSelection($depth = 0)
|
public function getFieldSelection($depth = 0)
|
||||||
{
|
{
|
||||||
@ -148,7 +155,10 @@ class ResolveInfo
|
|||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function foldSelectionSet(SelectionSetNode $selectionSet, $descend)
|
/**
|
||||||
|
* @return bool[]
|
||||||
|
*/
|
||||||
|
private function foldSelectionSet(SelectionSetNode $selectionSet, int $descend) : array
|
||||||
{
|
{
|
||||||
$fields = [];
|
$fields = [];
|
||||||
|
|
||||||
@ -162,10 +172,16 @@ class ResolveInfo
|
|||||||
if (isset($this->fragments[$spreadName])) {
|
if (isset($this->fragments[$spreadName])) {
|
||||||
/** @var FragmentDefinitionNode $fragment */
|
/** @var FragmentDefinitionNode $fragment */
|
||||||
$fragment = $this->fragments[$spreadName];
|
$fragment = $this->fragments[$spreadName];
|
||||||
$fields = array_merge_recursive($this->foldSelectionSet($fragment->selectionSet, $descend), $fields);
|
$fields = array_merge_recursive(
|
||||||
|
$this->foldSelectionSet($fragment->selectionSet, $descend),
|
||||||
|
$fields
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} elseif ($selectionNode instanceof InlineFragmentNode) {
|
} elseif ($selectionNode instanceof InlineFragmentNode) {
|
||||||
$fields = array_merge_recursive($this->foldSelectionSet($selectionNode->selectionSet, $descend), $fields);
|
$fields = array_merge_recursive(
|
||||||
|
$this->foldSelectionSet($selectionNode->selectionSet, $descend),
|
||||||
|
$fields
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
|
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scalar Type Definition
|
* Scalar Type Definition
|
||||||
@ -24,16 +28,17 @@ use GraphQL\Utils\Utils;
|
|||||||
*/
|
*/
|
||||||
abstract class ScalarType extends Type implements OutputType, InputType, LeafType, NamedType
|
abstract class ScalarType extends Type implements OutputType, InputType, LeafType, NamedType
|
||||||
{
|
{
|
||||||
/**
|
/** @var ScalarTypeDefinitionNode|null */
|
||||||
* @var ScalarTypeDefinitionNode|null
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
function __construct(array $config = [])
|
/**
|
||||||
|
* @param mixed[] $config
|
||||||
|
*/
|
||||||
|
public function __construct(array $config = [])
|
||||||
{
|
{
|
||||||
$this->name = isset($config['name']) ? $config['name'] : $this->tryInferName();
|
$this->name = $config['name'] ?? $this->tryInferName();
|
||||||
$this->description = isset($config['description']) ? $config['description'] : $this->description;
|
$this->description = $config['description'] ?? $this->description;
|
||||||
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;
|
$this->astNode = $config['astNode'] ?? null;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
|
|
||||||
Utils::invariant(is_string($this->name), 'Must provide name.');
|
Utils::invariant(is_string($this->name), 'Must provide name.');
|
||||||
|
@ -1,24 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Language\AST\StringValueNode;
|
use GraphQL\Language\AST\StringValueNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_array;
|
||||||
|
use function is_object;
|
||||||
|
use function is_scalar;
|
||||||
|
use function method_exists;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class StringType
|
* Class StringType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class StringType extends ScalarType
|
class StringType extends ScalarType
|
||||||
{
|
{
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name = Type::STRING;
|
public $name = Type::STRING;
|
||||||
|
|
||||||
/**
|
/** @var string */
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $description =
|
public $description =
|
||||||
'The `String` scalar type represents textual data, represented as UTF-8
|
'The `String` scalar type represents textual data, represented as UTF-8
|
||||||
character sequences. The String type is most often used by GraphQL to
|
character sequences. The String type is most often used by GraphQL to
|
||||||
@ -44,11 +47,24 @@ represent free-form human-readable text.';
|
|||||||
return (string) $value;
|
return (string) $value;
|
||||||
}
|
}
|
||||||
if (! is_scalar($value)) {
|
if (! is_scalar($value)) {
|
||||||
throw new Error("String cannot represent non scalar value: " . Utils::printSafe($value));
|
throw new Error('String cannot represent non scalar value: ' . Utils::printSafe($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->coerceString($value);
|
return $this->coerceString($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function coerceString($value)
|
||||||
|
{
|
||||||
|
if (is_array($value)) {
|
||||||
|
throw new Error(
|
||||||
|
'String cannot represent an array value: ' .
|
||||||
|
Utils::printSafe($value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
* @return string
|
* @return string
|
||||||
@ -60,12 +76,12 @@ represent free-form human-readable text.';
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $valueNode
|
* @param Node $valueNode
|
||||||
* @param array|null $variables
|
* @param mixed[]|null $variables
|
||||||
* @return null|string
|
* @return null|string
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function parseLiteral($valueNode, array $variables = null)
|
public function parseLiteral($valueNode, ?array $variables = null)
|
||||||
{
|
{
|
||||||
if ($valueNode instanceof StringValueNode) {
|
if ($valueNode instanceof StringValueNode) {
|
||||||
return $valueNode->value;
|
return $valueNode->value;
|
||||||
@ -74,15 +90,4 @@ represent free-form human-readable text.';
|
|||||||
// Intentionally without message, as all information already in wrapped Exception
|
// Intentionally without message, as all information already in wrapped Exception
|
||||||
throw new \Exception();
|
throw new \Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function coerceString($value) {
|
|
||||||
if (is_array($value)) {
|
|
||||||
throw new Error(
|
|
||||||
'String cannot represent an array value: ' .
|
|
||||||
Utils::printSafe($value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (string) $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,48 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\TypeDefinitionNode;
|
use GraphQL\Language\AST\TypeDefinitionNode;
|
||||||
use GraphQL\Type\Introspection;
|
use GraphQL\Type\Introspection;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function array_keys;
|
||||||
|
use function array_merge;
|
||||||
|
use function in_array;
|
||||||
|
use function preg_replace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry of standard GraphQL types
|
* Registry of standard GraphQL types
|
||||||
* and a base class for all other types.
|
* and a base class for all other types.
|
||||||
*
|
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
abstract class Type implements \JsonSerializable
|
abstract class Type implements \JsonSerializable
|
||||||
{
|
{
|
||||||
const STRING = 'String';
|
public const STRING = 'String';
|
||||||
const INT = 'Int';
|
public const INT = 'Int';
|
||||||
const BOOLEAN = 'Boolean';
|
public const BOOLEAN = 'Boolean';
|
||||||
const FLOAT = 'Float';
|
public const FLOAT = 'Float';
|
||||||
const ID = 'ID';
|
public const ID = 'ID';
|
||||||
|
|
||||||
/**
|
/** @var Type[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private static $internalTypes;
|
private static $internalTypes;
|
||||||
|
|
||||||
/**
|
/** @var Type[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private static $builtInTypes;
|
private static $builtInTypes;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
public $description;
|
||||||
|
|
||||||
|
/** @var TypeDefinitionNode|null */
|
||||||
|
public $astNode;
|
||||||
|
|
||||||
|
/** @var mixed[] */
|
||||||
|
public $config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @return IDType
|
* @return IDType
|
||||||
@ -39,6 +52,25 @@ abstract class Type implements \JsonSerializable
|
|||||||
return self::getInternalType(self::ID);
|
return self::getInternalType(self::ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return (IDType|StringType|FloatType|IntType|BooleanType)[]|IDType|StringType|FloatType|IntType|BooleanType
|
||||||
|
*/
|
||||||
|
private static function getInternalType($name = null)
|
||||||
|
{
|
||||||
|
if (self::$internalTypes === null) {
|
||||||
|
self::$internalTypes = [
|
||||||
|
self::ID => new IDType(),
|
||||||
|
self::STRING => new StringType(),
|
||||||
|
self::FLOAT => new FloatType(),
|
||||||
|
self::INT => new IntType(),
|
||||||
|
self::BOOLEAN => new BooleanType(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name ? self::$internalTypes[$name] : self::$internalTypes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @return StringType
|
* @return StringType
|
||||||
@ -96,31 +128,13 @@ abstract class Type implements \JsonSerializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $name
|
* Checks if the type is a builtin type
|
||||||
* @return array|IDType|StringType|FloatType|IntType|BooleanType
|
|
||||||
*/
|
|
||||||
private static function getInternalType($name = null)
|
|
||||||
{
|
|
||||||
if (null === self::$internalTypes) {
|
|
||||||
self::$internalTypes = [
|
|
||||||
self::ID => new IDType(),
|
|
||||||
self::STRING => new StringType(),
|
|
||||||
self::FLOAT => new FloatType(),
|
|
||||||
self::INT => new IntType(),
|
|
||||||
self::BOOLEAN => new BooleanType()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return $name ? self::$internalTypes[$name] : self::$internalTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all builtin scalar types
|
|
||||||
*
|
*
|
||||||
* @return Type[]
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function getInternalTypes()
|
public static function isBuiltInType(Type $type)
|
||||||
{
|
{
|
||||||
return self::getInternalType();
|
return in_array($type->name, array_keys(self::getAllBuiltInTypes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,24 +145,24 @@ abstract class Type implements \JsonSerializable
|
|||||||
*/
|
*/
|
||||||
public static function getAllBuiltInTypes()
|
public static function getAllBuiltInTypes()
|
||||||
{
|
{
|
||||||
if (null === self::$builtInTypes) {
|
if (self::$builtInTypes === null) {
|
||||||
self::$builtInTypes = array_merge(
|
self::$builtInTypes = array_merge(
|
||||||
Introspection::getTypes(),
|
Introspection::getTypes(),
|
||||||
self::getInternalTypes()
|
self::getInternalTypes()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$builtInTypes;
|
return self::$builtInTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the type is a builtin type
|
* Returns all builtin scalar types
|
||||||
*
|
*
|
||||||
* @param Type $type
|
* @return Type[]
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public static function isBuiltInType(Type $type)
|
public static function getInternalTypes()
|
||||||
{
|
{
|
||||||
return in_array($type->name, array_keys(self::getAllBuiltInTypes()));
|
return self::getInternalType();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,6 +179,23 @@ abstract class Type implements \JsonSerializable
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @param Type $type
|
||||||
|
* @return ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType
|
||||||
|
*/
|
||||||
|
public static function getNamedType($type)
|
||||||
|
{
|
||||||
|
if ($type === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
while ($type instanceof WrappingType) {
|
||||||
|
$type = $type->getWrappedType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @param Type $type
|
* @param Type $type
|
||||||
@ -181,7 +212,7 @@ abstract class Type implements \JsonSerializable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @param $type
|
* @param Type $type
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function isLeafType($type)
|
public static function isLeafType($type)
|
||||||
@ -209,16 +240,6 @@ abstract class Type implements \JsonSerializable
|
|||||||
return $type instanceof AbstractType;
|
return $type instanceof AbstractType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @api
|
|
||||||
* @param Type $type
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function isType($type)
|
|
||||||
{
|
|
||||||
return $type instanceof Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $type
|
* @param mixed $type
|
||||||
* @return mixed
|
* @return mixed
|
||||||
@ -233,6 +254,16 @@ abstract class Type implements \JsonSerializable
|
|||||||
return $type;
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @param Type $type
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isType($type)
|
||||||
|
{
|
||||||
|
return $type instanceof Type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @param Type $type
|
* @param Type $type
|
||||||
@ -244,40 +275,42 @@ abstract class Type implements \JsonSerializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @throws InvariantViolation
|
||||||
* @param Type $type
|
|
||||||
* @return ObjectType|InterfaceType|UnionType|ScalarType|InputObjectType|EnumType
|
|
||||||
*/
|
*/
|
||||||
public static function getNamedType($type)
|
public function assertValid()
|
||||||
{
|
{
|
||||||
if (null === $type) {
|
Utils::assertValidName($this->name);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
while ($type instanceof WrappingType) {
|
|
||||||
$type = $type->getWrappedType();
|
|
||||||
}
|
|
||||||
return $type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public $name;
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return $this->toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|null
|
* @return string
|
||||||
*/
|
*/
|
||||||
public $description;
|
public function toString()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var TypeDefinitionNode|null
|
* @return string
|
||||||
*/
|
*/
|
||||||
public $astNode;
|
public function __toString()
|
||||||
|
{
|
||||||
/**
|
try {
|
||||||
* @var array
|
return $this->toString();
|
||||||
*/
|
} catch (\Exception $e) {
|
||||||
public $config;
|
echo $e;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return null|string
|
* @return null|string
|
||||||
@ -297,44 +330,7 @@ abstract class Type implements \JsonSerializable
|
|||||||
if ($tmp->getNamespaceName() !== __NAMESPACE__) {
|
if ($tmp->getNamespaceName() !== __NAMESPACE__) {
|
||||||
return preg_replace('~Type$~', '', $name);
|
return preg_replace('~Type$~', '', $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws InvariantViolation
|
|
||||||
*/
|
|
||||||
public function assertValid()
|
|
||||||
{
|
|
||||||
Utils::assertValidName($this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function toString()
|
|
||||||
{
|
|
||||||
return $this->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function jsonSerialize()
|
|
||||||
{
|
|
||||||
return $this->toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return $this->toString();
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
echo $e;
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,32 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\UnionTypeDefinitionNode;
|
use GraphQL\Language\AST\UnionTypeDefinitionNode;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function call_user_func;
|
||||||
|
use function is_array;
|
||||||
|
use function is_callable;
|
||||||
|
use function is_string;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class UnionType
|
* Class UnionType
|
||||||
* @package GraphQL\Type\Definition
|
|
||||||
*/
|
*/
|
||||||
class UnionType extends Type implements AbstractType, OutputType, CompositeType, NamedType
|
class UnionType extends Type implements AbstractType, OutputType, CompositeType, NamedType
|
||||||
{
|
{
|
||||||
/**
|
/** @var UnionTypeDefinitionNode */
|
||||||
* @var UnionTypeDefinitionNode
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
/**
|
/** @var ObjectType[] */
|
||||||
* @var ObjectType[]
|
|
||||||
*/
|
|
||||||
private $types;
|
private $types;
|
||||||
|
|
||||||
/**
|
/** @var ObjectType[] */
|
||||||
* @var ObjectType[]
|
|
||||||
*/
|
|
||||||
private $possibleTypeNames;
|
private $possibleTypeNames;
|
||||||
|
|
||||||
/**
|
|
||||||
* UnionType constructor.
|
|
||||||
* @param $config
|
|
||||||
*/
|
|
||||||
public function __construct($config)
|
public function __construct($config)
|
||||||
{
|
{
|
||||||
if (! isset($config['name'])) {
|
if (! isset($config['name'])) {
|
||||||
@ -44,17 +41,36 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
|
|||||||
* Object type.
|
* Object type.
|
||||||
*/
|
*/
|
||||||
$this->name = $config['name'];
|
$this->name = $config['name'];
|
||||||
$this->description = isset($config['description']) ? $config['description'] : null;
|
$this->description = $config['description'] ?? null;
|
||||||
$this->astNode = isset($config['astNode']) ? $config['astNode'] : null;
|
$this->astNode = $config['astNode'] ?? null;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function isPossibleType(Type $type)
|
||||||
|
{
|
||||||
|
if (! $type instanceof ObjectType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->possibleTypeNames === null) {
|
||||||
|
$this->possibleTypeNames = [];
|
||||||
|
foreach ($this->getTypes() as $possibleType) {
|
||||||
|
$this->possibleTypeNames[$possibleType->name] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($this->possibleTypeNames[$type->name]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ObjectType[]
|
* @return ObjectType[]
|
||||||
*/
|
*/
|
||||||
public function getTypes()
|
public function getTypes()
|
||||||
{
|
{
|
||||||
if (null === $this->types) {
|
if ($this->types === null) {
|
||||||
if (! isset($this->config['types'])) {
|
if (! isset($this->config['types'])) {
|
||||||
$types = null;
|
$types = null;
|
||||||
} elseif (is_callable($this->config['types'])) {
|
} elseif (is_callable($this->config['types'])) {
|
||||||
@ -65,49 +81,34 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
|
|||||||
|
|
||||||
if (! is_array($types)) {
|
if (! is_array($types)) {
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"Must provide Array of types or a callable which returns " .
|
sprintf(
|
||||||
"such an array for Union {$this->name}"
|
'Must provide Array of types or a callable which returns such an array for Union %s',
|
||||||
|
$this->name
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->types = $types;
|
$this->types = $types;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->types;
|
return $this->types;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Type $type
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function isPossibleType(Type $type)
|
|
||||||
{
|
|
||||||
if (!$type instanceof ObjectType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $this->possibleTypeNames) {
|
|
||||||
$this->possibleTypeNames = [];
|
|
||||||
foreach ($this->getTypes() as $possibleType) {
|
|
||||||
$this->possibleTypeNames[$possibleType->name] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isset($this->possibleTypeNames[$type->name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves concrete ObjectType for given object value
|
* Resolves concrete ObjectType for given object value
|
||||||
*
|
*
|
||||||
* @param $objectValue
|
* @param object $objectValue
|
||||||
* @param $context
|
* @param mixed $context
|
||||||
* @param ResolveInfo $info
|
|
||||||
* @return callable|null
|
* @return callable|null
|
||||||
*/
|
*/
|
||||||
public function resolveType($objectValue, $context, ResolveInfo $info)
|
public function resolveType($objectValue, $context, ResolveInfo $info)
|
||||||
{
|
{
|
||||||
if (isset($this->config['resolveType'])) {
|
if (isset($this->config['resolveType'])) {
|
||||||
$fn = $this->config['resolveType'];
|
$fn = $this->config['resolveType'];
|
||||||
|
|
||||||
return $fn($objectValue, $context, $info);
|
return $fn($objectValue, $context, $info);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,11 +119,17 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
|
|||||||
{
|
{
|
||||||
parent::assertValid();
|
parent::assertValid();
|
||||||
|
|
||||||
if (isset($this->config['resolveType'])) {
|
if (! isset($this->config['resolveType'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
is_callable($this->config['resolveType']),
|
is_callable($this->config['resolveType']),
|
||||||
"{$this->name} must provide \"resolveType\" as a function, but got: " . Utils::printSafe($this->config['resolveType'])
|
sprintf(
|
||||||
|
'%s must provide "resolveType" as a function, but got: %s',
|
||||||
|
$this->name,
|
||||||
|
Utils::printSafe($this->config['resolveType'])
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -10,6 +13,7 @@ GraphQLUnionType |
|
|||||||
GraphQLEnumType |
|
GraphQLEnumType |
|
||||||
GraphQLInputObjectType;
|
GraphQLInputObjectType;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface UnmodifiedType
|
interface UnmodifiedType
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type\Definition;
|
namespace GraphQL\Type\Definition;
|
||||||
|
|
||||||
interface WrappingType
|
interface WrappingType
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type;
|
namespace GraphQL\Type;
|
||||||
|
|
||||||
use GraphQL\Type\Definition\AbstractType;
|
use GraphQL\Type\Definition\AbstractType;
|
||||||
@ -12,24 +15,17 @@ use GraphQL\Utils\Utils;
|
|||||||
/**
|
/**
|
||||||
* EXPERIMENTAL!
|
* EXPERIMENTAL!
|
||||||
* This class can be removed or changed in future versions without a prior notice.
|
* This class can be removed or changed in future versions without a prior notice.
|
||||||
*
|
|
||||||
* Class EagerResolution
|
|
||||||
* @package GraphQL\Type
|
|
||||||
*/
|
*/
|
||||||
class EagerResolution implements Resolution
|
class EagerResolution implements Resolution
|
||||||
{
|
{
|
||||||
/**
|
/** @var Type[] */
|
||||||
* @var Type[]
|
|
||||||
*/
|
|
||||||
private $typeMap = [];
|
private $typeMap = [];
|
||||||
|
|
||||||
/**
|
/** @var array<string, ObjectType[]> */
|
||||||
* @var array<string, ObjectType[]>
|
|
||||||
*/
|
|
||||||
private $implementations = [];
|
private $implementations = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EagerResolution constructor.
|
*
|
||||||
* @param Type[] $initialTypes
|
* @param Type[] $initialTypes
|
||||||
*/
|
*/
|
||||||
public function __construct(array $initialTypes)
|
public function __construct(array $initialTypes)
|
||||||
@ -42,20 +38,22 @@ class EagerResolution implements Resolution
|
|||||||
|
|
||||||
// Keep track of all possible types for abstract types
|
// Keep track of all possible types for abstract types
|
||||||
foreach ($this->typeMap as $typeName => $type) {
|
foreach ($this->typeMap as $typeName => $type) {
|
||||||
if ($type instanceof ObjectType) {
|
if (! ($type instanceof ObjectType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($type->getInterfaces() as $iface) {
|
foreach ($type->getInterfaces() as $iface) {
|
||||||
$this->implementations[$iface->name][] = $type;
|
$this->implementations[$iface->name][] = $type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
public function resolveType($name)
|
public function resolveType($name)
|
||||||
{
|
{
|
||||||
return isset($this->typeMap[$name]) ? $this->typeMap[$name] : null;
|
return $this->typeMap[$name] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,21 +71,14 @@ class EagerResolution implements Resolution
|
|||||||
|
|
||||||
/** @var InterfaceType $abstractType */
|
/** @var InterfaceType $abstractType */
|
||||||
Utils::invariant($abstractType instanceof InterfaceType);
|
Utils::invariant($abstractType instanceof InterfaceType);
|
||||||
return isset($this->implementations[$abstractType->name]) ? $this->implementations[$abstractType->name] : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return $this->implementations[$abstractType->name] ?? [];
|
||||||
* @return Type[]
|
|
||||||
*/
|
|
||||||
public function getTypeMap()
|
|
||||||
{
|
|
||||||
return $this->typeMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns serializable schema representation suitable for GraphQL\Type\LazyResolution
|
* Returns serializable schema representation suitable for GraphQL\Type\LazyResolution
|
||||||
*
|
*
|
||||||
* @return array
|
* @return mixed[]
|
||||||
*/
|
*/
|
||||||
public function getDescriptor()
|
public function getDescriptor()
|
||||||
{
|
{
|
||||||
@ -105,10 +96,19 @@ class EagerResolution implements Resolution
|
|||||||
}
|
}
|
||||||
$typeMap[$type->name] = 1;
|
$typeMap[$type->name] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'version' => '1.0',
|
'version' => '1.0',
|
||||||
'typeMap' => $typeMap,
|
'typeMap' => $typeMap,
|
||||||
'possibleTypeMap' => $possibleTypesMap
|
'possibleTypeMap' => $possibleTypesMap,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Type[]
|
||||||
|
*/
|
||||||
|
public function getTypeMap()
|
||||||
|
{
|
||||||
|
return $this->typeMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type;
|
namespace GraphQL\Type;
|
||||||
|
|
||||||
|
use GraphQL\Language\DirectiveLocation;
|
||||||
use GraphQL\Language\Printer;
|
use GraphQL\Language\Printer;
|
||||||
use GraphQL\Type\Definition\Directive;
|
use GraphQL\Type\Definition\Directive;
|
||||||
use GraphQL\Language\DirectiveLocation;
|
|
||||||
use GraphQL\Type\Definition\EnumType;
|
use GraphQL\Type\Definition\EnumType;
|
||||||
use GraphQL\Type\Definition\FieldArgument;
|
use GraphQL\Type\Definition\FieldArgument;
|
||||||
use GraphQL\Type\Definition\FieldDefinition;
|
use GraphQL\Type\Definition\FieldDefinition;
|
||||||
@ -18,22 +21,20 @@ use GraphQL\Type\Definition\ScalarType;
|
|||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Definition\UnionType;
|
use GraphQL\Type\Definition\UnionType;
|
||||||
use GraphQL\Type\Definition\WrappingType;
|
use GraphQL\Type\Definition\WrappingType;
|
||||||
use GraphQL\Utils\Utils;
|
|
||||||
use GraphQL\Utils\AST;
|
use GraphQL\Utils\AST;
|
||||||
|
use GraphQL\Utils\Utils;
|
||||||
class TypeKind {
|
use function array_filter;
|
||||||
const SCALAR = 0;
|
use function array_key_exists;
|
||||||
const OBJECT = 1;
|
use function array_values;
|
||||||
const INTERFACE_KIND = 2;
|
use function in_array;
|
||||||
const UNION = 3;
|
use function is_bool;
|
||||||
const ENUM = 4;
|
use function method_exists;
|
||||||
const INPUT_OBJECT = 5;
|
use function trigger_error;
|
||||||
const LIST_KIND = 6;
|
use const E_USER_DEPRECATED;
|
||||||
const NON_NULL = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Introspection
|
class Introspection
|
||||||
{
|
{
|
||||||
|
/** @var Type[] */
|
||||||
private static $map = [];
|
private static $map = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +43,7 @@ class Introspection
|
|||||||
* Whether to include descriptions in the introspection result.
|
* Whether to include descriptions in the introspection result.
|
||||||
* Default: true
|
* Default: true
|
||||||
*
|
*
|
||||||
* @param array $options
|
* @param bool[]|bool $options
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function getIntrospectionQuery($options = [])
|
public static function getIntrospectionQuery($options = [])
|
||||||
@ -154,6 +155,15 @@ class Introspection
|
|||||||
EOD;
|
EOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Type $type
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isIntrospectionType($type)
|
||||||
|
{
|
||||||
|
return array_key_exists($type->name, self::getTypes());
|
||||||
|
}
|
||||||
|
|
||||||
public static function getTypes()
|
public static function getTypes()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -168,15 +178,6 @@ EOD;
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Type $type
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function isIntrospectionType($type)
|
|
||||||
{
|
|
||||||
return in_array($type->name, array_keys(self::getTypes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function _schema()
|
public static function _schema()
|
||||||
{
|
{
|
||||||
if (! isset(self::$map['__Schema'])) {
|
if (! isset(self::$map['__Schema'])) {
|
||||||
@ -194,14 +195,14 @@ EOD;
|
|||||||
'type' => new NonNull(new ListOfType(new NonNull(self::_type()))),
|
'type' => new NonNull(new ListOfType(new NonNull(self::_type()))),
|
||||||
'resolve' => function (Schema $schema) {
|
'resolve' => function (Schema $schema) {
|
||||||
return array_values($schema->getTypeMap());
|
return array_values($schema->getTypeMap());
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'queryType' => [
|
'queryType' => [
|
||||||
'description' => 'The type that query operations will be rooted at.',
|
'description' => 'The type that query operations will be rooted at.',
|
||||||
'type' => new NonNull(self::_type()),
|
'type' => new NonNull(self::_type()),
|
||||||
'resolve' => function (Schema $schema) {
|
'resolve' => function (Schema $schema) {
|
||||||
return $schema->getQueryType();
|
return $schema->getQueryType();
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'mutationType' => [
|
'mutationType' => [
|
||||||
'description' =>
|
'description' =>
|
||||||
@ -210,7 +211,7 @@ EOD;
|
|||||||
'type' => self::_type(),
|
'type' => self::_type(),
|
||||||
'resolve' => function (Schema $schema) {
|
'resolve' => function (Schema $schema) {
|
||||||
return $schema->getMutationType();
|
return $schema->getMutationType();
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'subscriptionType' => [
|
'subscriptionType' => [
|
||||||
'description' => 'If this server support subscription, the type that subscription operations will be rooted at.',
|
'description' => 'If this server support subscription, the type that subscription operations will be rooted at.',
|
||||||
@ -224,163 +225,15 @@ EOD;
|
|||||||
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_directive()))),
|
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_directive()))),
|
||||||
'resolve' => function (Schema $schema) {
|
'resolve' => function (Schema $schema) {
|
||||||
return $schema->getDirectives();
|
return $schema->getDirectives();
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$map['__Schema'];
|
return self::$map['__Schema'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function _directive()
|
|
||||||
{
|
|
||||||
if (!isset(self::$map['__Directive'])) {
|
|
||||||
self::$map['__Directive'] = new ObjectType([
|
|
||||||
'name' => '__Directive',
|
|
||||||
'isIntrospection' => true,
|
|
||||||
'description' => 'A Directive provides a way to describe alternate runtime execution and ' .
|
|
||||||
'type validation behavior in a GraphQL document.' .
|
|
||||||
"\n\nIn some cases, you need to provide options to alter GraphQL's " .
|
|
||||||
'execution behavior in ways field arguments will not suffice, such as ' .
|
|
||||||
'conditionally including or skipping a field. Directives provide this by ' .
|
|
||||||
'describing additional information to the executor.',
|
|
||||||
'fields' => [
|
|
||||||
'name' => ['type' => Type::nonNull(Type::string())],
|
|
||||||
'description' => ['type' => Type::string()],
|
|
||||||
'locations' => [
|
|
||||||
'type' => Type::nonNull(Type::listOf(Type::nonNull(
|
|
||||||
self::_directiveLocation()
|
|
||||||
)))
|
|
||||||
],
|
|
||||||
'args' => [
|
|
||||||
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))),
|
|
||||||
'resolve' => function (Directive $directive) {
|
|
||||||
return $directive->args ?: [];
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// NOTE: the following three fields are deprecated and are no longer part
|
|
||||||
// of the GraphQL specification.
|
|
||||||
'onOperation' => [
|
|
||||||
'deprecationReason' => 'Use `locations`.',
|
|
||||||
'type' => Type::nonNull(Type::boolean()),
|
|
||||||
'resolve' => function($d) {
|
|
||||||
return in_array(DirectiveLocation::QUERY, $d->locations) ||
|
|
||||||
in_array(DirectiveLocation::MUTATION, $d->locations) ||
|
|
||||||
in_array(DirectiveLocation::SUBSCRIPTION, $d->locations);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'onFragment' => [
|
|
||||||
'deprecationReason' => 'Use `locations`.',
|
|
||||||
'type' => Type::nonNull(Type::boolean()),
|
|
||||||
'resolve' => function($d) {
|
|
||||||
return in_array(DirectiveLocation::FRAGMENT_SPREAD, $d->locations) ||
|
|
||||||
in_array(DirectiveLocation::INLINE_FRAGMENT, $d->locations) ||
|
|
||||||
in_array(DirectiveLocation::FRAGMENT_DEFINITION, $d->locations);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'onField' => [
|
|
||||||
'deprecationReason' => 'Use `locations`.',
|
|
||||||
'type' => Type::nonNull(Type::boolean()),
|
|
||||||
'resolve' => function($d) {
|
|
||||||
return in_array(DirectiveLocation::FIELD, $d->locations);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return self::$map['__Directive'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function _directiveLocation()
|
|
||||||
{
|
|
||||||
if (!isset(self::$map['__DirectiveLocation'])) {
|
|
||||||
self::$map['__DirectiveLocation'] = new EnumType([
|
|
||||||
'name' => '__DirectiveLocation',
|
|
||||||
'isIntrospection' => true,
|
|
||||||
'description' =>
|
|
||||||
'A Directive can be adjacent to many parts of the GraphQL language, a ' .
|
|
||||||
'__DirectiveLocation describes one such possible adjacencies.',
|
|
||||||
'values' => [
|
|
||||||
'QUERY' => [
|
|
||||||
'value' => DirectiveLocation::QUERY,
|
|
||||||
'description' => 'Location adjacent to a query operation.'
|
|
||||||
],
|
|
||||||
'MUTATION' => [
|
|
||||||
'value' => DirectiveLocation::MUTATION,
|
|
||||||
'description' => 'Location adjacent to a mutation operation.'
|
|
||||||
],
|
|
||||||
'SUBSCRIPTION' => [
|
|
||||||
'value' => DirectiveLocation::SUBSCRIPTION,
|
|
||||||
'description' => 'Location adjacent to a subscription operation.'
|
|
||||||
],
|
|
||||||
'FIELD' => [
|
|
||||||
'value' => DirectiveLocation::FIELD,
|
|
||||||
'description' => 'Location adjacent to a field.'
|
|
||||||
],
|
|
||||||
'FRAGMENT_DEFINITION' => [
|
|
||||||
'value' => DirectiveLocation::FRAGMENT_DEFINITION,
|
|
||||||
'description' => 'Location adjacent to a fragment definition.'
|
|
||||||
],
|
|
||||||
'FRAGMENT_SPREAD' => [
|
|
||||||
'value' => DirectiveLocation::FRAGMENT_SPREAD,
|
|
||||||
'description' => 'Location adjacent to a fragment spread.'
|
|
||||||
],
|
|
||||||
'INLINE_FRAGMENT' => [
|
|
||||||
'value' => DirectiveLocation::INLINE_FRAGMENT,
|
|
||||||
'description' => 'Location adjacent to an inline fragment.'
|
|
||||||
],
|
|
||||||
'SCHEMA' => [
|
|
||||||
'value' => DirectiveLocation::SCHEMA,
|
|
||||||
'description' => 'Location adjacent to a schema definition.'
|
|
||||||
],
|
|
||||||
'SCALAR' => [
|
|
||||||
'value' => DirectiveLocation::SCALAR,
|
|
||||||
'description' => 'Location adjacent to a scalar definition.'
|
|
||||||
],
|
|
||||||
'OBJECT' => [
|
|
||||||
'value' => DirectiveLocation::OBJECT,
|
|
||||||
'description' => 'Location adjacent to an object type definition.'
|
|
||||||
],
|
|
||||||
'FIELD_DEFINITION' => [
|
|
||||||
'value' => DirectiveLocation::FIELD_DEFINITION,
|
|
||||||
'description' => 'Location adjacent to a field definition.'
|
|
||||||
],
|
|
||||||
'ARGUMENT_DEFINITION' => [
|
|
||||||
'value' => DirectiveLocation::ARGUMENT_DEFINITION,
|
|
||||||
'description' => 'Location adjacent to an argument definition.'
|
|
||||||
],
|
|
||||||
'INTERFACE' => [
|
|
||||||
'value' => DirectiveLocation::IFACE,
|
|
||||||
'description' => 'Location adjacent to an interface definition.'
|
|
||||||
],
|
|
||||||
'UNION' => [
|
|
||||||
'value' => DirectiveLocation::UNION,
|
|
||||||
'description' => 'Location adjacent to a union definition.'
|
|
||||||
],
|
|
||||||
'ENUM' => [
|
|
||||||
'value' => DirectiveLocation::ENUM,
|
|
||||||
'description' => 'Location adjacent to an enum definition.'
|
|
||||||
],
|
|
||||||
'ENUM_VALUE' => [
|
|
||||||
'value' => DirectiveLocation::ENUM_VALUE,
|
|
||||||
'description' => 'Location adjacent to an enum value definition.'
|
|
||||||
],
|
|
||||||
'INPUT_OBJECT' => [
|
|
||||||
'value' => DirectiveLocation::INPUT_OBJECT,
|
|
||||||
'description' => 'Location adjacent to an input object type definition.'
|
|
||||||
],
|
|
||||||
'INPUT_FIELD_DEFINITION' => [
|
|
||||||
'value' => DirectiveLocation::INPUT_FIELD_DEFINITION,
|
|
||||||
'description' => 'Location adjacent to an input object field definition.'
|
|
||||||
]
|
|
||||||
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return self::$map['__DirectiveLocation'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function _type()
|
public static function _type()
|
||||||
{
|
{
|
||||||
if (! isset(self::$map['__Type'])) {
|
if (! isset(self::$map['__Type'])) {
|
||||||
@ -420,30 +273,35 @@ EOD;
|
|||||||
case $type instanceof UnionType:
|
case $type instanceof UnionType:
|
||||||
return TypeKind::UNION;
|
return TypeKind::UNION;
|
||||||
default:
|
default:
|
||||||
throw new \Exception("Unknown kind of type: " . Utils::printSafe($type));
|
throw new \Exception('Unknown kind of type: ' . Utils::printSafe($type));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'description' => ['type' => Type::string()],
|
'description' => ['type' => Type::string()],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'type' => Type::listOf(Type::nonNull(self::_field())),
|
'type' => Type::listOf(Type::nonNull(self::_field())),
|
||||||
'args' => [
|
'args' => [
|
||||||
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false]
|
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false],
|
||||||
],
|
],
|
||||||
'resolve' => function (Type $type, $args) {
|
'resolve' => function (Type $type, $args) {
|
||||||
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
|
if ($type instanceof ObjectType || $type instanceof InterfaceType) {
|
||||||
$fields = $type->getFields();
|
$fields = $type->getFields();
|
||||||
|
|
||||||
if (empty($args['includeDeprecated'])) {
|
if (empty($args['includeDeprecated'])) {
|
||||||
$fields = array_filter($fields, function (FieldDefinition $field) {
|
$fields = array_filter(
|
||||||
|
$fields,
|
||||||
|
function (FieldDefinition $field) {
|
||||||
return ! $field->deprecationReason;
|
return ! $field->deprecationReason;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return array_values($fields);
|
return array_values($fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'interfaces' => [
|
'interfaces' => [
|
||||||
'type' => Type::listOf(Type::nonNull(self::_type())),
|
'type' => Type::listOf(Type::nonNull(self::_type())),
|
||||||
@ -451,8 +309,9 @@ EOD;
|
|||||||
if ($type instanceof ObjectType) {
|
if ($type instanceof ObjectType) {
|
||||||
return $type->getInterfaces();
|
return $type->getInterfaces();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'possibleTypes' => [
|
'possibleTypes' => [
|
||||||
'type' => Type::listOf(Type::nonNull(self::_type())),
|
'type' => Type::listOf(Type::nonNull(self::_type())),
|
||||||
@ -460,28 +319,33 @@ EOD;
|
|||||||
if ($type instanceof InterfaceType || $type instanceof UnionType) {
|
if ($type instanceof InterfaceType || $type instanceof UnionType) {
|
||||||
return $info->schema->getPossibleTypes($type);
|
return $info->schema->getPossibleTypes($type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'enumValues' => [
|
'enumValues' => [
|
||||||
'type' => Type::listOf(Type::nonNull(self::_enumValue())),
|
'type' => Type::listOf(Type::nonNull(self::_enumValue())),
|
||||||
'args' => [
|
'args' => [
|
||||||
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false]
|
'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false],
|
||||||
],
|
],
|
||||||
'resolve' => function ($type, $args) {
|
'resolve' => function ($type, $args) {
|
||||||
if ($type instanceof EnumType) {
|
if ($type instanceof EnumType) {
|
||||||
$values = array_values($type->getValues());
|
$values = array_values($type->getValues());
|
||||||
|
|
||||||
if (empty($args['includeDeprecated'])) {
|
if (empty($args['includeDeprecated'])) {
|
||||||
$values = array_filter($values, function ($value) {
|
$values = array_filter(
|
||||||
|
$values,
|
||||||
|
function ($value) {
|
||||||
return ! $value->deprecationReason;
|
return ! $value->deprecationReason;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $values;
|
return $values;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'inputFields' => [
|
'inputFields' => [
|
||||||
'type' => Type::listOf(Type::nonNull(self::_inputValue())),
|
'type' => Type::listOf(Type::nonNull(self::_inputValue())),
|
||||||
@ -489,8 +353,9 @@ EOD;
|
|||||||
if ($type instanceof InputObjectType) {
|
if ($type instanceof InputObjectType) {
|
||||||
return array_values($type->getFields());
|
return array_values($type->getFields());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'ofType' => [
|
'ofType' => [
|
||||||
'type' => self::_type(),
|
'type' => self::_type(),
|
||||||
@ -498,20 +363,68 @@ EOD;
|
|||||||
if ($type instanceof WrappingType) {
|
if ($type instanceof WrappingType) {
|
||||||
return $type->getWrappedType();
|
return $type->getWrappedType();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$map['__Type'];
|
return self::$map['__Type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function _typeKind()
|
||||||
|
{
|
||||||
|
if (! isset(self::$map['__TypeKind'])) {
|
||||||
|
self::$map['__TypeKind'] = new EnumType([
|
||||||
|
'name' => '__TypeKind',
|
||||||
|
'isIntrospection' => true,
|
||||||
|
'description' => 'An enum describing what kind of type a given `__Type` is.',
|
||||||
|
'values' => [
|
||||||
|
'SCALAR' => [
|
||||||
|
'value' => TypeKind::SCALAR,
|
||||||
|
'description' => 'Indicates this type is a scalar.',
|
||||||
|
],
|
||||||
|
'OBJECT' => [
|
||||||
|
'value' => TypeKind::OBJECT,
|
||||||
|
'description' => 'Indicates this type is an object. `fields` and `interfaces` are valid fields.',
|
||||||
|
],
|
||||||
|
'INTERFACE' => [
|
||||||
|
'value' => TypeKind::INTERFACE_KIND,
|
||||||
|
'description' => 'Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.',
|
||||||
|
],
|
||||||
|
'UNION' => [
|
||||||
|
'value' => TypeKind::UNION,
|
||||||
|
'description' => 'Indicates this type is a union. `possibleTypes` is a valid field.',
|
||||||
|
],
|
||||||
|
'ENUM' => [
|
||||||
|
'value' => TypeKind::ENUM,
|
||||||
|
'description' => 'Indicates this type is an enum. `enumValues` is a valid field.',
|
||||||
|
],
|
||||||
|
'INPUT_OBJECT' => [
|
||||||
|
'value' => TypeKind::INPUT_OBJECT,
|
||||||
|
'description' => 'Indicates this type is an input object. `inputFields` is a valid field.',
|
||||||
|
],
|
||||||
|
'LIST' => [
|
||||||
|
'value' => TypeKind::LIST_KIND,
|
||||||
|
'description' => 'Indicates this type is a list. `ofType` is a valid field.',
|
||||||
|
],
|
||||||
|
'NON_NULL' => [
|
||||||
|
'value' => TypeKind::NON_NULL,
|
||||||
|
'description' => 'Indicates this type is a non-null. `ofType` is a valid field.',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$map['__TypeKind'];
|
||||||
|
}
|
||||||
|
|
||||||
public static function _field()
|
public static function _field()
|
||||||
{
|
{
|
||||||
if (! isset(self::$map['__Field'])) {
|
if (! isset(self::$map['__Field'])) {
|
||||||
|
|
||||||
self::$map['__Field'] = new ObjectType([
|
self::$map['__Field'] = new ObjectType([
|
||||||
'name' => '__Field',
|
'name' => '__Field',
|
||||||
'isIntrospection' => true,
|
'isIntrospection' => true,
|
||||||
@ -526,27 +439,28 @@ EOD;
|
|||||||
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))),
|
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))),
|
||||||
'resolve' => function (FieldDefinition $field) {
|
'resolve' => function (FieldDefinition $field) {
|
||||||
return empty($field->args) ? [] : $field->args;
|
return empty($field->args) ? [] : $field->args;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'type' => [
|
'type' => [
|
||||||
'type' => Type::nonNull(self::_type()),
|
'type' => Type::nonNull(self::_type()),
|
||||||
'resolve' => function (FieldDefinition $field) {
|
'resolve' => function (FieldDefinition $field) {
|
||||||
return $field->getType();
|
return $field->getType();
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'isDeprecated' => [
|
'isDeprecated' => [
|
||||||
'type' => Type::nonNull(Type::boolean()),
|
'type' => Type::nonNull(Type::boolean()),
|
||||||
'resolve' => function (FieldDefinition $field) {
|
'resolve' => function (FieldDefinition $field) {
|
||||||
return !!$field->deprecationReason;
|
return (bool) $field->deprecationReason;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'deprecationReason' => [
|
'deprecationReason' => [
|
||||||
'type' => Type::string()
|
'type' => Type::string(),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$map['__Field'];
|
return self::$map['__Field'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +482,7 @@ EOD;
|
|||||||
'type' => Type::nonNull(self::_type()),
|
'type' => Type::nonNull(self::_type()),
|
||||||
'resolve' => function ($value) {
|
'resolve' => function ($value) {
|
||||||
return method_exists($value, 'getType') ? $value->getType() : $value->type;
|
return method_exists($value, 'getType') ? $value->getType() : $value->type;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'defaultValue' => [
|
'defaultValue' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
@ -578,13 +492,17 @@ EOD;
|
|||||||
/** @var FieldArgument|InputObjectField $inputValue */
|
/** @var FieldArgument|InputObjectField $inputValue */
|
||||||
return ! $inputValue->defaultValueExists()
|
return ! $inputValue->defaultValueExists()
|
||||||
? null
|
? null
|
||||||
: Printer::doPrint(AST::astFromValue($inputValue->defaultValue, $inputValue->getType()));
|
: Printer::doPrint(AST::astFromValue(
|
||||||
}
|
$inputValue->defaultValue,
|
||||||
]
|
$inputValue->getType()
|
||||||
|
));
|
||||||
|
},
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$map['__InputValue'];
|
return self::$map['__InputValue'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,62 +522,168 @@ EOD;
|
|||||||
'isDeprecated' => [
|
'isDeprecated' => [
|
||||||
'type' => Type::nonNull(Type::boolean()),
|
'type' => Type::nonNull(Type::boolean()),
|
||||||
'resolve' => function ($enumValue) {
|
'resolve' => function ($enumValue) {
|
||||||
return !!$enumValue->deprecationReason;
|
return (bool) $enumValue->deprecationReason;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'deprecationReason' => [
|
'deprecationReason' => [
|
||||||
'type' => Type::string()
|
'type' => Type::string(),
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$map['__EnumValue'];
|
return self::$map['__EnumValue'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function _typeKind()
|
public static function _directive()
|
||||||
{
|
{
|
||||||
if (!isset(self::$map['__TypeKind'])) {
|
if (! isset(self::$map['__Directive'])) {
|
||||||
self::$map['__TypeKind'] = new EnumType([
|
self::$map['__Directive'] = new ObjectType([
|
||||||
'name' => '__TypeKind',
|
'name' => '__Directive',
|
||||||
'isIntrospection' => true,
|
'isIntrospection' => true,
|
||||||
'description' => 'An enum describing what kind of type a given `__Type` is.',
|
'description' => 'A Directive provides a way to describe alternate runtime execution and ' .
|
||||||
'values' => [
|
'type validation behavior in a GraphQL document.' .
|
||||||
'SCALAR' => [
|
"\n\nIn some cases, you need to provide options to alter GraphQL's " .
|
||||||
'value' => TypeKind::SCALAR,
|
'execution behavior in ways field arguments will not suffice, such as ' .
|
||||||
'description' => 'Indicates this type is a scalar.'
|
'conditionally including or skipping a field. Directives provide this by ' .
|
||||||
|
'describing additional information to the executor.',
|
||||||
|
'fields' => [
|
||||||
|
'name' => ['type' => Type::nonNull(Type::string())],
|
||||||
|
'description' => ['type' => Type::string()],
|
||||||
|
'locations' => [
|
||||||
|
'type' => Type::nonNull(Type::listOf(Type::nonNull(
|
||||||
|
self::_directiveLocation()
|
||||||
|
))),
|
||||||
],
|
],
|
||||||
'OBJECT' => [
|
'args' => [
|
||||||
'value' => TypeKind::OBJECT,
|
'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))),
|
||||||
'description' => 'Indicates this type is an object. `fields` and `interfaces` are valid fields.'
|
'resolve' => function (Directive $directive) {
|
||||||
|
return $directive->args ?: [];
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'INTERFACE' => [
|
|
||||||
'value' => TypeKind::INTERFACE_KIND,
|
// NOTE: the following three fields are deprecated and are no longer part
|
||||||
'description' => 'Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.'
|
// of the GraphQL specification.
|
||||||
|
'onOperation' => [
|
||||||
|
'deprecationReason' => 'Use `locations`.',
|
||||||
|
'type' => Type::nonNull(Type::boolean()),
|
||||||
|
'resolve' => function ($d) {
|
||||||
|
return in_array(DirectiveLocation::QUERY, $d->locations) ||
|
||||||
|
in_array(DirectiveLocation::MUTATION, $d->locations) ||
|
||||||
|
in_array(DirectiveLocation::SUBSCRIPTION, $d->locations);
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'UNION' => [
|
'onFragment' => [
|
||||||
'value' => TypeKind::UNION,
|
'deprecationReason' => 'Use `locations`.',
|
||||||
'description' => 'Indicates this type is a union. `possibleTypes` is a valid field.'
|
'type' => Type::nonNull(Type::boolean()),
|
||||||
|
'resolve' => function ($d) {
|
||||||
|
return in_array(DirectiveLocation::FRAGMENT_SPREAD, $d->locations) ||
|
||||||
|
in_array(DirectiveLocation::INLINE_FRAGMENT, $d->locations) ||
|
||||||
|
in_array(DirectiveLocation::FRAGMENT_DEFINITION, $d->locations);
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'ENUM' => [
|
'onField' => [
|
||||||
'value' => TypeKind::ENUM,
|
'deprecationReason' => 'Use `locations`.',
|
||||||
'description' => 'Indicates this type is an enum. `enumValues` is a valid field.'
|
'type' => Type::nonNull(Type::boolean()),
|
||||||
|
'resolve' => function ($d) {
|
||||||
|
return in_array(DirectiveLocation::FIELD, $d->locations);
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'INPUT_OBJECT' => [
|
|
||||||
'value' => TypeKind::INPUT_OBJECT,
|
|
||||||
'description' => 'Indicates this type is an input object. `inputFields` is a valid field.'
|
|
||||||
],
|
],
|
||||||
'LIST' => [
|
|
||||||
'value' => TypeKind::LIST_KIND,
|
|
||||||
'description' => 'Indicates this type is a list. `ofType` is a valid field.'
|
|
||||||
],
|
|
||||||
'NON_NULL' => [
|
|
||||||
'value' => TypeKind::NON_NULL,
|
|
||||||
'description' => 'Indicates this type is a non-null. `ofType` is a valid field.'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return self::$map['__TypeKind'];
|
|
||||||
|
return self::$map['__Directive'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function _directiveLocation()
|
||||||
|
{
|
||||||
|
if (! isset(self::$map['__DirectiveLocation'])) {
|
||||||
|
self::$map['__DirectiveLocation'] = new EnumType([
|
||||||
|
'name' => '__DirectiveLocation',
|
||||||
|
'isIntrospection' => true,
|
||||||
|
'description' =>
|
||||||
|
'A Directive can be adjacent to many parts of the GraphQL language, a ' .
|
||||||
|
'__DirectiveLocation describes one such possible adjacencies.',
|
||||||
|
'values' => [
|
||||||
|
'QUERY' => [
|
||||||
|
'value' => DirectiveLocation::QUERY,
|
||||||
|
'description' => 'Location adjacent to a query operation.',
|
||||||
|
],
|
||||||
|
'MUTATION' => [
|
||||||
|
'value' => DirectiveLocation::MUTATION,
|
||||||
|
'description' => 'Location adjacent to a mutation operation.',
|
||||||
|
],
|
||||||
|
'SUBSCRIPTION' => [
|
||||||
|
'value' => DirectiveLocation::SUBSCRIPTION,
|
||||||
|
'description' => 'Location adjacent to a subscription operation.',
|
||||||
|
],
|
||||||
|
'FIELD' => [
|
||||||
|
'value' => DirectiveLocation::FIELD,
|
||||||
|
'description' => 'Location adjacent to a field.',
|
||||||
|
],
|
||||||
|
'FRAGMENT_DEFINITION' => [
|
||||||
|
'value' => DirectiveLocation::FRAGMENT_DEFINITION,
|
||||||
|
'description' => 'Location adjacent to a fragment definition.',
|
||||||
|
],
|
||||||
|
'FRAGMENT_SPREAD' => [
|
||||||
|
'value' => DirectiveLocation::FRAGMENT_SPREAD,
|
||||||
|
'description' => 'Location adjacent to a fragment spread.',
|
||||||
|
],
|
||||||
|
'INLINE_FRAGMENT' => [
|
||||||
|
'value' => DirectiveLocation::INLINE_FRAGMENT,
|
||||||
|
'description' => 'Location adjacent to an inline fragment.',
|
||||||
|
],
|
||||||
|
'SCHEMA' => [
|
||||||
|
'value' => DirectiveLocation::SCHEMA,
|
||||||
|
'description' => 'Location adjacent to a schema definition.',
|
||||||
|
],
|
||||||
|
'SCALAR' => [
|
||||||
|
'value' => DirectiveLocation::SCALAR,
|
||||||
|
'description' => 'Location adjacent to a scalar definition.',
|
||||||
|
],
|
||||||
|
'OBJECT' => [
|
||||||
|
'value' => DirectiveLocation::OBJECT,
|
||||||
|
'description' => 'Location adjacent to an object type definition.',
|
||||||
|
],
|
||||||
|
'FIELD_DEFINITION' => [
|
||||||
|
'value' => DirectiveLocation::FIELD_DEFINITION,
|
||||||
|
'description' => 'Location adjacent to a field definition.',
|
||||||
|
],
|
||||||
|
'ARGUMENT_DEFINITION' => [
|
||||||
|
'value' => DirectiveLocation::ARGUMENT_DEFINITION,
|
||||||
|
'description' => 'Location adjacent to an argument definition.',
|
||||||
|
],
|
||||||
|
'INTERFACE' => [
|
||||||
|
'value' => DirectiveLocation::IFACE,
|
||||||
|
'description' => 'Location adjacent to an interface definition.',
|
||||||
|
],
|
||||||
|
'UNION' => [
|
||||||
|
'value' => DirectiveLocation::UNION,
|
||||||
|
'description' => 'Location adjacent to a union definition.',
|
||||||
|
],
|
||||||
|
'ENUM' => [
|
||||||
|
'value' => DirectiveLocation::ENUM,
|
||||||
|
'description' => 'Location adjacent to an enum definition.',
|
||||||
|
],
|
||||||
|
'ENUM_VALUE' => [
|
||||||
|
'value' => DirectiveLocation::ENUM_VALUE,
|
||||||
|
'description' => 'Location adjacent to an enum value definition.',
|
||||||
|
],
|
||||||
|
'INPUT_OBJECT' => [
|
||||||
|
'value' => DirectiveLocation::INPUT_OBJECT,
|
||||||
|
'description' => 'Location adjacent to an input object type definition.',
|
||||||
|
],
|
||||||
|
'INPUT_FIELD_DEFINITION' => [
|
||||||
|
'value' => DirectiveLocation::INPUT_FIELD_DEFINITION,
|
||||||
|
'description' => 'Location adjacent to an input object field definition.',
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$map['__DirectiveLocation'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function schemaMetaFieldDef()
|
public static function schemaMetaFieldDef()
|
||||||
@ -677,9 +701,10 @@ EOD;
|
|||||||
ResolveInfo $info
|
ResolveInfo $info
|
||||||
) {
|
) {
|
||||||
return $info->schema;
|
return $info->schema;
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$map['__schema'];
|
return self::$map['__schema'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,13 +716,14 @@ EOD;
|
|||||||
'type' => self::_type(),
|
'type' => self::_type(),
|
||||||
'description' => 'Request the type information of a single type.',
|
'description' => 'Request the type information of a single type.',
|
||||||
'args' => [
|
'args' => [
|
||||||
['name' => 'name', 'type' => Type::nonNull(Type::string())]
|
['name' => 'name', 'type' => Type::nonNull(Type::string())],
|
||||||
],
|
],
|
||||||
'resolve' => function ($source, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($source, $args, $context, ResolveInfo $info) {
|
||||||
return $info->schema->getType($args['name']);
|
return $info->schema->getType($args['name']);
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$map['__type'];
|
return self::$map['__type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -716,9 +742,10 @@ EOD;
|
|||||||
ResolveInfo $info
|
ResolveInfo $info
|
||||||
) {
|
) {
|
||||||
return $info->parentType->name;
|
return $info->parentType->name;
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$map['__typename'];
|
return self::$map['__typename'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type;
|
namespace GraphQL\Type;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
@ -6,29 +9,22 @@ use GraphQL\Type\Definition\AbstractType;
|
|||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function call_user_func;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EXPERIMENTAL!
|
* EXPERIMENTAL!
|
||||||
* This class can be removed or changed in future versions without a prior notice.
|
* This class can be removed or changed in future versions without a prior notice.
|
||||||
*
|
|
||||||
* Class LazyResolution
|
|
||||||
* @package GraphQL\Type
|
|
||||||
*/
|
*/
|
||||||
class LazyResolution implements Resolution
|
class LazyResolution implements Resolution
|
||||||
{
|
{
|
||||||
/**
|
/** @var int[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $typeMap;
|
private $typeMap;
|
||||||
|
|
||||||
/**
|
/** @var int[][] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $possibleTypeMap;
|
private $possibleTypeMap;
|
||||||
|
|
||||||
/**
|
/** @var callable */
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
private $typeLoader;
|
private $typeLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,14 +37,13 @@ class LazyResolution implements Resolution
|
|||||||
/**
|
/**
|
||||||
* Map of $interfaceTypeName => $objectType[]
|
* Map of $interfaceTypeName => $objectType[]
|
||||||
*
|
*
|
||||||
* @var array
|
* @var Type[][]
|
||||||
*/
|
*/
|
||||||
private $loadedPossibleTypes;
|
private $loadedPossibleTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LazyResolution constructor.
|
*
|
||||||
* @param array $descriptor
|
* @param mixed[] $descriptor
|
||||||
* @param callable $typeLoader
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $descriptor, callable $typeLoader)
|
public function __construct(array $descriptor, callable $typeLoader)
|
||||||
{
|
{
|
||||||
@ -66,28 +61,6 @@ class LazyResolution implements Resolution
|
|||||||
$this->loadedPossibleTypes = [];
|
$this->loadedPossibleTypes = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function resolveType($name)
|
|
||||||
{
|
|
||||||
if (!isset($this->typeMap[$name])) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!isset($this->loadedTypes[$name])) {
|
|
||||||
$type = call_user_func($this->typeLoader, $name);
|
|
||||||
if (!$type instanceof Type && null !== $type) {
|
|
||||||
throw new InvariantViolation(
|
|
||||||
"Lazy Type Resolution Error: Expecting GraphQL Type instance, but got " .
|
|
||||||
Utils::getVariableType($type)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->loadedTypes[$name] = $type;
|
|
||||||
}
|
|
||||||
return $this->loadedTypes[$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
@ -102,14 +75,42 @@ class LazyResolution implements Resolution
|
|||||||
$obj = $this->resolveType($typeName);
|
$obj = $this->resolveType($typeName);
|
||||||
if (! $obj instanceof ObjectType) {
|
if (! $obj instanceof ObjectType) {
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"Lazy Type Resolution Error: Implementation {$typeName} of interface {$type->name} " .
|
sprintf(
|
||||||
"is expected to be instance of ObjectType, but got " . Utils::getVariableType($obj)
|
'Lazy Type Resolution Error: Implementation %s of interface %s is expected to be instance of ObjectType, but got %s',
|
||||||
|
$typeName,
|
||||||
|
$type->name,
|
||||||
|
Utils::getVariableType($obj)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$tmp[] = $obj;
|
$tmp[] = $obj;
|
||||||
}
|
}
|
||||||
$this->loadedPossibleTypes[$type->name] = $tmp;
|
$this->loadedPossibleTypes[$type->name] = $tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->loadedPossibleTypes[$type->name];
|
return $this->loadedPossibleTypes[$type->name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function resolveType($name)
|
||||||
|
{
|
||||||
|
if (! isset($this->typeMap[$name])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (! isset($this->loadedTypes[$name])) {
|
||||||
|
$type = call_user_func($this->typeLoader, $name);
|
||||||
|
if (! $type instanceof Type && $type !== null) {
|
||||||
|
throw new InvariantViolation(
|
||||||
|
'Lazy Type Resolution Error: Expecting GraphQL Type instance, but got ' .
|
||||||
|
Utils::getVariableType($type)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loadedTypes[$name] = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->loadedTypes[$name];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type;
|
namespace GraphQL\Type;
|
||||||
|
|
||||||
use GraphQL\Type\Definition\AbstractType;
|
use GraphQL\Type\Definition\AbstractType;
|
||||||
@ -10,7 +13,6 @@ use GraphQL\Type\Definition\Type;
|
|||||||
* This interface can be removed or changed in future versions without a prior notice.
|
* This interface can be removed or changed in future versions without a prior notice.
|
||||||
*
|
*
|
||||||
* Interface Resolution
|
* Interface Resolution
|
||||||
* @package GraphQL\Type
|
|
||||||
*/
|
*/
|
||||||
interface Resolution
|
interface Resolution
|
||||||
{
|
{
|
||||||
@ -25,7 +27,6 @@ interface Resolution
|
|||||||
/**
|
/**
|
||||||
* Returns instances of possible ObjectTypes for given InterfaceType or UnionType
|
* Returns instances of possible ObjectTypes for given InterfaceType or UnionType
|
||||||
*
|
*
|
||||||
* @param AbstractType $type
|
|
||||||
* @return ObjectType[]
|
* @return ObjectType[]
|
||||||
*/
|
*/
|
||||||
public function resolvePossibleTypes(AbstractType $type);
|
public function resolvePossibleTypes(AbstractType $type);
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type;
|
namespace GraphQL\Type;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
@ -13,6 +16,11 @@ use GraphQL\Type\Definition\Type;
|
|||||||
use GraphQL\Type\Definition\UnionType;
|
use GraphQL\Type\Definition\UnionType;
|
||||||
use GraphQL\Utils\TypeInfo;
|
use GraphQL\Utils\TypeInfo;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function array_values;
|
||||||
|
use function implode;
|
||||||
|
use function is_array;
|
||||||
|
use function is_callable;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schema Definition (see [related docs](type-system/schema.md))
|
* Schema Definition (see [related docs](type-system/schema.md))
|
||||||
@ -33,14 +41,10 @@ use GraphQL\Utils\Utils;
|
|||||||
* ->setMutation($MyAppMutationRootType);
|
* ->setMutation($MyAppMutationRootType);
|
||||||
*
|
*
|
||||||
* $schema = new GraphQL\Type\Schema($config);
|
* $schema = new GraphQL\Type\Schema($config);
|
||||||
*
|
|
||||||
* @package GraphQL
|
|
||||||
*/
|
*/
|
||||||
class Schema
|
class Schema
|
||||||
{
|
{
|
||||||
/**
|
/** @var SchemaConfig */
|
||||||
* @var SchemaConfig
|
|
||||||
*/
|
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,9 +54,7 @@ class Schema
|
|||||||
*/
|
*/
|
||||||
private $resolvedTypes = [];
|
private $resolvedTypes = [];
|
||||||
|
|
||||||
/**
|
/** @var Type[][]|null */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $possibleTypeMap;
|
private $possibleTypeMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,16 +64,12 @@ class Schema
|
|||||||
*/
|
*/
|
||||||
private $fullyLoaded = false;
|
private $fullyLoaded = false;
|
||||||
|
|
||||||
/**
|
/** @var InvariantViolation[]|null */
|
||||||
* @var InvariantViolation[]|null
|
|
||||||
*/
|
|
||||||
private $validationErrors;
|
private $validationErrors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schema constructor.
|
|
||||||
*
|
|
||||||
* @api
|
* @api
|
||||||
* @param array|SchemaConfig $config
|
* @param mixed[]|SchemaConfig $config
|
||||||
*/
|
*/
|
||||||
public function __construct($config)
|
public function __construct($config)
|
||||||
{
|
{
|
||||||
@ -89,23 +87,26 @@ class Schema
|
|||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$config instanceof SchemaConfig,
|
$config instanceof SchemaConfig,
|
||||||
'Schema constructor expects instance of GraphQL\Type\SchemaConfig or an array with keys: %s; but got: %s',
|
'Schema constructor expects instance of GraphQL\Type\SchemaConfig or an array with keys: %s; but got: %s',
|
||||||
implode(', ', [
|
implode(
|
||||||
|
', ',
|
||||||
|
[
|
||||||
'query',
|
'query',
|
||||||
'mutation',
|
'mutation',
|
||||||
'subscription',
|
'subscription',
|
||||||
'types',
|
'types',
|
||||||
'directives',
|
'directives',
|
||||||
'typeLoader'
|
'typeLoader',
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
Utils::getVariableType($config)
|
Utils::getVariableType($config)
|
||||||
);
|
);
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
! $config->types || is_array($config->types) || is_callable($config->types),
|
! $config->types || is_array($config->types) || is_callable($config->types),
|
||||||
"\"types\" must be array or callable if provided but got: " . Utils::getVariableType($config->types)
|
'"types" must be array or callable if provided but got: ' . Utils::getVariableType($config->types)
|
||||||
);
|
);
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
! $config->directives || is_array($config->directives),
|
! $config->directives || is_array($config->directives),
|
||||||
"\"directives\" must be Array if provided but got: " . Utils::getVariableType($config->directives)
|
'"directives" must be Array if provided but got: ' . Utils::getVariableType($config->directives)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,8 +125,10 @@ class Schema
|
|||||||
if (isset($this->resolvedTypes[$type->name])) {
|
if (isset($this->resolvedTypes[$type->name])) {
|
||||||
Utils::invariant(
|
Utils::invariant(
|
||||||
$type === $this->resolvedTypes[$type->name],
|
$type === $this->resolvedTypes[$type->name],
|
||||||
"Schema must contain unique named types but contains multiple types named \"$type\" ".
|
sprintf(
|
||||||
"(see http://webonyx.github.io/graphql-php/type-system/#type-registry)."
|
'Schema must contain unique named types but contains multiple types named "%s" (see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
|
||||||
|
$type
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->resolvedTypes[$type->name] = $type;
|
$this->resolvedTypes[$type->name] = $type;
|
||||||
@ -133,10 +136,98 @@ class Schema
|
|||||||
}
|
}
|
||||||
$this->resolvedTypes += Type::getInternalTypes() + Introspection::getTypes();
|
$this->resolvedTypes += Type::getInternalTypes() + Introspection::getTypes();
|
||||||
|
|
||||||
if (!$this->config->typeLoader) {
|
if ($this->config->typeLoader) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Perform full scan of the schema
|
// Perform full scan of the schema
|
||||||
$this->getTypeMap();
|
$this->getTypeMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Generator
|
||||||
|
*/
|
||||||
|
private function resolveAdditionalTypes()
|
||||||
|
{
|
||||||
|
$types = $this->config->types ?: [];
|
||||||
|
|
||||||
|
if (is_callable($types)) {
|
||||||
|
$types = $types();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! is_array($types) && ! $types instanceof \Traversable) {
|
||||||
|
throw new InvariantViolation(sprintf(
|
||||||
|
'Schema types callable must return array or instance of Traversable but got: %s',
|
||||||
|
Utils::getVariableType($types)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($types as $index => $type) {
|
||||||
|
if (! $type instanceof Type) {
|
||||||
|
throw new InvariantViolation(sprintf(
|
||||||
|
'Each entry of schema types must be instance of GraphQL\Type\Definition\Type but entry at %s is %s',
|
||||||
|
$index,
|
||||||
|
Utils::printSafe($type)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
yield $type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns array of all types in this schema. Keys of this array represent type names, values are instances
|
||||||
|
* of corresponding type definitions
|
||||||
|
*
|
||||||
|
* This operation requires full schema scan. Do not use in production environment.
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @return Type[]
|
||||||
|
*/
|
||||||
|
public function getTypeMap()
|
||||||
|
{
|
||||||
|
if (! $this->fullyLoaded) {
|
||||||
|
$this->resolvedTypes = $this->collectAllTypes();
|
||||||
|
$this->fullyLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resolvedTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Type[]
|
||||||
|
*/
|
||||||
|
private function collectAllTypes()
|
||||||
|
{
|
||||||
|
$typeMap = [];
|
||||||
|
foreach ($this->resolvedTypes as $type) {
|
||||||
|
$typeMap = TypeInfo::extractTypes($type, $typeMap);
|
||||||
|
}
|
||||||
|
foreach ($this->getDirectives() as $directive) {
|
||||||
|
if (! ($directive instanceof Directive)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$typeMap = TypeInfo::extractTypesFromDirectives($directive, $typeMap);
|
||||||
|
}
|
||||||
|
// When types are set as array they are resolved in constructor
|
||||||
|
if (is_callable($this->config->types)) {
|
||||||
|
foreach ($this->resolveAdditionalTypes() as $type) {
|
||||||
|
$typeMap = TypeInfo::extractTypes($type, $typeMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $typeMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of directives supported by this schema
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @return Directive[]
|
||||||
|
*/
|
||||||
|
public function getDirectives()
|
||||||
|
{
|
||||||
|
return $this->config->directives ?: GraphQL::getStandardDirectives();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,24 +272,6 @@ class Schema
|
|||||||
return $this->config;
|
return $this->config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns array of all types in this schema. Keys of this array represent type names, values are instances
|
|
||||||
* of corresponding type definitions
|
|
||||||
*
|
|
||||||
* This operation requires full schema scan. Do not use in production environment.
|
|
||||||
*
|
|
||||||
* @api
|
|
||||||
* @return Type[]
|
|
||||||
*/
|
|
||||||
public function getTypeMap()
|
|
||||||
{
|
|
||||||
if (!$this->fullyLoaded) {
|
|
||||||
$this->resolvedTypes = $this->collectAllTypes();
|
|
||||||
$this->fullyLoaded = true;
|
|
||||||
}
|
|
||||||
return $this->resolvedTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns type by it's name
|
* Returns type by it's name
|
||||||
*
|
*
|
||||||
@ -215,104 +288,12 @@ class Schema
|
|||||||
}
|
}
|
||||||
$this->resolvedTypes[$name] = $type;
|
$this->resolvedTypes[$name] = $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolvedTypes[$name];
|
return $this->resolvedTypes[$name];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @param string $typeName
|
||||||
*/
|
|
||||||
private function collectAllTypes()
|
|
||||||
{
|
|
||||||
$typeMap = [];
|
|
||||||
foreach ($this->resolvedTypes as $type) {
|
|
||||||
$typeMap = TypeInfo::extractTypes($type, $typeMap);
|
|
||||||
}
|
|
||||||
foreach ($this->getDirectives() as $directive) {
|
|
||||||
if ($directive instanceof Directive) {
|
|
||||||
$typeMap = TypeInfo::extractTypesFromDirectives($directive, $typeMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// When types are set as array they are resolved in constructor
|
|
||||||
if (is_callable($this->config->types)) {
|
|
||||||
foreach ($this->resolveAdditionalTypes() as $type) {
|
|
||||||
$typeMap = TypeInfo::extractTypes($type, $typeMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $typeMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \Generator
|
|
||||||
*/
|
|
||||||
private function resolveAdditionalTypes()
|
|
||||||
{
|
|
||||||
$types = $this->config->types ?: [];
|
|
||||||
|
|
||||||
if (is_callable($types)) {
|
|
||||||
$types = $types();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_array($types) && !$types instanceof \Traversable) {
|
|
||||||
throw new InvariantViolation(sprintf(
|
|
||||||
'Schema types callable must return array or instance of Traversable but got: %s',
|
|
||||||
Utils::getVariableType($types)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($types as $index => $type) {
|
|
||||||
if (!$type instanceof Type) {
|
|
||||||
throw new InvariantViolation(sprintf(
|
|
||||||
'Each entry of schema types must be instance of GraphQL\Type\Definition\Type but entry at %s is %s',
|
|
||||||
$index,
|
|
||||||
Utils::printSafe($type)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
yield $type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all possible concrete types for given abstract type
|
|
||||||
* (implementations for interfaces and members of union type for unions)
|
|
||||||
*
|
|
||||||
* This operation requires full schema scan. Do not use in production environment.
|
|
||||||
*
|
|
||||||
* @api
|
|
||||||
* @param AbstractType $abstractType
|
|
||||||
* @return ObjectType[]
|
|
||||||
*/
|
|
||||||
public function getPossibleTypes(AbstractType $abstractType)
|
|
||||||
{
|
|
||||||
$possibleTypeMap = $this->getPossibleTypeMap();
|
|
||||||
return isset($possibleTypeMap[$abstractType->name]) ? array_values($possibleTypeMap[$abstractType->name]) : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getPossibleTypeMap()
|
|
||||||
{
|
|
||||||
if ($this->possibleTypeMap === null) {
|
|
||||||
$this->possibleTypeMap = [];
|
|
||||||
foreach ($this->getTypeMap() as $type) {
|
|
||||||
if ($type instanceof ObjectType) {
|
|
||||||
foreach ($type->getInterfaces() as $interface) {
|
|
||||||
if ($interface instanceof InterfaceType) {
|
|
||||||
$this->possibleTypeMap[$interface->name][$type->name] = $type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ($type instanceof UnionType) {
|
|
||||||
foreach ($type->getTypes() as $innerType) {
|
|
||||||
$this->possibleTypeMap[$type->name][$innerType->name] = $innerType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->possibleTypeMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $typeName
|
|
||||||
* @return Type
|
* @return Type
|
||||||
*/
|
*/
|
||||||
private function loadType($typeName)
|
private function loadType($typeName)
|
||||||
@ -327,25 +308,82 @@ class Schema
|
|||||||
|
|
||||||
if (! $type instanceof Type) {
|
if (! $type instanceof Type) {
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"Type loader is expected to return valid type \"$typeName\", but it returned " . Utils::printSafe($type)
|
sprintf(
|
||||||
|
'Type loader is expected to return valid type "%s", but it returned %s',
|
||||||
|
$typeName,
|
||||||
|
Utils::printSafe($type)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ($type->name !== $typeName) {
|
if ($type->name !== $typeName) {
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
"Type loader is expected to return type \"$typeName\", but it returned \"{$type->name}\""
|
sprintf('Type loader is expected to return type "%s", but it returned "%s"', $typeName, $type->name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $type;
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $typeName
|
||||||
|
* @return Type
|
||||||
|
*/
|
||||||
|
private function defaultTypeLoader($typeName)
|
||||||
|
{
|
||||||
|
// Default type loader simply fallbacks to collecting all types
|
||||||
|
$typeMap = $this->getTypeMap();
|
||||||
|
|
||||||
|
return $typeMap[$typeName] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all possible concrete types for given abstract type
|
||||||
|
* (implementations for interfaces and members of union type for unions)
|
||||||
|
*
|
||||||
|
* This operation requires full schema scan. Do not use in production environment.
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @return ObjectType[]
|
||||||
|
*/
|
||||||
|
public function getPossibleTypes(AbstractType $abstractType)
|
||||||
|
{
|
||||||
|
$possibleTypeMap = $this->getPossibleTypeMap();
|
||||||
|
|
||||||
|
return isset($possibleTypeMap[$abstractType->name]) ? array_values($possibleTypeMap[$abstractType->name]) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Type[][]
|
||||||
|
*/
|
||||||
|
private function getPossibleTypeMap()
|
||||||
|
{
|
||||||
|
if ($this->possibleTypeMap === null) {
|
||||||
|
$this->possibleTypeMap = [];
|
||||||
|
foreach ($this->getTypeMap() as $type) {
|
||||||
|
if ($type instanceof ObjectType) {
|
||||||
|
foreach ($type->getInterfaces() as $interface) {
|
||||||
|
if (! ($interface instanceof InterfaceType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->possibleTypeMap[$interface->name][$type->name] = $type;
|
||||||
|
}
|
||||||
|
} elseif ($type instanceof UnionType) {
|
||||||
|
foreach ($type->getTypes() as $innerType) {
|
||||||
|
$this->possibleTypeMap[$type->name][$innerType->name] = $innerType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->possibleTypeMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if object type is concrete type of given abstract type
|
* Returns true if object type is concrete type of given abstract type
|
||||||
* (implementation for interfaces and members of union type for unions)
|
* (implementation for interfaces and members of union type for unions)
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @param AbstractType $abstractType
|
|
||||||
* @param ObjectType $possibleType
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isPossibleType(AbstractType $abstractType, ObjectType $possibleType)
|
public function isPossibleType(AbstractType $abstractType, ObjectType $possibleType)
|
||||||
@ -358,22 +396,11 @@ class Schema
|
|||||||
return $abstractType->isPossibleType($possibleType);
|
return $abstractType->isPossibleType($possibleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of directives supported by this schema
|
|
||||||
*
|
|
||||||
* @api
|
|
||||||
* @return Directive[]
|
|
||||||
*/
|
|
||||||
public function getDirectives()
|
|
||||||
{
|
|
||||||
return $this->config->directives ?: GraphQL::getStandardDirectives();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns instance of directive by name
|
* Returns instance of directive by name
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @param $name
|
* @param string $name
|
||||||
* @return Directive
|
* @return Directive
|
||||||
*/
|
*/
|
||||||
public function getDirective($name)
|
public function getDirective($name)
|
||||||
@ -383,6 +410,7 @@ class Schema
|
|||||||
return $directive;
|
return $directive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,14 +423,42 @@ class Schema
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $typeName
|
* Validates schema.
|
||||||
* @return Type
|
*
|
||||||
|
* This operation requires full schema scan. Do not use in production environment.
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @throws InvariantViolation
|
||||||
*/
|
*/
|
||||||
private function defaultTypeLoader($typeName)
|
public function assertValid()
|
||||||
{
|
{
|
||||||
// Default type loader simply fallbacks to collecting all types
|
$errors = $this->validate();
|
||||||
$typeMap = $this->getTypeMap();
|
|
||||||
return isset($typeMap[$typeName]) ? $typeMap[$typeName] : null;
|
if ($errors) {
|
||||||
|
throw new InvariantViolation(implode("\n\n", $this->validationErrors));
|
||||||
|
}
|
||||||
|
|
||||||
|
$internalTypes = Type::getInternalTypes() + Introspection::getTypes();
|
||||||
|
foreach ($this->getTypeMap() as $name => $type) {
|
||||||
|
if (isset($internalTypes[$name])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type->assertValid();
|
||||||
|
|
||||||
|
// Make sure type loader returns the same instance as registered in other places of schema
|
||||||
|
if (! $this->config->typeLoader) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::invariant(
|
||||||
|
$this->loadType($name) === $type,
|
||||||
|
sprintf(
|
||||||
|
'Type loader returns different instance for %s than field/argument definitions. Make sure you always return the same instance for the same type name.',
|
||||||
|
$name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -431,39 +487,4 @@ class Schema
|
|||||||
|
|
||||||
return $this->validationErrors;
|
return $this->validationErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates schema.
|
|
||||||
*
|
|
||||||
* This operation requires full schema scan. Do not use in production environment.
|
|
||||||
*
|
|
||||||
* @api
|
|
||||||
* @throws InvariantViolation
|
|
||||||
*/
|
|
||||||
public function assertValid()
|
|
||||||
{
|
|
||||||
$errors = $this->validate();
|
|
||||||
|
|
||||||
if ($errors) {
|
|
||||||
throw new InvariantViolation(implode("\n\n", $this->validationErrors));
|
|
||||||
}
|
|
||||||
|
|
||||||
$internalTypes = Type::getInternalTypes() + Introspection::getTypes();
|
|
||||||
foreach ($this->getTypeMap() as $name => $type) {
|
|
||||||
if (isset($internalTypes[$name])) {
|
|
||||||
continue ;
|
|
||||||
}
|
|
||||||
|
|
||||||
$type->assertValid();
|
|
||||||
|
|
||||||
// Make sure type loader returns the same instance as registered in other places of schema
|
|
||||||
if ($this->config->typeLoader) {
|
|
||||||
Utils::invariant(
|
|
||||||
$this->loadType($name) === $type,
|
|
||||||
"Type loader returns different instance for {$name} than field/argument definitions. ".
|
|
||||||
'Make sure you always return the same instance for the same type name.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type;
|
namespace GraphQL\Type;
|
||||||
|
|
||||||
use GraphQL\Language\AST\SchemaDefinitionNode;
|
use GraphQL\Language\AST\SchemaDefinitionNode;
|
||||||
@ -6,6 +9,7 @@ use GraphQL\Type\Definition\Directive;
|
|||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function is_callable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schema configuration class.
|
* Schema configuration class.
|
||||||
@ -23,44 +27,28 @@ use GraphQL\Utils\Utils;
|
|||||||
*/
|
*/
|
||||||
class SchemaConfig
|
class SchemaConfig
|
||||||
{
|
{
|
||||||
/**
|
/** @var ObjectType */
|
||||||
* @var ObjectType
|
|
||||||
*/
|
|
||||||
public $query;
|
public $query;
|
||||||
|
|
||||||
/**
|
/** @var ObjectType */
|
||||||
* @var ObjectType
|
|
||||||
*/
|
|
||||||
public $mutation;
|
public $mutation;
|
||||||
|
|
||||||
/**
|
/** @var ObjectType */
|
||||||
* @var ObjectType
|
|
||||||
*/
|
|
||||||
public $subscription;
|
public $subscription;
|
||||||
|
|
||||||
/**
|
/** @var Type[]|callable */
|
||||||
* @var Type[]|callable
|
|
||||||
*/
|
|
||||||
public $types;
|
public $types;
|
||||||
|
|
||||||
/**
|
/** @var Directive[] */
|
||||||
* @var Directive[]
|
|
||||||
*/
|
|
||||||
public $directives;
|
public $directives;
|
||||||
|
|
||||||
/**
|
/** @var callable */
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
public $typeLoader;
|
public $typeLoader;
|
||||||
|
|
||||||
/**
|
/** @var SchemaDefinitionNode */
|
||||||
* @var SchemaDefinitionNode
|
|
||||||
*/
|
|
||||||
public $astNode;
|
public $astNode;
|
||||||
|
|
||||||
/**
|
/** @var bool */
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public $assumeValid;
|
public $assumeValid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,7 +56,7 @@ class SchemaConfig
|
|||||||
* (or just returns empty config when array is not passed).
|
* (or just returns empty config when array is not passed).
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @param array $options
|
* @param mixed[] $options
|
||||||
* @return SchemaConfig
|
* @return SchemaConfig
|
||||||
*/
|
*/
|
||||||
public static function create(array $options = [])
|
public static function create(array $options = [])
|
||||||
@ -126,88 +114,12 @@ class SchemaConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SchemaDefinitionNode $astNode
|
|
||||||
* @return SchemaConfig
|
* @return SchemaConfig
|
||||||
*/
|
*/
|
||||||
public function setAstNode(SchemaDefinitionNode $astNode)
|
public function setAstNode(SchemaDefinitionNode $astNode)
|
||||||
{
|
{
|
||||||
$this->astNode = $astNode;
|
$this->astNode = $astNode;
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @api
|
|
||||||
* @param ObjectType $query
|
|
||||||
* @return SchemaConfig
|
|
||||||
*/
|
|
||||||
public function setQuery($query)
|
|
||||||
{
|
|
||||||
$this->query = $query;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @api
|
|
||||||
* @param ObjectType $mutation
|
|
||||||
* @return SchemaConfig
|
|
||||||
*/
|
|
||||||
public function setMutation($mutation)
|
|
||||||
{
|
|
||||||
$this->mutation = $mutation;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @api
|
|
||||||
* @param ObjectType $subscription
|
|
||||||
* @return SchemaConfig
|
|
||||||
*/
|
|
||||||
public function setSubscription($subscription)
|
|
||||||
{
|
|
||||||
$this->subscription = $subscription;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @api
|
|
||||||
* @param Type[]|callable $types
|
|
||||||
* @return SchemaConfig
|
|
||||||
*/
|
|
||||||
public function setTypes($types)
|
|
||||||
{
|
|
||||||
$this->types = $types;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @api
|
|
||||||
* @param Directive[] $directives
|
|
||||||
* @return SchemaConfig
|
|
||||||
*/
|
|
||||||
public function setDirectives(array $directives)
|
|
||||||
{
|
|
||||||
$this->directives = $directives;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @api
|
|
||||||
* @param callable $typeLoader
|
|
||||||
* @return SchemaConfig
|
|
||||||
*/
|
|
||||||
public function setTypeLoader(callable $typeLoader)
|
|
||||||
{
|
|
||||||
$this->typeLoader = $typeLoader;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param bool $assumeValid
|
|
||||||
* @return SchemaConfig
|
|
||||||
*/
|
|
||||||
public function setAssumeValid($assumeValid)
|
|
||||||
{
|
|
||||||
$this->assumeValid = $assumeValid;
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +132,18 @@ class SchemaConfig
|
|||||||
return $this->query;
|
return $this->query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @param ObjectType $query
|
||||||
|
* @return SchemaConfig
|
||||||
|
*/
|
||||||
|
public function setQuery($query)
|
||||||
|
{
|
||||||
|
$this->query = $query;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @return ObjectType
|
* @return ObjectType
|
||||||
@ -229,6 +153,18 @@ class SchemaConfig
|
|||||||
return $this->mutation;
|
return $this->mutation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @param ObjectType $mutation
|
||||||
|
* @return SchemaConfig
|
||||||
|
*/
|
||||||
|
public function setMutation($mutation)
|
||||||
|
{
|
||||||
|
$this->mutation = $mutation;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @return ObjectType
|
* @return ObjectType
|
||||||
@ -238,6 +174,18 @@ class SchemaConfig
|
|||||||
return $this->subscription;
|
return $this->subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @param ObjectType $subscription
|
||||||
|
* @return SchemaConfig
|
||||||
|
*/
|
||||||
|
public function setSubscription($subscription)
|
||||||
|
{
|
||||||
|
$this->subscription = $subscription;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @return Type[]
|
* @return Type[]
|
||||||
@ -247,6 +195,18 @@ class SchemaConfig
|
|||||||
return $this->types ?: [];
|
return $this->types ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @param Type[]|callable $types
|
||||||
|
* @return SchemaConfig
|
||||||
|
*/
|
||||||
|
public function setTypes($types)
|
||||||
|
{
|
||||||
|
$this->types = $types;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @return Directive[]
|
* @return Directive[]
|
||||||
@ -256,6 +216,18 @@ class SchemaConfig
|
|||||||
return $this->directives ?: [];
|
return $this->directives ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @param Directive[] $directives
|
||||||
|
* @return SchemaConfig
|
||||||
|
*/
|
||||||
|
public function setDirectives(array $directives)
|
||||||
|
{
|
||||||
|
$this->directives = $directives;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api
|
* @api
|
||||||
* @return callable
|
* @return callable
|
||||||
@ -265,6 +237,17 @@ class SchemaConfig
|
|||||||
return $this->typeLoader;
|
return $this->typeLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api
|
||||||
|
* @return SchemaConfig
|
||||||
|
*/
|
||||||
|
public function setTypeLoader(callable $typeLoader)
|
||||||
|
{
|
||||||
|
$this->typeLoader = $typeLoader;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
@ -272,4 +255,15 @@ class SchemaConfig
|
|||||||
{
|
{
|
||||||
return $this->assumeValid;
|
return $this->assumeValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $assumeValid
|
||||||
|
* @return SchemaConfig
|
||||||
|
*/
|
||||||
|
public function setAssumeValid($assumeValid)
|
||||||
|
{
|
||||||
|
$this->assumeValid = $assumeValid;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Type;
|
namespace GraphQL\Type;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
@ -28,17 +31,21 @@ use GraphQL\Type\Definition\Type;
|
|||||||
use GraphQL\Type\Definition\UnionType;
|
use GraphQL\Type\Definition\UnionType;
|
||||||
use GraphQL\Utils\TypeComparators;
|
use GraphQL\Utils\TypeComparators;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use function array_filter;
|
||||||
|
use function array_key_exists;
|
||||||
|
use function array_merge;
|
||||||
|
use function count;
|
||||||
|
use function is_array;
|
||||||
|
use function is_object;
|
||||||
|
use function iterator_to_array;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
class SchemaValidationContext
|
class SchemaValidationContext
|
||||||
{
|
{
|
||||||
/**
|
/** @var Error[] */
|
||||||
* @var Error[]
|
|
||||||
*/
|
|
||||||
private $errors = [];
|
private $errors = [];
|
||||||
|
|
||||||
/**
|
/** @var Schema */
|
||||||
* @var Schema
|
|
||||||
*/
|
|
||||||
private $schema;
|
private $schema;
|
||||||
|
|
||||||
public function __construct(Schema $schema)
|
public function __construct(Schema $schema)
|
||||||
@ -78,12 +85,32 @@ class SchemaValidationContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
$subscriptionType = $this->schema->getSubscriptionType();
|
$subscriptionType = $this->schema->getSubscriptionType();
|
||||||
if ($subscriptionType && !$subscriptionType instanceof ObjectType) {
|
if (! $subscriptionType || $subscriptionType instanceof ObjectType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
'Subscription root type must be Object type if provided, it cannot be ' . Utils::printSafe($subscriptionType) . '.',
|
'Subscription root type must be Object type if provided, it cannot be ' . Utils::printSafe($subscriptionType) . '.',
|
||||||
$this->getOperationTypeNode($subscriptionType, 'subscription')
|
$this->getOperationTypeNode($subscriptionType, 'subscription')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $message
|
||||||
|
* @param Node[]|Node|TypeNode|TypeDefinitionNode|null $nodes
|
||||||
|
*/
|
||||||
|
private function reportError($message, $nodes = null)
|
||||||
|
{
|
||||||
|
$nodes = array_filter($nodes && is_array($nodes) ? $nodes : [$nodes]);
|
||||||
|
$this->addError(new Error($message, $nodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Error $error
|
||||||
|
*/
|
||||||
|
private function addError($error)
|
||||||
|
{
|
||||||
|
$this->errors[] = $error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,7 +145,7 @@ class SchemaValidationContext
|
|||||||
// Ensure all directives are in fact GraphQL directives.
|
// Ensure all directives are in fact GraphQL directives.
|
||||||
if (! $directive instanceof Directive) {
|
if (! $directive instanceof Directive) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Expected directive but got: " . Utils::printSafe($directive) . '.',
|
'Expected directive but got: ' . Utils::printSafe($directive) . '.',
|
||||||
is_object($directive) ? $directive->astNode : null
|
is_object($directive) ? $directive->astNode : null
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -138,7 +165,7 @@ class SchemaValidationContext
|
|||||||
|
|
||||||
if (isset($argNames[$argName])) {
|
if (isset($argNames[$argName])) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Argument @{$directive->name}({$argName}:) can only be defined once.",
|
sprintf('Argument @%s(%s:) can only be defined once.', $directive->name, $argName),
|
||||||
$this->getAllDirectiveArgNodes($directive, $argName)
|
$this->getAllDirectiveArgNodes($directive, $argName)
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -147,16 +174,22 @@ class SchemaValidationContext
|
|||||||
$argNames[$argName] = true;
|
$argNames[$argName] = true;
|
||||||
|
|
||||||
// Ensure the type is an input type.
|
// Ensure the type is an input type.
|
||||||
if (!Type::isInputType($arg->getType())) {
|
if (Type::isInputType($arg->getType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"The type of @{$directive->name}({$argName}:) must be Input Type " .
|
sprintf(
|
||||||
'but got: ' . Utils::printSafe($arg->getType()) . '.',
|
'The type of @%s(%s:) must be Input Type but got: %s.',
|
||||||
|
$directive->name,
|
||||||
|
$argName,
|
||||||
|
Utils::printSafe($arg->getType())
|
||||||
|
),
|
||||||
$this->getDirectiveArgTypeNode($directive, $argName)
|
$this->getDirectiveArgTypeNode($directive, $argName)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Type|Directive|FieldDefinition|EnumValueDefinition|InputObjectField $node
|
* @param Type|Directive|FieldDefinition|EnumValueDefinition|InputObjectField $node
|
||||||
@ -165,9 +198,43 @@ class SchemaValidationContext
|
|||||||
{
|
{
|
||||||
// Ensure names are valid, however introspection types opt out.
|
// Ensure names are valid, however introspection types opt out.
|
||||||
$error = Utils::isValidNameError($node->name, $node->astNode);
|
$error = Utils::isValidNameError($node->name, $node->astNode);
|
||||||
if ($error && !Introspection::isIntrospectionType($node)) {
|
if (! $error || Introspection::isIntrospectionType($node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->addError($error);
|
$this->addError($error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $argName
|
||||||
|
* @return InputValueDefinitionNode[]
|
||||||
|
*/
|
||||||
|
private function getAllDirectiveArgNodes(Directive $directive, $argName)
|
||||||
|
{
|
||||||
|
$argNodes = [];
|
||||||
|
$directiveNode = $directive->astNode;
|
||||||
|
if ($directiveNode && $directiveNode->arguments) {
|
||||||
|
foreach ($directiveNode->arguments as $node) {
|
||||||
|
if ($node->name->value !== $argName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$argNodes[] = $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $argNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $argName
|
||||||
|
* @return TypeNode|null
|
||||||
|
*/
|
||||||
|
private function getDirectiveArgTypeNode(Directive $directive, $argName)
|
||||||
|
{
|
||||||
|
$argNode = $this->getAllDirectiveArgNodes($directive, $argName)[0];
|
||||||
|
|
||||||
|
return $argNode ? $argNode->type : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateTypes()
|
public function validateTypes()
|
||||||
@ -177,7 +244,7 @@ class SchemaValidationContext
|
|||||||
// Ensure all provided types are in fact GraphQL type.
|
// Ensure all provided types are in fact GraphQL type.
|
||||||
if (! $type instanceof NamedType) {
|
if (! $type instanceof NamedType) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Expected GraphQL named type but got: " . Utils::printSafe($type) . '.',
|
'Expected GraphQL named type but got: ' . Utils::printSafe($type) . '.',
|
||||||
is_object($type) ? $type->astNode : null
|
is_object($type) ? $type->astNode : null
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -217,7 +284,7 @@ class SchemaValidationContext
|
|||||||
// Objects and Interfaces both must define one or more fields.
|
// Objects and Interfaces both must define one or more fields.
|
||||||
if (! $fieldMap) {
|
if (! $fieldMap) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Type {$type->name} must define one or more fields.",
|
sprintf('Type %s must define one or more fields.', $type->name),
|
||||||
$this->getAllObjectOrInterfaceNodes($type)
|
$this->getAllObjectOrInterfaceNodes($type)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -230,7 +297,7 @@ class SchemaValidationContext
|
|||||||
$fieldNodes = $this->getAllFieldNodes($type, $fieldName);
|
$fieldNodes = $this->getAllFieldNodes($type, $fieldName);
|
||||||
if ($fieldNodes && count($fieldNodes) > 1) {
|
if ($fieldNodes && count($fieldNodes) > 1) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Field {$type->name}.{$fieldName} can only be defined once.",
|
sprintf('Field %s.%s can only be defined once.', $type->name, $fieldName),
|
||||||
$fieldNodes
|
$fieldNodes
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -239,8 +306,12 @@ class SchemaValidationContext
|
|||||||
// Ensure the type is an output type
|
// Ensure the type is an output type
|
||||||
if (! Type::isOutputType($field->getType())) {
|
if (! Type::isOutputType($field->getType())) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"The type of {$type->name}.{$fieldName} must be Output Type " .
|
sprintf(
|
||||||
'but got: ' . Utils::printSafe($field->getType()) . '.',
|
'The type of %s.%s must be Output Type but got: %s.',
|
||||||
|
$type->name,
|
||||||
|
$fieldName,
|
||||||
|
Utils::printSafe($field->getType())
|
||||||
|
),
|
||||||
$this->getFieldTypeNode($type, $fieldName)
|
$this->getFieldTypeNode($type, $fieldName)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -255,23 +326,146 @@ class SchemaValidationContext
|
|||||||
|
|
||||||
if (isset($argNames[$argName])) {
|
if (isset($argNames[$argName])) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Field argument {$type->name}.{$fieldName}({$argName}:) can only " .
|
sprintf(
|
||||||
'be defined once.',
|
'Field argument %s.%s(%s:) can only be defined once.',
|
||||||
|
$type->name,
|
||||||
|
$fieldName,
|
||||||
|
$argName
|
||||||
|
),
|
||||||
$this->getAllFieldArgNodes($type, $fieldName, $argName)
|
$this->getAllFieldArgNodes($type, $fieldName, $argName)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$argNames[$argName] = true;
|
$argNames[$argName] = true;
|
||||||
|
|
||||||
// Ensure the type is an input type
|
// Ensure the type is an input type
|
||||||
if (!Type::isInputType($arg->getType())) {
|
if (Type::isInputType($arg->getType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"The type of {$type->name}.{$fieldName}({$argName}:) must be Input " .
|
sprintf(
|
||||||
'Type but got: '. Utils::printSafe($arg->getType()) . '.',
|
'The type of %s.%s(%s:) must be Input Type but got: %s.',
|
||||||
|
$type->name,
|
||||||
|
$fieldName,
|
||||||
|
$argName,
|
||||||
|
Utils::printSafe($arg->getType())
|
||||||
|
),
|
||||||
$this->getFieldArgTypeNode($type, $fieldName, $argName)
|
$this->getFieldArgTypeNode($type, $fieldName, $argName)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectType|InterfaceType $type
|
||||||
|
* @return ObjectTypeDefinitionNode[]|ObjectTypeExtensionNode[]|InterfaceTypeDefinitionNode[]|InterfaceTypeExtensionNode[]
|
||||||
|
*/
|
||||||
|
private function getAllObjectOrInterfaceNodes($type)
|
||||||
|
{
|
||||||
|
return $type->astNode
|
||||||
|
? ($type->extensionASTNodes
|
||||||
|
? array_merge([$type->astNode], $type->extensionASTNodes)
|
||||||
|
: [$type->astNode])
|
||||||
|
: ($type->extensionASTNodes ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectType|InterfaceType $type
|
||||||
|
* @param string $fieldName
|
||||||
|
* @return FieldDefinitionNode[]
|
||||||
|
*/
|
||||||
|
private function getAllFieldNodes($type, $fieldName)
|
||||||
|
{
|
||||||
|
$fieldNodes = [];
|
||||||
|
$astNodes = $this->getAllObjectOrInterfaceNodes($type);
|
||||||
|
foreach ($astNodes as $astNode) {
|
||||||
|
if (! $astNode || ! $astNode->fields) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($astNode->fields as $node) {
|
||||||
|
if ($node->name->value !== $fieldName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fieldNodes[] = $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fieldNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectType|InterfaceType $type
|
||||||
|
* @param string $fieldName
|
||||||
|
* @return TypeNode|null
|
||||||
|
*/
|
||||||
|
private function getFieldTypeNode($type, $fieldName)
|
||||||
|
{
|
||||||
|
$fieldNode = $this->getFieldNode($type, $fieldName);
|
||||||
|
|
||||||
|
return $fieldNode ? $fieldNode->type : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectType|InterfaceType $type
|
||||||
|
* @param string $fieldName
|
||||||
|
* @return FieldDefinitionNode|null
|
||||||
|
*/
|
||||||
|
private function getFieldNode($type, $fieldName)
|
||||||
|
{
|
||||||
|
$nodes = $this->getAllFieldNodes($type, $fieldName);
|
||||||
|
|
||||||
|
return $nodes[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectType|InterfaceType $type
|
||||||
|
* @param string $fieldName
|
||||||
|
* @param string $argName
|
||||||
|
* @return InputValueDefinitionNode[]
|
||||||
|
*/
|
||||||
|
private function getAllFieldArgNodes($type, $fieldName, $argName)
|
||||||
|
{
|
||||||
|
$argNodes = [];
|
||||||
|
$fieldNode = $this->getFieldNode($type, $fieldName);
|
||||||
|
if ($fieldNode && $fieldNode->arguments) {
|
||||||
|
foreach ($fieldNode->arguments as $node) {
|
||||||
|
if ($node->name->value !== $argName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$argNodes[] = $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $argNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectType|InterfaceType $type
|
||||||
|
* @param string $fieldName
|
||||||
|
* @param string $argName
|
||||||
|
* @return TypeNode|null
|
||||||
|
*/
|
||||||
|
private function getFieldArgTypeNode($type, $fieldName, $argName)
|
||||||
|
{
|
||||||
|
$fieldArgNode = $this->getFieldArgNode($type, $fieldName, $argName);
|
||||||
|
|
||||||
|
return $fieldArgNode ? $fieldArgNode->type : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ObjectType|InterfaceType $type
|
||||||
|
* @param string $fieldName
|
||||||
|
* @param string $argName
|
||||||
|
* @return InputValueDefinitionNode|null
|
||||||
|
*/
|
||||||
|
private function getFieldArgNode($type, $fieldName, $argName)
|
||||||
|
{
|
||||||
|
$nodes = $this->getAllFieldArgNodes($type, $fieldName, $argName);
|
||||||
|
|
||||||
|
return $nodes[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validateObjectInterfaces(ObjectType $object)
|
private function validateObjectInterfaces(ObjectType $object)
|
||||||
@ -281,8 +475,7 @@ class SchemaValidationContext
|
|||||||
if (! $iface instanceof InterfaceType) {
|
if (! $iface instanceof InterfaceType) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
sprintf(
|
sprintf(
|
||||||
'Type %s must only implement Interface types, ' .
|
'Type %s must only implement Interface types, it cannot implement %s.',
|
||||||
'it cannot implement %s.',
|
|
||||||
$object->name,
|
$object->name,
|
||||||
Utils::printSafe($iface)
|
Utils::printSafe($iface)
|
||||||
),
|
),
|
||||||
@ -292,7 +485,7 @@ class SchemaValidationContext
|
|||||||
}
|
}
|
||||||
if (isset($implementedTypeNames[$iface->name])) {
|
if (isset($implementedTypeNames[$iface->name])) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Type {$object->name} can only implement {$iface->name} once.",
|
sprintf('Type %s can only implement %s once.', $object->name, $iface->name),
|
||||||
$this->getAllImplementsInterfaceNodes($object, $iface)
|
$this->getAllImplementsInterfaceNodes($object, $iface)
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -303,7 +496,43 @@ class SchemaValidationContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ObjectType $object
|
* @param InterfaceType $iface
|
||||||
|
* @return NamedTypeNode|null
|
||||||
|
*/
|
||||||
|
private function getImplementsInterfaceNode(ObjectType $type, $iface)
|
||||||
|
{
|
||||||
|
$nodes = $this->getAllImplementsInterfaceNodes($type, $iface);
|
||||||
|
|
||||||
|
return $nodes[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InterfaceType $iface
|
||||||
|
* @return NamedTypeNode[]
|
||||||
|
*/
|
||||||
|
private function getAllImplementsInterfaceNodes(ObjectType $type, $iface)
|
||||||
|
{
|
||||||
|
$implementsNodes = [];
|
||||||
|
$astNodes = $this->getAllObjectOrInterfaceNodes($type);
|
||||||
|
|
||||||
|
foreach ($astNodes as $astNode) {
|
||||||
|
if (! $astNode || ! $astNode->interfaces) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($astNode->interfaces as $node) {
|
||||||
|
if ($node->name->value !== $iface->name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$implementsNodes[] = $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $implementsNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @param InterfaceType $iface
|
* @param InterfaceType $iface
|
||||||
*/
|
*/
|
||||||
private function validateObjectImplementsInterface(ObjectType $object, $iface)
|
private function validateObjectImplementsInterface(ObjectType $object, $iface)
|
||||||
@ -320,8 +549,12 @@ class SchemaValidationContext
|
|||||||
// Assert interface field exists on object.
|
// Assert interface field exists on object.
|
||||||
if (! $objectField) {
|
if (! $objectField) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Interface field {$iface->name}.{$fieldName} expected but " .
|
sprintf(
|
||||||
"{$object->name} does not provide it.",
|
'Interface field %s.%s expected but %s does not provide it.',
|
||||||
|
$iface->name,
|
||||||
|
$fieldName,
|
||||||
|
$object->name
|
||||||
|
),
|
||||||
[$this->getFieldNode($iface, $fieldName), $object->astNode]
|
[$this->getFieldNode($iface, $fieldName), $object->astNode]
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -329,17 +562,22 @@ class SchemaValidationContext
|
|||||||
|
|
||||||
// Assert interface field type is satisfied by object field type, by being
|
// Assert interface field type is satisfied by object field type, by being
|
||||||
// a valid subtype. (covariant)
|
// a valid subtype. (covariant)
|
||||||
if (
|
if (! TypeComparators::isTypeSubTypeOf(
|
||||||
!TypeComparators::isTypeSubTypeOf(
|
|
||||||
$this->schema,
|
$this->schema,
|
||||||
$objectField->getType(),
|
$objectField->getType(),
|
||||||
$ifaceField->getType()
|
$ifaceField->getType()
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Interface field {$iface->name}.{$fieldName} expects type ".
|
sprintf(
|
||||||
"{$ifaceField->getType()} but {$object->name}.{$fieldName} " .
|
'Interface field %s.%s expects type %s but %s.%s is type %s.',
|
||||||
"is type " . Utils::printSafe($objectField->getType()) . ".",
|
$iface->name,
|
||||||
|
$fieldName,
|
||||||
|
$ifaceField->getType(),
|
||||||
|
$object->name,
|
||||||
|
$fieldName,
|
||||||
|
Utils::printSafe($objectField->getType())
|
||||||
|
),
|
||||||
[
|
[
|
||||||
$this->getFieldTypeNode($iface, $fieldName),
|
$this->getFieldTypeNode($iface, $fieldName),
|
||||||
$this->getFieldTypeNode($object, $fieldName),
|
$this->getFieldTypeNode($object, $fieldName),
|
||||||
@ -362,8 +600,14 @@ class SchemaValidationContext
|
|||||||
// Assert interface field arg exists on object field.
|
// Assert interface field arg exists on object field.
|
||||||
if (! $objectArg) {
|
if (! $objectArg) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Interface field argument {$iface->name}.{$fieldName}({$argName}:) " .
|
sprintf(
|
||||||
"expected but {$object->name}.{$fieldName} does not provide it.",
|
'Interface field argument %s.%s(%s:) expected but %s.%s does not provide it.',
|
||||||
|
$iface->name,
|
||||||
|
$fieldName,
|
||||||
|
$argName,
|
||||||
|
$object->name,
|
||||||
|
$fieldName
|
||||||
|
),
|
||||||
[
|
[
|
||||||
$this->getFieldArgNode($iface, $fieldName, $argName),
|
$this->getFieldArgNode($iface, $fieldName, $argName),
|
||||||
$this->getFieldNode($object, $fieldName),
|
$this->getFieldNode($object, $fieldName),
|
||||||
@ -377,17 +621,23 @@ class SchemaValidationContext
|
|||||||
// TODO: change to contravariant?
|
// TODO: change to contravariant?
|
||||||
if (! TypeComparators::isEqualType($ifaceArg->getType(), $objectArg->getType())) {
|
if (! TypeComparators::isEqualType($ifaceArg->getType(), $objectArg->getType())) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Interface field argument {$iface->name}.{$fieldName}({$argName}:) ".
|
sprintf(
|
||||||
"expects type " . Utils::printSafe($ifaceArg->getType()) . " but " .
|
'Interface field argument %s.%s(%s:) expects type %s but %s.%s(%s:) is type %s.',
|
||||||
"{$object->name}.{$fieldName}({$argName}:) is type " .
|
$iface->name,
|
||||||
Utils::printSafe($objectArg->getType()) . ".",
|
$fieldName,
|
||||||
|
$argName,
|
||||||
|
Utils::printSafe($ifaceArg->getType()),
|
||||||
|
$object->name,
|
||||||
|
$fieldName,
|
||||||
|
$argName,
|
||||||
|
Utils::printSafe($objectArg->getType())
|
||||||
|
),
|
||||||
[
|
[
|
||||||
$this->getFieldArgTypeNode($iface, $fieldName, $argName),
|
$this->getFieldArgTypeNode($iface, $fieldName, $argName),
|
||||||
$this->getFieldArgTypeNode($object, $fieldName, $argName),
|
$this->getFieldArgTypeNode($object, $fieldName, $argName),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: validate default values?
|
// TODO: validate default values?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,11 +653,20 @@ class SchemaValidationContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$ifaceArg && $objectArg->getType() instanceof NonNull) {
|
if ($ifaceArg || ! ($objectArg->getType() instanceof NonNull)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Object field argument {$object->name}.{$fieldName}({$argName}:) " .
|
sprintf(
|
||||||
"is of required type " . Utils::printSafe($objectArg->getType()) . " but is not also " .
|
'Object field argument %s.%s(%s:) is of required type %s but is not also provided by the Interface field %s.%s.',
|
||||||
"provided by the Interface field {$iface->name}.{$fieldName}.",
|
$object->name,
|
||||||
|
$fieldName,
|
||||||
|
$argName,
|
||||||
|
Utils::printSafe($objectArg->getType()),
|
||||||
|
$iface->name,
|
||||||
|
$fieldName
|
||||||
|
),
|
||||||
[
|
[
|
||||||
$this->getFieldArgTypeNode($object, $fieldName, $argName),
|
$this->getFieldArgTypeNode($object, $fieldName, $argName),
|
||||||
$this->getFieldNode($iface, $fieldName),
|
$this->getFieldNode($iface, $fieldName),
|
||||||
@ -416,7 +675,6 @@ class SchemaValidationContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function validateUnionMembers(UnionType $union)
|
private function validateUnionMembers(UnionType $union)
|
||||||
{
|
{
|
||||||
@ -424,7 +682,7 @@ class SchemaValidationContext
|
|||||||
|
|
||||||
if (! $memberTypes) {
|
if (! $memberTypes) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Union type {$union->name} must define one or more member types.",
|
sprintf('Union type %s must define one or more member types.', $union->name),
|
||||||
$union->astNode
|
$union->astNode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -434,253 +692,28 @@ class SchemaValidationContext
|
|||||||
foreach ($memberTypes as $memberType) {
|
foreach ($memberTypes as $memberType) {
|
||||||
if (isset($includedTypeNames[$memberType->name])) {
|
if (isset($includedTypeNames[$memberType->name])) {
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Union type {$union->name} can only include type ".
|
sprintf('Union type %s can only include type %s once.', $union->name, $memberType->name),
|
||||||
"{$memberType->name} once.",
|
|
||||||
$this->getUnionMemberTypeNodes($union, $memberType->name)
|
$this->getUnionMemberTypeNodes($union, $memberType->name)
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$includedTypeNames[$memberType->name] = true;
|
$includedTypeNames[$memberType->name] = true;
|
||||||
if (!$memberType instanceof ObjectType) {
|
if ($memberType instanceof ObjectType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$this->reportError(
|
$this->reportError(
|
||||||
"Union type {$union->name} can only include Object types, ".
|
sprintf(
|
||||||
"it cannot include " . Utils::printSafe($memberType) . ".",
|
'Union type %s can only include Object types, it cannot include %s.',
|
||||||
|
$union->name,
|
||||||
|
Utils::printSafe($memberType)
|
||||||
|
),
|
||||||
$this->getUnionMemberTypeNodes($union, Utils::printSafe($memberType))
|
$this->getUnionMemberTypeNodes($union, Utils::printSafe($memberType))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function validateEnumValues(EnumType $enumType)
|
|
||||||
{
|
|
||||||
$enumValues = $enumType->getValues();
|
|
||||||
|
|
||||||
if (!$enumValues) {
|
|
||||||
$this->reportError(
|
|
||||||
"Enum type {$enumType->name} must define one or more values.",
|
|
||||||
$enumType->astNode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($enumValues as $enumValue) {
|
|
||||||
$valueName = $enumValue->name;
|
|
||||||
|
|
||||||
// Ensure no duplicates
|
|
||||||
$allNodes = $this->getEnumValueNodes($enumType, $valueName);
|
|
||||||
if ($allNodes && count($allNodes) > 1) {
|
|
||||||
$this->reportError(
|
|
||||||
"Enum type {$enumType->name} can include value {$valueName} only once.",
|
|
||||||
$allNodes
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure valid name.
|
|
||||||
$this->validateName($enumValue);
|
|
||||||
if ($valueName === 'true' || $valueName === 'false' || $valueName === 'null') {
|
|
||||||
$this->reportError(
|
|
||||||
"Enum type {$enumType->name} cannot include value: {$valueName}.",
|
|
||||||
$enumValue->astNode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function validateInputFields(InputObjectType $inputObj)
|
|
||||||
{
|
|
||||||
$fieldMap = $inputObj->getFields();
|
|
||||||
|
|
||||||
if (!$fieldMap) {
|
|
||||||
$this->reportError(
|
|
||||||
"Input Object type {$inputObj->name} must define one or more fields.",
|
|
||||||
$inputObj->astNode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the arguments are valid
|
|
||||||
foreach ($fieldMap as $fieldName => $field) {
|
|
||||||
// Ensure they are named correctly.
|
|
||||||
$this->validateName($field);
|
|
||||||
|
|
||||||
// TODO: Ensure they are unique per field.
|
|
||||||
|
|
||||||
// Ensure the type is an input type
|
|
||||||
if (!Type::isInputType($field->getType())) {
|
|
||||||
$this->reportError(
|
|
||||||
"The type of {$inputObj->name}.{$fieldName} must be Input Type " .
|
|
||||||
"but got: " . Utils::printSafe($field->getType()) . ".",
|
|
||||||
$field->astNode ? $field->astNode->type : null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ObjectType|InterfaceType $type
|
|
||||||
* @return ObjectTypeDefinitionNode[]|ObjectTypeExtensionNode[]|InterfaceTypeDefinitionNode[]|InterfaceTypeExtensionNode[]
|
|
||||||
*/
|
|
||||||
private function getAllObjectOrInterfaceNodes($type)
|
|
||||||
{
|
|
||||||
return $type->astNode
|
|
||||||
? ($type->extensionASTNodes
|
|
||||||
? array_merge([$type->astNode], $type->extensionASTNodes)
|
|
||||||
: [$type->astNode])
|
|
||||||
: ($type->extensionASTNodes ?: []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectType $type
|
|
||||||
* @param InterfaceType $iface
|
|
||||||
* @return NamedTypeNode|null
|
|
||||||
*/
|
|
||||||
private function getImplementsInterfaceNode(ObjectType $type, $iface)
|
|
||||||
{
|
|
||||||
$nodes = $this->getAllImplementsInterfaceNodes($type, $iface);
|
|
||||||
return $nodes && isset($nodes[0]) ? $nodes[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectType $type
|
|
||||||
* @param InterfaceType $iface
|
|
||||||
* @return NamedTypeNode[]
|
|
||||||
*/
|
|
||||||
private function getAllImplementsInterfaceNodes(ObjectType $type, $iface)
|
|
||||||
{
|
|
||||||
$implementsNodes = [];
|
|
||||||
$astNodes = $this->getAllObjectOrInterfaceNodes($type);
|
|
||||||
|
|
||||||
foreach($astNodes as $astNode) {
|
|
||||||
if ($astNode && $astNode->interfaces) {
|
|
||||||
foreach($astNode->interfaces as $node) {
|
|
||||||
if ($node->name->value === $iface->name) {
|
|
||||||
$implementsNodes[] = $node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $implementsNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectType|InterfaceType $type
|
|
||||||
* @param string $fieldName
|
|
||||||
* @return FieldDefinitionNode|null
|
|
||||||
*/
|
|
||||||
private function getFieldNode($type, $fieldName)
|
|
||||||
{
|
|
||||||
$nodes = $this->getAllFieldNodes($type, $fieldName);
|
|
||||||
return $nodes && isset($nodes[0]) ? $nodes[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectType|InterfaceType $type
|
|
||||||
* @param string $fieldName
|
|
||||||
* @return FieldDefinitionNode[]
|
|
||||||
*/
|
|
||||||
private function getAllFieldNodes($type, $fieldName)
|
|
||||||
{
|
|
||||||
$fieldNodes = [];
|
|
||||||
$astNodes = $this->getAllObjectOrInterfaceNodes($type);
|
|
||||||
foreach($astNodes as $astNode) {
|
|
||||||
if ($astNode && $astNode->fields) {
|
|
||||||
foreach($astNode->fields as $node) {
|
|
||||||
if ($node->name->value === $fieldName) {
|
|
||||||
$fieldNodes[] = $node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $fieldNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectType|InterfaceType $type
|
|
||||||
* @param string $fieldName
|
|
||||||
* @return TypeNode|null
|
|
||||||
*/
|
|
||||||
private function getFieldTypeNode($type, $fieldName)
|
|
||||||
{
|
|
||||||
$fieldNode = $this->getFieldNode($type, $fieldName);
|
|
||||||
return $fieldNode ? $fieldNode->type : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectType|InterfaceType $type
|
|
||||||
* @param string $fieldName
|
|
||||||
* @param string $argName
|
|
||||||
* @return InputValueDefinitionNode|null
|
|
||||||
*/
|
|
||||||
private function getFieldArgNode($type, $fieldName, $argName)
|
|
||||||
{
|
|
||||||
$nodes = $this->getAllFieldArgNodes($type, $fieldName, $argName);
|
|
||||||
return $nodes && isset($nodes[0]) ? $nodes[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectType|InterfaceType $type
|
|
||||||
* @param string $fieldName
|
|
||||||
* @param string $argName
|
|
||||||
* @return InputValueDefinitionNode[]
|
|
||||||
*/
|
|
||||||
private function getAllFieldArgNodes($type, $fieldName, $argName)
|
|
||||||
{
|
|
||||||
$argNodes = [];
|
|
||||||
$fieldNode = $this->getFieldNode($type, $fieldName);
|
|
||||||
if ($fieldNode && $fieldNode->arguments) {
|
|
||||||
foreach ($fieldNode->arguments as $node) {
|
|
||||||
if ($node->name->value === $argName) {
|
|
||||||
$argNodes[] = $node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $argNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ObjectType|InterfaceType $type
|
|
||||||
* @param string $fieldName
|
|
||||||
* @param string $argName
|
|
||||||
* @return TypeNode|null
|
|
||||||
*/
|
|
||||||
private function getFieldArgTypeNode($type, $fieldName, $argName)
|
|
||||||
{
|
|
||||||
$fieldArgNode = $this->getFieldArgNode($type, $fieldName, $argName);
|
|
||||||
return $fieldArgNode ? $fieldArgNode->type : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Directive $directive
|
|
||||||
* @param string $argName
|
|
||||||
* @return InputValueDefinitionNode[]
|
|
||||||
*/
|
|
||||||
private function getAllDirectiveArgNodes(Directive $directive, $argName)
|
|
||||||
{
|
|
||||||
$argNodes = [];
|
|
||||||
$directiveNode = $directive->astNode;
|
|
||||||
if ($directiveNode && $directiveNode->arguments) {
|
|
||||||
foreach($directiveNode->arguments as $node) {
|
|
||||||
if ($node->name->value === $argName) {
|
|
||||||
$argNodes[] = $node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $argNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Directive $directive
|
|
||||||
* @param string $argName
|
|
||||||
* @return TypeNode|null
|
|
||||||
*/
|
|
||||||
private function getDirectiveArgTypeNode(Directive $directive, $argName)
|
|
||||||
{
|
|
||||||
$argNode = $this->getAllDirectiveArgNodes($directive, $argName)[0];
|
|
||||||
return $argNode ? $argNode->type : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param UnionType $union
|
|
||||||
* @param string $typeName
|
* @param string $typeName
|
||||||
* @return NamedTypeNode[]
|
* @return NamedTypeNode[]
|
||||||
*/
|
*/
|
||||||
@ -694,12 +727,48 @@ class SchemaValidationContext
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $union->astNode ?
|
return $union->astNode ?
|
||||||
$union->astNode->types : null;
|
$union->astNode->types : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function validateEnumValues(EnumType $enumType)
|
||||||
|
{
|
||||||
|
$enumValues = $enumType->getValues();
|
||||||
|
|
||||||
|
if (! $enumValues) {
|
||||||
|
$this->reportError(
|
||||||
|
sprintf('Enum type %s must define one or more values.', $enumType->name),
|
||||||
|
$enumType->astNode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($enumValues as $enumValue) {
|
||||||
|
$valueName = $enumValue->name;
|
||||||
|
|
||||||
|
// Ensure no duplicates
|
||||||
|
$allNodes = $this->getEnumValueNodes($enumType, $valueName);
|
||||||
|
if ($allNodes && count($allNodes) > 1) {
|
||||||
|
$this->reportError(
|
||||||
|
sprintf('Enum type %s can include value %s only once.', $enumType->name, $valueName),
|
||||||
|
$allNodes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure valid name.
|
||||||
|
$this->validateName($enumValue);
|
||||||
|
if ($valueName !== 'true' && $valueName !== 'false' && $valueName !== 'null') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->reportError(
|
||||||
|
sprintf('Enum type %s cannot include value: %s.', $enumType->name, $valueName),
|
||||||
|
$enumValue->astNode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param EnumType $enum
|
|
||||||
* @param string $valueName
|
* @param string $valueName
|
||||||
* @return EnumValueDefinitionNode[]
|
* @return EnumValueDefinitionNode[]
|
||||||
*/
|
*/
|
||||||
@ -713,25 +782,43 @@ class SchemaValidationContext
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $enum->astNode ?
|
return $enum->astNode ?
|
||||||
$enum->astNode->values : null;
|
$enum->astNode->values : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function validateInputFields(InputObjectType $inputObj)
|
||||||
* @param string $message
|
|
||||||
* @param array|Node|TypeNode|TypeDefinitionNode $nodes
|
|
||||||
*/
|
|
||||||
private function reportError($message, $nodes = null)
|
|
||||||
{
|
{
|
||||||
$nodes = array_filter($nodes && is_array($nodes) ? $nodes : [$nodes]);
|
$fieldMap = $inputObj->getFields();
|
||||||
$this->addError(new Error($message, $nodes));
|
|
||||||
|
if (! $fieldMap) {
|
||||||
|
$this->reportError(
|
||||||
|
sprintf('Input Object type %s must define one or more fields.', $inputObj->name),
|
||||||
|
$inputObj->astNode
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Ensure the arguments are valid
|
||||||
* @param Error $error
|
foreach ($fieldMap as $fieldName => $field) {
|
||||||
*/
|
// Ensure they are named correctly.
|
||||||
private function addError($error)
|
$this->validateName($field);
|
||||||
{
|
|
||||||
$this->errors[] = $error;
|
// TODO: Ensure they are unique per field.
|
||||||
|
|
||||||
|
// Ensure the type is an input type
|
||||||
|
if (Type::isInputType($field->getType())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->reportError(
|
||||||
|
sprintf(
|
||||||
|
'The type of %s.%s must be Input Type but got: %s.',
|
||||||
|
$inputObj->name,
|
||||||
|
$fieldName,
|
||||||
|
Utils::printSafe($field->getType())
|
||||||
|
),
|
||||||
|
$field->astNode ? $field->astNode->type : null
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
src/Type/TypeKind.php
Normal file
17
src/Type/TypeKind.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Type;
|
||||||
|
|
||||||
|
class TypeKind
|
||||||
|
{
|
||||||
|
const SCALAR = 0;
|
||||||
|
const OBJECT = 1;
|
||||||
|
const INTERFACE_KIND = 2;
|
||||||
|
const UNION = 3;
|
||||||
|
const ENUM = 4;
|
||||||
|
const INPUT_OBJECT = 5;
|
||||||
|
const LIST_KIND = 6;
|
||||||
|
const NON_NULL = 7;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user