mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 22:36:02 +03:00
Updated parser to consume latest lexer; New public parser API methods: parseType and parseValue; added directives to schema parser/printer
This commit is contained in:
parent
3eeb4d450b
commit
cd14146032
@ -13,6 +13,11 @@ class EnumTypeDefinition extends Node implements TypeDefinition
|
|||||||
*/
|
*/
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var EnumValueDefinition[]
|
* @var EnumValueDefinition[]
|
||||||
*/
|
*/
|
||||||
|
@ -12,4 +12,9 @@ class EnumValueDefinition extends Node
|
|||||||
* @var Name
|
* @var Name
|
||||||
*/
|
*/
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,9 @@ class FieldDefinition extends Node
|
|||||||
* @var Type
|
* @var Type
|
||||||
*/
|
*/
|
||||||
public $type;
|
public $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,11 @@ class InputObjectTypeDefinition extends Node implements TypeDefinition
|
|||||||
*/
|
*/
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var InputValueDefinition[]
|
* @var InputValueDefinition[]
|
||||||
*/
|
*/
|
||||||
|
@ -22,4 +22,9 @@ class InputValueDefinition extends Node
|
|||||||
* @var Value
|
* @var Value
|
||||||
*/
|
*/
|
||||||
public $defaultValue;
|
public $defaultValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
}
|
}
|
@ -13,6 +13,11 @@ class InterfaceTypeDefinition extends Node implements TypeDefinition
|
|||||||
*/
|
*/
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var FieldDefinition[]
|
* @var FieldDefinition[]
|
||||||
*/
|
*/
|
||||||
|
@ -3,6 +3,10 @@ namespace GraphQL\Language\AST;
|
|||||||
|
|
||||||
use GraphQL\Utils;
|
use GraphQL\Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Node
|
||||||
|
* @package GraphQL\Language\AST
|
||||||
|
*/
|
||||||
abstract class Node
|
abstract class Node
|
||||||
{
|
{
|
||||||
// constants from language/kinds.js:
|
// constants from language/kinds.js:
|
||||||
|
@ -18,6 +18,11 @@ class ObjectTypeDefinition extends Node implements TypeDefinition
|
|||||||
*/
|
*/
|
||||||
public $interfaces = [];
|
public $interfaces = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var FieldDefinition[]
|
* @var FieldDefinition[]
|
||||||
*/
|
*/
|
||||||
|
@ -4,10 +4,18 @@ namespace GraphQL\Language\AST;
|
|||||||
|
|
||||||
class ScalarTypeDefinition extends Node implements TypeDefinition
|
class ScalarTypeDefinition extends Node implements TypeDefinition
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
public $kind = Node::SCALAR_TYPE_DEFINITION;
|
public $kind = Node::SCALAR_TYPE_DEFINITION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Name
|
* @var Name
|
||||||
*/
|
*/
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,11 @@ class SchemaDefinition extends Node implements TypeSystemDefinition
|
|||||||
*/
|
*/
|
||||||
public $kind = Node::SCHEMA_DEFINITION;
|
public $kind = Node::SCHEMA_DEFINITION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var OperationTypeDefinition[]
|
* @var OperationTypeDefinition[]
|
||||||
*/
|
*/
|
||||||
|
@ -13,6 +13,11 @@ class UnionTypeDefinition extends Node implements TypeDefinition
|
|||||||
*/
|
*/
|
||||||
public $name;
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Directive[]
|
||||||
|
*/
|
||||||
|
public $directives;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var NamedType[]
|
* @var NamedType[]
|
||||||
*/
|
*/
|
||||||
|
@ -53,11 +53,6 @@ class Parser
|
|||||||
* in the source that they correspond to. This configuration flag
|
* in the source that they correspond to. This configuration flag
|
||||||
* disables that behavior for performance or testing.)
|
* disables that behavior for performance or testing.)
|
||||||
*
|
*
|
||||||
* noSource: boolean,
|
|
||||||
* By default, the parser creates AST nodes that contain a reference
|
|
||||||
* to the source that they were created from. This configuration flag
|
|
||||||
* disables that behavior for performance or testing.
|
|
||||||
*
|
|
||||||
* @param Source|string $source
|
* @param Source|string $source
|
||||||
* @param array $options
|
* @param array $options
|
||||||
* @return Document
|
* @return Document
|
||||||
@ -69,67 +64,78 @@ class Parser
|
|||||||
return $parser->parseDocument();
|
return $parser->parseDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Source
|
|
||||||
*/
|
|
||||||
private $source;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* Given a string containing a GraphQL value (ex. `[42]`), parse the AST for
|
||||||
|
* that value.
|
||||||
|
* Throws GraphQLError if a syntax error is encountered.
|
||||||
|
*
|
||||||
|
* This is useful within tools that operate upon GraphQL Values directly and
|
||||||
|
* in isolation of complete GraphQL documents.
|
||||||
|
*
|
||||||
|
* Consider providing the results to the utility function: valueFromAST().
|
||||||
|
*
|
||||||
|
* @param Source|string $source
|
||||||
|
* @param array $options
|
||||||
|
* @return BooleanValue|EnumValue|FloatValue|IntValue|ListValue|ObjectValue|StringValue|Variable
|
||||||
*/
|
*/
|
||||||
private $options;
|
public static function parseValue($source, array $options = [])
|
||||||
|
{
|
||||||
|
$sourceObj = $source instanceof Source ? $source : new Source($source);
|
||||||
|
$parser = new Parser($sourceObj, $options);
|
||||||
|
$parser->expect(Token::SOF);
|
||||||
|
$value = $parser->parseValueLiteral(false);
|
||||||
|
$parser->expect(Token::EOF);
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for
|
||||||
|
* that type.
|
||||||
|
* Throws GraphQLError if a syntax error is encountered.
|
||||||
|
*
|
||||||
|
* This is useful within tools that operate upon GraphQL Types directly and
|
||||||
|
* in isolation of complete GraphQL documents.
|
||||||
|
*
|
||||||
|
* Consider providing the results to the utility function: typeFromAST().
|
||||||
|
* @param Source|string $source
|
||||||
|
* @param array $options
|
||||||
|
* @return ListType|Name|NonNullType
|
||||||
*/
|
*/
|
||||||
private $prevEnd;
|
public static function parseType($source, array $options = [])
|
||||||
|
{
|
||||||
|
$sourceObj = $source instanceof Source ? $source : new Source($source);
|
||||||
|
$parser = new Parser($sourceObj, $options);
|
||||||
|
$parser->expect(Token::SOF);
|
||||||
|
$type = $parser->parseTypeReference();
|
||||||
|
$parser->expect(Token::EOF);
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Lexer
|
* @var Lexer
|
||||||
*/
|
*/
|
||||||
private $lexer;
|
private $lexer;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Token
|
|
||||||
*/
|
|
||||||
private $token;
|
|
||||||
|
|
||||||
function __construct(Source $source, array $options = [])
|
function __construct(Source $source, array $options = [])
|
||||||
{
|
{
|
||||||
$this->lexer = new Lexer($source);
|
$this->lexer = new Lexer($source, $options);
|
||||||
$this->source = $source;
|
|
||||||
$this->options = $options;
|
|
||||||
$this->prevEnd = 0;
|
|
||||||
$this->token = $this->lexer->nextToken();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a location object, used to identify the place in
|
* Returns a location object, used to identify the place in
|
||||||
* the source that created a given parsed object.
|
* the source that created a given parsed object.
|
||||||
*
|
*
|
||||||
* @param int $start
|
* @param Token $startToken
|
||||||
* @return Location|null
|
* @return Location|null
|
||||||
*/
|
*/
|
||||||
function loc($start)
|
function loc(Token $startToken)
|
||||||
{
|
{
|
||||||
if (!empty($this->options['noLocation'])) {
|
if (empty($this->lexer->options['noLocation'])) {
|
||||||
|
return new Location($startToken, $this->lexer->lastToken, $this->lexer->source);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!empty($this->options['noSource'])) {
|
|
||||||
return new Location($start, $this->prevEnd);
|
|
||||||
}
|
|
||||||
return new Location($start, $this->prevEnd, $this->source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the internal parser object to the next lexed token.
|
|
||||||
*/
|
|
||||||
function advance()
|
|
||||||
{
|
|
||||||
$prevEnd = $this->token->end;
|
|
||||||
$this->prevEnd = $prevEnd;
|
|
||||||
$this->token = $this->lexer->nextToken($prevEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the next token is of a given kind
|
* Determines if the next token is of a given kind
|
||||||
@ -139,7 +145,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function peek($kind)
|
function peek($kind)
|
||||||
{
|
{
|
||||||
return $this->token->kind === $kind;
|
return $this->lexer->token->kind === $kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,10 +157,10 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function skip($kind)
|
function skip($kind)
|
||||||
{
|
{
|
||||||
$match = $this->token->kind === $kind;
|
$match = $this->lexer->token->kind === $kind;
|
||||||
|
|
||||||
if ($match) {
|
if ($match) {
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
}
|
}
|
||||||
return $match;
|
return $match;
|
||||||
}
|
}
|
||||||
@ -168,17 +174,17 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function expect($kind)
|
function expect($kind)
|
||||||
{
|
{
|
||||||
$token = $this->token;
|
$token = $this->lexer->token;
|
||||||
|
|
||||||
if ($token->kind === $kind) {
|
if ($token->kind === $kind) {
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
return $token;
|
return $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new SyntaxError(
|
throw new SyntaxError(
|
||||||
$this->source,
|
$this->lexer->source,
|
||||||
$token->start,
|
$token->start,
|
||||||
"Expected " . Token::getKindDescription($kind) . ", found " . $token->getDescription()
|
"Expected $kind, found " . $token->getDescription()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,14 +199,14 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function expectKeyword($value)
|
function expectKeyword($value)
|
||||||
{
|
{
|
||||||
$token = $this->token;
|
$token = $this->lexer->token;
|
||||||
|
|
||||||
if ($token->kind === Token::NAME && $token->value === $value) {
|
if ($token->kind === Token::NAME && $token->value === $value) {
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
return $token;
|
return $token;
|
||||||
}
|
}
|
||||||
throw new SyntaxError(
|
throw new SyntaxError(
|
||||||
$this->source,
|
$this->lexer->source,
|
||||||
$token->start,
|
$token->start,
|
||||||
'Expected "' . $value . '", found ' . $token->getDescription()
|
'Expected "' . $value . '", found ' . $token->getDescription()
|
||||||
);
|
);
|
||||||
@ -212,8 +218,8 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function unexpected(Token $atToken = null)
|
function unexpected(Token $atToken = null)
|
||||||
{
|
{
|
||||||
$token = $atToken ?: $this->token;
|
$token = $atToken ?: $this->lexer->token;
|
||||||
return new SyntaxError($this->source, $token->start, "Unexpected " . $token->getDescription());
|
return new SyntaxError($this->lexer->source, $token->start, "Unexpected " . $token->getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -274,22 +280,10 @@ class Parser
|
|||||||
|
|
||||||
return new Name([
|
return new Name([
|
||||||
'value' => $token->value,
|
'value' => $token->value,
|
||||||
'loc' => $this->loc($token->start)
|
'loc' => $this->loc($token)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Name
|
|
||||||
* @throws SyntaxError
|
|
||||||
*/
|
|
||||||
function parseFragmentName()
|
|
||||||
{
|
|
||||||
if ($this->token->value === 'on') {
|
|
||||||
throw $this->unexpected();
|
|
||||||
}
|
|
||||||
return $this->parseName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the parsing rules in the Document section.
|
* Implements the parsing rules in the Document section.
|
||||||
*
|
*
|
||||||
@ -298,9 +292,10 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseDocument()
|
function parseDocument()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$definitions = [];
|
$this->expect(Token::SOF);
|
||||||
|
|
||||||
|
$definitions = [];
|
||||||
do {
|
do {
|
||||||
$definitions[] = $this->parseDefinition();
|
$definitions[] = $this->parseDefinition();
|
||||||
} while (!$this->skip(Token::EOF));
|
} while (!$this->skip(Token::EOF));
|
||||||
@ -322,10 +317,9 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->peek(Token::NAME)) {
|
if ($this->peek(Token::NAME)) {
|
||||||
switch ($this->token->value) {
|
switch ($this->lexer->token->value) {
|
||||||
case 'query':
|
case 'query':
|
||||||
case 'mutation':
|
case 'mutation':
|
||||||
// Note: subscription is an experimental non-spec addition.
|
|
||||||
case 'subscription':
|
case 'subscription':
|
||||||
return $this->parseOperationDefinition();
|
return $this->parseOperationDefinition();
|
||||||
|
|
||||||
@ -357,7 +351,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseOperationDefinition()
|
function parseOperationDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
if ($this->peek(Token::BRACE_L)) {
|
if ($this->peek(Token::BRACE_L)) {
|
||||||
return new OperationDefinition([
|
return new OperationDefinition([
|
||||||
'operation' => 'query',
|
'operation' => 'query',
|
||||||
@ -423,11 +417,11 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseVariableDefinition()
|
function parseVariableDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$var = $this->parseVariable();
|
$var = $this->parseVariable();
|
||||||
|
|
||||||
$this->expect(Token::COLON);
|
$this->expect(Token::COLON);
|
||||||
$type = $this->parseType();
|
$type = $this->parseTypeReference();
|
||||||
|
|
||||||
return new VariableDefinition([
|
return new VariableDefinition([
|
||||||
'variable' => $var,
|
'variable' => $var,
|
||||||
@ -444,7 +438,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseVariable()
|
function parseVariable()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expect(Token::DOLLAR);
|
$this->expect(Token::DOLLAR);
|
||||||
|
|
||||||
return new Variable([
|
return new Variable([
|
||||||
@ -458,7 +452,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseSelectionSet()
|
function parseSelectionSet()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
return new SelectionSet([
|
return new SelectionSet([
|
||||||
'selections' => $this->many(Token::BRACE_L, [$this, 'parseSelection'], Token::BRACE_R),
|
'selections' => $this->many(Token::BRACE_L, [$this, 'parseSelection'], Token::BRACE_R),
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
@ -466,6 +460,11 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Selection :
|
||||||
|
* - Field
|
||||||
|
* - FragmentSpread
|
||||||
|
* - InlineFragment
|
||||||
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function parseSelection()
|
function parseSelection()
|
||||||
@ -480,7 +479,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseField()
|
function parseField()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$nameOrAlias = $this->parseName();
|
$nameOrAlias = $this->parseName();
|
||||||
|
|
||||||
if ($this->skip(Token::COLON)) {
|
if ($this->skip(Token::COLON)) {
|
||||||
@ -517,7 +516,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseArgument()
|
function parseArgument()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
|
||||||
$this->expect(Token::COLON);
|
$this->expect(Token::COLON);
|
||||||
@ -538,10 +537,10 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseFragment()
|
function parseFragment()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expect(Token::SPREAD);
|
$this->expect(Token::SPREAD);
|
||||||
|
|
||||||
if ($this->peek(Token::NAME) && $this->token->value !== 'on') {
|
if ($this->peek(Token::NAME) && $this->lexer->token->value !== 'on') {
|
||||||
return new FragmentSpread([
|
return new FragmentSpread([
|
||||||
'name' => $this->parseFragmentName(),
|
'name' => $this->parseFragmentName(),
|
||||||
'directives' => $this->parseDirectives(),
|
'directives' => $this->parseDirectives(),
|
||||||
@ -550,8 +549,8 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
$typeCondition = null;
|
$typeCondition = null;
|
||||||
if ($this->token->value === 'on') {
|
if ($this->lexer->token->value === 'on') {
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
$typeCondition = $this->parseNamedType();
|
$typeCondition = $this->parseNamedType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,7 +568,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseFragmentDefinition()
|
function parseFragmentDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('fragment');
|
$this->expectKeyword('fragment');
|
||||||
|
|
||||||
$name = $this->parseFragmentName();
|
$name = $this->parseFragmentName();
|
||||||
@ -585,64 +584,77 @@ class Parser
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the parsing rules in the Values section.
|
|
||||||
function parseVariableValue()
|
|
||||||
{
|
|
||||||
return $this->parseValueLiteral(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return BooleanValue|EnumValue|FloatValue|IntValue|StringValue|Variable
|
* @return Name
|
||||||
* @throws SyntaxError
|
* @throws SyntaxError
|
||||||
*/
|
*/
|
||||||
function parseConstValue()
|
function parseFragmentName()
|
||||||
{
|
{
|
||||||
return $this->parseValueLiteral(true);
|
if ($this->lexer->token->value === 'on') {
|
||||||
|
throw $this->unexpected();
|
||||||
|
}
|
||||||
|
return $this->parseName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implements the parsing rules in the Values section.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Value[Const] :
|
||||||
|
* - [~Const] Variable
|
||||||
|
* - IntValue
|
||||||
|
* - FloatValue
|
||||||
|
* - StringValue
|
||||||
|
* - BooleanValue
|
||||||
|
* - EnumValue
|
||||||
|
* - ListValue[?Const]
|
||||||
|
* - ObjectValue[?Const]
|
||||||
|
*
|
||||||
|
* BooleanValue : one of `true` `false`
|
||||||
|
*
|
||||||
|
* EnumValue : Name but not `true`, `false` or `null`
|
||||||
|
*
|
||||||
* @param $isConst
|
* @param $isConst
|
||||||
* @return BooleanValue|EnumValue|FloatValue|IntValue|StringValue|Variable
|
* @return BooleanValue|EnumValue|FloatValue|IntValue|StringValue|Variable|ListValue|ObjectValue
|
||||||
* @throws SyntaxError
|
* @throws SyntaxError
|
||||||
*/
|
*/
|
||||||
function parseValueLiteral($isConst)
|
function parseValueLiteral($isConst)
|
||||||
{
|
{
|
||||||
$token = $this->token;
|
$token = $this->lexer->token;
|
||||||
switch ($token->kind) {
|
switch ($token->kind) {
|
||||||
case Token::BRACKET_L:
|
case Token::BRACKET_L:
|
||||||
return $this->parseArray($isConst);
|
return $this->parseArray($isConst);
|
||||||
case Token::BRACE_L:
|
case Token::BRACE_L:
|
||||||
return $this->parseObject($isConst);
|
return $this->parseObject($isConst);
|
||||||
case Token::INT:
|
case Token::INT:
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
return new IntValue([
|
return new IntValue([
|
||||||
'value' => $token->value,
|
'value' => $token->value,
|
||||||
'loc' => $this->loc($token->start)
|
'loc' => $this->loc($token)
|
||||||
]);
|
]);
|
||||||
case Token::FLOAT:
|
case Token::FLOAT:
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
return new FloatValue([
|
return new FloatValue([
|
||||||
'value' => $token->value,
|
'value' => $token->value,
|
||||||
'loc' => $this->loc($token->start)
|
'loc' => $this->loc($token)
|
||||||
]);
|
]);
|
||||||
case Token::STRING:
|
case Token::STRING:
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
return new StringValue([
|
return new StringValue([
|
||||||
'value' => $token->value,
|
'value' => $token->value,
|
||||||
'loc' => $this->loc($token->start)
|
'loc' => $this->loc($token)
|
||||||
]);
|
]);
|
||||||
case Token::NAME:
|
case Token::NAME:
|
||||||
if ($token->value === 'true' || $token->value === 'false') {
|
if ($token->value === 'true' || $token->value === 'false') {
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
return new BooleanValue([
|
return new BooleanValue([
|
||||||
'value' => $token->value === 'true',
|
'value' => $token->value === 'true',
|
||||||
'loc' => $this->loc($token->start)
|
'loc' => $this->loc($token)
|
||||||
]);
|
]);
|
||||||
} else if ($token->value !== 'null') {
|
} else if ($token->value !== 'null') {
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
return new EnumValue([
|
return new EnumValue([
|
||||||
'value' => $token->value,
|
'value' => $token->value,
|
||||||
'loc' => $this->loc($token->start)
|
'loc' => $this->loc($token)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -656,13 +668,30 @@ class Parser
|
|||||||
throw $this->unexpected();
|
throw $this->unexpected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return BooleanValue|EnumValue|FloatValue|IntValue|StringValue|Variable
|
||||||
|
* @throws SyntaxError
|
||||||
|
*/
|
||||||
|
function parseConstValue()
|
||||||
|
{
|
||||||
|
return $this->parseValueLiteral(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return BooleanValue|EnumValue|FloatValue|IntValue|ListValue|ObjectValue|StringValue|Variable
|
||||||
|
*/
|
||||||
|
function parseVariableValue()
|
||||||
|
{
|
||||||
|
return $this->parseValueLiteral(false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bool $isConst
|
* @param bool $isConst
|
||||||
* @return ListValue
|
* @return ListValue
|
||||||
*/
|
*/
|
||||||
function parseArray($isConst)
|
function parseArray($isConst)
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$item = $isConst ? 'parseConstValue' : 'parseVariableValue';
|
$item = $isConst ? 'parseConstValue' : 'parseVariableValue';
|
||||||
return new ListValue([
|
return new ListValue([
|
||||||
'values' => $this->any(Token::BRACKET_L, [$this, $item], Token::BRACKET_R),
|
'values' => $this->any(Token::BRACKET_L, [$this, $item], Token::BRACKET_R),
|
||||||
@ -670,9 +699,13 @@ class Parser
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $isConst
|
||||||
|
* @return ObjectValue
|
||||||
|
*/
|
||||||
function parseObject($isConst)
|
function parseObject($isConst)
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expect(Token::BRACE_L);
|
$this->expect(Token::BRACE_L);
|
||||||
$fields = [];
|
$fields = [];
|
||||||
while (!$this->skip(Token::BRACE_R)) {
|
while (!$this->skip(Token::BRACE_R)) {
|
||||||
@ -684,9 +717,13 @@ class Parser
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $isConst
|
||||||
|
* @return ObjectField
|
||||||
|
*/
|
||||||
function parseObjectField($isConst)
|
function parseObjectField($isConst)
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
|
||||||
$this->expect(Token::COLON);
|
$this->expect(Token::COLON);
|
||||||
@ -718,7 +755,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseDirective()
|
function parseDirective()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expect(Token::AT);
|
$this->expect(Token::AT);
|
||||||
return new Directive([
|
return new Directive([
|
||||||
'name' => $this->parseName(),
|
'name' => $this->parseName(),
|
||||||
@ -735,12 +772,12 @@ class Parser
|
|||||||
* @return ListType|Name|NonNullType
|
* @return ListType|Name|NonNullType
|
||||||
* @throws SyntaxError
|
* @throws SyntaxError
|
||||||
*/
|
*/
|
||||||
function parseType()
|
function parseTypeReference()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
|
|
||||||
if ($this->skip(Token::BRACKET_L)) {
|
if ($this->skip(Token::BRACKET_L)) {
|
||||||
$type = $this->parseType();
|
$type = $this->parseTypeReference();
|
||||||
$this->expect(Token::BRACKET_R);
|
$this->expect(Token::BRACKET_R);
|
||||||
$type = new ListType([
|
$type = new ListType([
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
@ -761,7 +798,7 @@ class Parser
|
|||||||
|
|
||||||
function parseNamedType()
|
function parseNamedType()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
|
|
||||||
return new NamedType([
|
return new NamedType([
|
||||||
'name' => $this->parseName(),
|
'name' => $this->parseName(),
|
||||||
@ -773,6 +810,7 @@ class Parser
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* TypeSystemDefinition :
|
* TypeSystemDefinition :
|
||||||
|
* - SchemaDefinition
|
||||||
* - TypeDefinition
|
* - TypeDefinition
|
||||||
* - TypeExtensionDefinition
|
* - TypeExtensionDefinition
|
||||||
* - DirectiveDefinition
|
* - DirectiveDefinition
|
||||||
@ -791,7 +829,7 @@ class Parser
|
|||||||
function parseTypeSystemDefinition()
|
function parseTypeSystemDefinition()
|
||||||
{
|
{
|
||||||
if ($this->peek(Token::NAME)) {
|
if ($this->peek(Token::NAME)) {
|
||||||
switch ($this->token->value) {
|
switch ($this->lexer->token->value) {
|
||||||
case 'schema': return $this->parseSchemaDefinition();
|
case 'schema': return $this->parseSchemaDefinition();
|
||||||
case 'scalar': return $this->parseScalarTypeDefinition();
|
case 'scalar': return $this->parseScalarTypeDefinition();
|
||||||
case 'type': return $this->parseObjectTypeDefinition();
|
case 'type': return $this->parseObjectTypeDefinition();
|
||||||
@ -813,8 +851,9 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseSchemaDefinition()
|
function parseSchemaDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('schema');
|
$this->expectKeyword('schema');
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
|
|
||||||
$operationTypes = $this->many(
|
$operationTypes = $this->many(
|
||||||
Token::BRACE_L,
|
Token::BRACE_L,
|
||||||
@ -823,14 +862,18 @@ class Parser
|
|||||||
);
|
);
|
||||||
|
|
||||||
return new SchemaDefinition([
|
return new SchemaDefinition([
|
||||||
|
'directives' => $directives,
|
||||||
'operationTypes' => $operationTypes,
|
'operationTypes' => $operationTypes,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return OperationTypeDefinition
|
||||||
|
*/
|
||||||
function parseOperationTypeDefinition()
|
function parseOperationTypeDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$operation = $this->parseOperationType();
|
$operation = $this->parseOperationType();
|
||||||
$this->expect(Token::COLON);
|
$this->expect(Token::COLON);
|
||||||
$type = $this->parseNamedType();
|
$type = $this->parseNamedType();
|
||||||
@ -848,12 +891,14 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseScalarTypeDefinition()
|
function parseScalarTypeDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('scalar');
|
$this->expectKeyword('scalar');
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
|
|
||||||
return new ScalarTypeDefinition([
|
return new ScalarTypeDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
'directives' => $directives,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -864,10 +909,12 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseObjectTypeDefinition()
|
function parseObjectTypeDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('type');
|
$this->expectKeyword('type');
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
$interfaces = $this->parseImplementsInterfaces();
|
$interfaces = $this->parseImplementsInterfaces();
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
|
|
||||||
$fields = $this->any(
|
$fields = $this->any(
|
||||||
Token::BRACE_L,
|
Token::BRACE_L,
|
||||||
[$this, 'parseFieldDefinition'],
|
[$this, 'parseFieldDefinition'],
|
||||||
@ -877,6 +924,7 @@ class Parser
|
|||||||
return new ObjectTypeDefinition([
|
return new ObjectTypeDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'interfaces' => $interfaces,
|
'interfaces' => $interfaces,
|
||||||
|
'directives' => $directives,
|
||||||
'fields' => $fields,
|
'fields' => $fields,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
@ -888,11 +936,11 @@ class Parser
|
|||||||
function parseImplementsInterfaces()
|
function parseImplementsInterfaces()
|
||||||
{
|
{
|
||||||
$types = [];
|
$types = [];
|
||||||
if ($this->token->value === 'implements') {
|
if ($this->lexer->token->value === 'implements') {
|
||||||
$this->advance();
|
$this->lexer->advance();
|
||||||
do {
|
do {
|
||||||
$types[] = $this->parseNamedType();
|
$types[] = $this->parseNamedType();
|
||||||
} while (!$this->peek(Token::BRACE_L));
|
} while ($this->peek(Token::NAME));
|
||||||
}
|
}
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
@ -903,16 +951,18 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseFieldDefinition()
|
function parseFieldDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
$args = $this->parseArgumentDefs();
|
$args = $this->parseArgumentDefs();
|
||||||
$this->expect(Token::COLON);
|
$this->expect(Token::COLON);
|
||||||
$type = $this->parseType();
|
$type = $this->parseTypeReference();
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
|
|
||||||
return new FieldDefinition([
|
return new FieldDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'arguments' => $args,
|
'arguments' => $args,
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
|
'directives' => $directives,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -934,18 +984,20 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseInputValueDef()
|
function parseInputValueDef()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
$this->expect(Token::COLON);
|
$this->expect(Token::COLON);
|
||||||
$type = $this->parseType();
|
$type = $this->parseTypeReference();
|
||||||
$defaultValue = null;
|
$defaultValue = null;
|
||||||
if ($this->skip(Token::EQUALS)) {
|
if ($this->skip(Token::EQUALS)) {
|
||||||
$defaultValue = $this->parseConstValue();
|
$defaultValue = $this->parseConstValue();
|
||||||
}
|
}
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
return new InputValueDefinition([
|
return new InputValueDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'defaultValue' => $defaultValue,
|
'defaultValue' => $defaultValue,
|
||||||
|
'directives' => $directives,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -956,9 +1008,10 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseInterfaceTypeDefinition()
|
function parseInterfaceTypeDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('interface');
|
$this->expectKeyword('interface');
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
$fields = $this->any(
|
$fields = $this->any(
|
||||||
Token::BRACE_L,
|
Token::BRACE_L,
|
||||||
[$this, 'parseFieldDefinition'],
|
[$this, 'parseFieldDefinition'],
|
||||||
@ -967,6 +1020,7 @@ class Parser
|
|||||||
|
|
||||||
return new InterfaceTypeDefinition([
|
return new InterfaceTypeDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
'directives' => $directives,
|
||||||
'fields' => $fields,
|
'fields' => $fields,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
@ -978,20 +1032,26 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseUnionTypeDefinition()
|
function parseUnionTypeDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('union');
|
$this->expectKeyword('union');
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
$this->expect(Token::EQUALS);
|
$this->expect(Token::EQUALS);
|
||||||
$types = $this->parseUnionMembers();
|
$types = $this->parseUnionMembers();
|
||||||
|
|
||||||
return new UnionTypeDefinition([
|
return new UnionTypeDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
'directives' => $directives,
|
||||||
'types' => $types,
|
'types' => $types,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* UnionMembers :
|
||||||
|
* - NamedType
|
||||||
|
* - UnionMembers | NamedType
|
||||||
|
*
|
||||||
* @return NamedType[]
|
* @return NamedType[]
|
||||||
*/
|
*/
|
||||||
function parseUnionMembers()
|
function parseUnionMembers()
|
||||||
@ -1009,9 +1069,10 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseEnumTypeDefinition()
|
function parseEnumTypeDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('enum');
|
$this->expectKeyword('enum');
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
$values = $this->many(
|
$values = $this->many(
|
||||||
Token::BRACE_L,
|
Token::BRACE_L,
|
||||||
[$this, 'parseEnumValueDefinition'],
|
[$this, 'parseEnumValueDefinition'],
|
||||||
@ -1020,6 +1081,7 @@ class Parser
|
|||||||
|
|
||||||
return new EnumTypeDefinition([
|
return new EnumTypeDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
'directives' => $directives,
|
||||||
'values' => $values,
|
'values' => $values,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
@ -1030,11 +1092,13 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseEnumValueDefinition()
|
function parseEnumValueDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
|
|
||||||
return new EnumValueDefinition([
|
return new EnumValueDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
'directives' => $directives,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -1045,9 +1109,10 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseInputObjectTypeDefinition()
|
function parseInputObjectTypeDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('input');
|
$this->expectKeyword('input');
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
$directives = $this->parseDirectives();
|
||||||
$fields = $this->any(
|
$fields = $this->any(
|
||||||
Token::BRACE_L,
|
Token::BRACE_L,
|
||||||
[$this, 'parseInputValueDef'],
|
[$this, 'parseInputValueDef'],
|
||||||
@ -1056,6 +1121,7 @@ class Parser
|
|||||||
|
|
||||||
return new InputObjectTypeDefinition([
|
return new InputObjectTypeDefinition([
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
'directives' => $directives,
|
||||||
'fields' => $fields,
|
'fields' => $fields,
|
||||||
'loc' => $this->loc($start)
|
'loc' => $this->loc($start)
|
||||||
]);
|
]);
|
||||||
@ -1067,7 +1133,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
function parseTypeExtensionDefinition()
|
function parseTypeExtensionDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('extend');
|
$this->expectKeyword('extend');
|
||||||
$definition = $this->parseObjectTypeDefinition();
|
$definition = $this->parseObjectTypeDefinition();
|
||||||
|
|
||||||
@ -1078,12 +1144,15 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* DirectiveDefinition :
|
||||||
|
* - directive @ Name ArgumentsDefinition? on DirectiveLocations
|
||||||
|
*
|
||||||
* @return DirectiveDefinition
|
* @return DirectiveDefinition
|
||||||
* @throws SyntaxError
|
* @throws SyntaxError
|
||||||
*/
|
*/
|
||||||
function parseDirectiveDefinition()
|
function parseDirectiveDefinition()
|
||||||
{
|
{
|
||||||
$start = $this->token->start;
|
$start = $this->lexer->token;
|
||||||
$this->expectKeyword('directive');
|
$this->expectKeyword('directive');
|
||||||
$this->expect(Token::AT);
|
$this->expect(Token::AT);
|
||||||
$name = $this->parseName();
|
$name = $this->parseName();
|
||||||
|
@ -115,33 +115,77 @@ class Printer
|
|||||||
Node::NON_NULL_TYPE => function(NonNullType $node) {return $node->type . '!';},
|
Node::NON_NULL_TYPE => function(NonNullType $node) {return $node->type . '!';},
|
||||||
|
|
||||||
// Type System Definitions
|
// Type System Definitions
|
||||||
Node::SCHEMA_DEFINITION => function(SchemaDefinition $def) {return 'schema ' . self::block($def->operationTypes);},
|
Node::SCHEMA_DEFINITION => function(SchemaDefinition $def) {
|
||||||
|
return self::join([
|
||||||
|
'schema',
|
||||||
|
self::join($def->directives, ' '),
|
||||||
|
self::block($def->operationTypes)
|
||||||
|
], ' ');
|
||||||
|
},
|
||||||
Node::OPERATION_TYPE_DEFINITION => function(OperationTypeDefinition $def) {return $def->operation . ': ' . $def->type;},
|
Node::OPERATION_TYPE_DEFINITION => function(OperationTypeDefinition $def) {return $def->operation . ': ' . $def->type;},
|
||||||
|
|
||||||
Node::SCALAR_TYPE_DEFINITION => function(ScalarTypeDefinition $def) {return "scalar {$def->name}";},
|
Node::SCALAR_TYPE_DEFINITION => function(ScalarTypeDefinition $def) {
|
||||||
|
return self::join(['scalar', $def->name, self::join($def->directives, ' ')], ' ');
|
||||||
|
},
|
||||||
Node::OBJECT_TYPE_DEFINITION => function(ObjectTypeDefinition $def) {
|
Node::OBJECT_TYPE_DEFINITION => function(ObjectTypeDefinition $def) {
|
||||||
return 'type ' . $def->name . ' ' .
|
return self::join([
|
||||||
self::wrap('implements ', self::join($def->interfaces, ', '), ' ') .
|
'type',
|
||||||
self::block($def->fields);
|
$def->name,
|
||||||
|
self::wrap('implements ', self::join($def->interfaces, ', ')),
|
||||||
|
self::join($def->directives, ' '),
|
||||||
|
self::block($def->fields)
|
||||||
|
], ' ');
|
||||||
},
|
},
|
||||||
Node::FIELD_DEFINITION => function(FieldDefinition $def) {
|
Node::FIELD_DEFINITION => function(FieldDefinition $def) {
|
||||||
return $def->name . self::wrap('(', self::join($def->arguments, ', '), ')') . ': ' . $def->type;
|
return $def->name
|
||||||
|
. self::wrap('(', self::join($def->arguments, ', '), ')')
|
||||||
|
. ': ' . $def->type
|
||||||
|
. self::wrap(' ', self::join($def->directives, ' '));
|
||||||
},
|
},
|
||||||
Node::INPUT_VALUE_DEFINITION => function(InputValueDefinition $def) {
|
Node::INPUT_VALUE_DEFINITION => function(InputValueDefinition $def) {
|
||||||
return $def->name . ': ' . $def->type . self::wrap(' = ', $def->defaultValue);
|
return self::join([
|
||||||
|
$def->name . ': ' . $def->type,
|
||||||
|
self::wrap('= ', $def->defaultValue),
|
||||||
|
self::join($def->directives, ' ')
|
||||||
|
], ' ');
|
||||||
},
|
},
|
||||||
Node::INTERFACE_TYPE_DEFINITION => function(InterfaceTypeDefinition $def) {
|
Node::INTERFACE_TYPE_DEFINITION => function(InterfaceTypeDefinition $def) {
|
||||||
return 'interface ' . $def->name . ' ' . self::block($def->fields);
|
return self::join([
|
||||||
|
'interface',
|
||||||
|
$def->name,
|
||||||
|
self::join($def->directives, ' '),
|
||||||
|
self::block($def->fields)
|
||||||
|
], ' ');
|
||||||
},
|
},
|
||||||
Node::UNION_TYPE_DEFINITION => function(UnionTypeDefinition $def) {
|
Node::UNION_TYPE_DEFINITION => function(UnionTypeDefinition $def) {
|
||||||
return 'union ' . $def->name . ' = ' . self::join($def->types, ' | ');
|
return self::join([
|
||||||
|
'union',
|
||||||
|
$def->name,
|
||||||
|
self::join($def->directives, ' '),
|
||||||
|
'= ' . self::join($def->types, ' | ')
|
||||||
|
], ' ');
|
||||||
},
|
},
|
||||||
Node::ENUM_TYPE_DEFINITION => function(EnumTypeDefinition $def) {
|
Node::ENUM_TYPE_DEFINITION => function(EnumTypeDefinition $def) {
|
||||||
return 'enum ' . $def->name . ' ' . self::block($def->values);
|
return self::join([
|
||||||
|
'enum',
|
||||||
|
$def->name,
|
||||||
|
self::join($def->directives, ' '),
|
||||||
|
self::block($def->values)
|
||||||
|
], ' ');
|
||||||
|
},
|
||||||
|
Node::ENUM_VALUE_DEFINITION => function(EnumValueDefinition $def) {
|
||||||
|
return self::join([
|
||||||
|
$def->name,
|
||||||
|
self::join($def->directives, ' ')
|
||||||
|
], ' ');
|
||||||
},
|
},
|
||||||
Node::ENUM_VALUE_DEFINITION => function(EnumValueDefinition $def) {return $def->name;},
|
|
||||||
Node::INPUT_OBJECT_TYPE_DEFINITION => function(InputObjectTypeDefinition $def) {
|
Node::INPUT_OBJECT_TYPE_DEFINITION => function(InputObjectTypeDefinition $def) {
|
||||||
return 'input ' . $def->name . ' ' . self::block($def->fields);
|
return self::join([
|
||||||
|
'input',
|
||||||
|
$def->name,
|
||||||
|
self::join($def->directives, ' '),
|
||||||
|
self::block($def->fields)
|
||||||
|
], ' ');
|
||||||
},
|
},
|
||||||
Node::TYPE_EXTENSION_DEFINITION => function(TypeExtensionDefinition $def) {return "extend {$def->definition}";},
|
Node::TYPE_EXTENSION_DEFINITION => function(TypeExtensionDefinition $def) {return "extend {$def->definition}";},
|
||||||
Node::DIRECTIVE_DEFINITION => function(DirectiveDefinition $def) {
|
Node::DIRECTIVE_DEFINITION => function(DirectiveDefinition $def) {
|
||||||
@ -162,12 +206,12 @@ class Printer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given maybeArray, print an empty string if it is null or empty, otherwise
|
* Given array, print each item on its own line, wrapped in an
|
||||||
* print each item on it's own line, wrapped in an indented "{ }" block.
|
* indented "{ }" block.
|
||||||
*/
|
*/
|
||||||
public static function block($maybeArray)
|
public static function block($array)
|
||||||
{
|
{
|
||||||
return self::length($maybeArray) ? self::indent("{\n" . self::join($maybeArray, "\n")) . "\n}" : '';
|
return $array && self::length($array) ? self::indent("{\n" . self::join($array, "\n")) . "\n}" : '{}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function indent($maybeString)
|
public static function indent($maybeString)
|
||||||
|
@ -64,19 +64,17 @@ class Visitor
|
|||||||
Node::LIST_TYPE => ['type'],
|
Node::LIST_TYPE => ['type'],
|
||||||
Node::NON_NULL_TYPE => ['type'],
|
Node::NON_NULL_TYPE => ['type'],
|
||||||
|
|
||||||
Node::SCHEMA_DEFINITION => ['operationTypes'],
|
Node::SCHEMA_DEFINITION => ['directives', 'operationTypes'],
|
||||||
Node::OPERATION_TYPE_DEFINITION => ['type'],
|
Node::OPERATION_TYPE_DEFINITION => ['type'],
|
||||||
Node::SCALAR_TYPE_DEFINITION => ['name'],
|
Node::SCALAR_TYPE_DEFINITION => ['name', 'directives'],
|
||||||
Node::OBJECT_TYPE_DEFINITION => ['name', 'interfaces', 'fields'],
|
Node::OBJECT_TYPE_DEFINITION => ['name', 'interfaces', 'directives', 'fields'],
|
||||||
Node::FIELD_DEFINITION => ['name', 'arguments', 'type'],
|
Node::FIELD_DEFINITION => ['name', 'arguments', 'type', 'directives'],
|
||||||
Node::INPUT_VALUE_DEFINITION => ['name', 'type', 'defaultValue'],
|
Node::INPUT_VALUE_DEFINITION => ['name', 'type', 'defaultValue', 'directives'],
|
||||||
Node::INPUT_VALUE_DEFINITION => [ 'name', 'type', 'defaultValue' ],
|
Node::INTERFACE_TYPE_DEFINITION => [ 'name', 'directives', 'fields' ],
|
||||||
Node::INTERFACE_TYPE_DEFINITION => [ 'name', 'fields' ],
|
Node::UNION_TYPE_DEFINITION => [ 'name', 'directives', 'types' ],
|
||||||
Node::UNION_TYPE_DEFINITION => [ 'name', 'types' ],
|
Node::ENUM_TYPE_DEFINITION => [ 'name', 'directives', 'values' ],
|
||||||
Node::ENUM_TYPE_DEFINITION => [ 'name', 'values' ],
|
Node::ENUM_VALUE_DEFINITION => [ 'name', 'directives' ],
|
||||||
|
Node::INPUT_OBJECT_TYPE_DEFINITION => [ 'name', 'directives', 'fields' ],
|
||||||
Node::ENUM_VALUE_DEFINITION => [ 'name' ],
|
|
||||||
Node::INPUT_OBJECT_TYPE_DEFINITION => [ 'name', 'fields' ],
|
|
||||||
Node::TYPE_EXTENSION_DEFINITION => [ 'definition' ],
|
Node::TYPE_EXTENSION_DEFINITION => [ 'definition' ],
|
||||||
Node::DIRECTIVE_DEFINITION => [ 'name', 'arguments', 'locations' ]
|
Node::DIRECTIVE_DEFINITION => [ 'name', 'arguments', 'locations' ]
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,7 @@ use GraphQL\Language\AST\Field;
|
|||||||
use GraphQL\Language\AST\IntValue;
|
use GraphQL\Language\AST\IntValue;
|
||||||
use GraphQL\Language\AST\Location;
|
use GraphQL\Language\AST\Location;
|
||||||
use GraphQL\Language\AST\Name;
|
use GraphQL\Language\AST\Name;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Language\AST\OperationDefinition;
|
use GraphQL\Language\AST\OperationDefinition;
|
||||||
use GraphQL\Language\AST\SelectionSet;
|
use GraphQL\Language\AST\SelectionSet;
|
||||||
use GraphQL\Language\AST\StringValue;
|
use GraphQL\Language\AST\StringValue;
|
||||||
@ -18,45 +19,6 @@ use GraphQL\Utils;
|
|||||||
|
|
||||||
class ParserTest extends \PHPUnit_Framework_TestCase
|
class ParserTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @it accepts option to not include source
|
|
||||||
*/
|
|
||||||
public function testAcceptsOptionToNotIncludeSource()
|
|
||||||
{
|
|
||||||
$actual = Parser::parse('{ field }', ['noSource' => true]);
|
|
||||||
|
|
||||||
$expected = new Document([
|
|
||||||
'loc' => new Location(0, 9),
|
|
||||||
'definitions' => [
|
|
||||||
new OperationDefinition([
|
|
||||||
'loc' => new Location(0, 9),
|
|
||||||
'operation' => 'query',
|
|
||||||
'name' => null,
|
|
||||||
'variableDefinitions' => null,
|
|
||||||
'directives' => [],
|
|
||||||
'selectionSet' => new SelectionSet([
|
|
||||||
'loc' => new Location(0, 9),
|
|
||||||
'selections' => [
|
|
||||||
new Field([
|
|
||||||
'loc' => new Location(2, 7),
|
|
||||||
'alias' => null,
|
|
||||||
'name' => new Name([
|
|
||||||
'loc' => new Location(2, 7),
|
|
||||||
'value' => 'field'
|
|
||||||
]),
|
|
||||||
'arguments' => [],
|
|
||||||
'directives' => [],
|
|
||||||
'selectionSet' => null
|
|
||||||
])
|
|
||||||
]
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @it parse provides useful errors
|
* @it parse provides useful errors
|
||||||
*/
|
*/
|
||||||
@ -78,7 +40,7 @@ class ParserTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$run(0, '{', "Syntax Error GraphQL (1:2) Expected Name, found EOF\n\n1: {\n ^\n", [1], [new SourceLocation(1,2)]);
|
$run(0, '{', "Syntax Error GraphQL (1:2) Expected Name, found <EOF>\n\n1: {\n ^\n", [1], [new SourceLocation(1,2)]);
|
||||||
$run(1,
|
$run(1,
|
||||||
'{ ...MissingOn }
|
'{ ...MissingOn }
|
||||||
fragment MissingOn Type
|
fragment MissingOn Type
|
||||||
@ -100,7 +62,7 @@ fragment MissingOn Type
|
|||||||
Parser::parse(new Source('query', 'MyQuery.graphql'));
|
Parser::parse(new Source('query', 'MyQuery.graphql'));
|
||||||
$this->fail('Expected exception not thrown');
|
$this->fail('Expected exception not thrown');
|
||||||
} catch (SyntaxError $e) {
|
} catch (SyntaxError $e) {
|
||||||
$this->assertEquals("Syntax Error MyQuery.graphql (1:6) Expected {, found EOF\n\n1: query\n ^\n", $e->getMessage());
|
$this->assertEquals("Syntax Error MyQuery.graphql (1:6) Expected {, found <EOF>\n\n1: query\n ^\n", $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +249,7 @@ fragment $fragmentName on Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @it parse creates ast
|
* @it creates ast
|
||||||
*/
|
*/
|
||||||
public function testParseCreatesAst()
|
public function testParseCreatesAst()
|
||||||
{
|
{
|
||||||
@ -300,73 +262,267 @@ fragment $fragmentName on Type {
|
|||||||
');
|
');
|
||||||
$result = Parser::parse($source);
|
$result = Parser::parse($source);
|
||||||
|
|
||||||
$expected = new Document(array(
|
$loc = function($start, $end) use ($source) {
|
||||||
'loc' => new Location(0, 41, $source),
|
return [
|
||||||
'definitions' => array(
|
'start' => $start,
|
||||||
new OperationDefinition(array(
|
'end' => $end
|
||||||
'loc' => new Location(0, 40, $source),
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
|
'loc' => $loc(0, 41),
|
||||||
|
'definitions' => [
|
||||||
|
[
|
||||||
|
'kind' => Node::OPERATION_DEFINITION,
|
||||||
|
'loc' => $loc(0, 40),
|
||||||
'operation' => 'query',
|
'operation' => 'query',
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'variableDefinitions' => null,
|
'variableDefinitions' => null,
|
||||||
'directives' => array(),
|
'directives' => [],
|
||||||
'selectionSet' => new SelectionSet(array(
|
'selectionSet' => [
|
||||||
'loc' => new Location(0, 40, $source),
|
'kind' => Node::SELECTION_SET,
|
||||||
'selections' => array(
|
'loc' => $loc(0, 40),
|
||||||
new Field(array(
|
'selections' => [
|
||||||
'loc' => new Location(4, 38, $source),
|
[
|
||||||
|
'kind' => Node::FIELD,
|
||||||
|
'loc' => $loc(4, 38),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => new Name(array(
|
'name' => [
|
||||||
'loc' => new Location(4, 8, $source),
|
'kind' => Node::NAME,
|
||||||
|
'loc' => $loc(4, 8),
|
||||||
'value' => 'node'
|
'value' => 'node'
|
||||||
)),
|
],
|
||||||
'arguments' => array(
|
'arguments' => [
|
||||||
new Argument(array(
|
[
|
||||||
'name' => new Name(array(
|
'kind' => Node::ARGUMENT,
|
||||||
'loc' => new Location(9, 11, $source),
|
'name' => [
|
||||||
|
'kind' => Node::NAME,
|
||||||
|
'loc' => $loc(9, 11),
|
||||||
'value' => 'id'
|
'value' => 'id'
|
||||||
)),
|
],
|
||||||
'value' => new IntValue(array(
|
'value' => [
|
||||||
'loc' => new Location(13, 14, $source),
|
'kind' => Node::INT,
|
||||||
|
'loc' => $loc(13, 14),
|
||||||
'value' => '4'
|
'value' => '4'
|
||||||
)),
|
],
|
||||||
'loc' => new Location(9, 14, $source)
|
'loc' => $loc(9, 14, $source)
|
||||||
))
|
]
|
||||||
),
|
],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => new SelectionSet(array(
|
'selectionSet' => [
|
||||||
'loc' => new Location(16, 38, $source),
|
'kind' => Node::SELECTION_SET,
|
||||||
'selections' => array(
|
'loc' => $loc(16, 38),
|
||||||
new Field(array(
|
'selections' => [
|
||||||
'loc' => new Location(22, 24, $source),
|
[
|
||||||
|
'kind' => Node::FIELD,
|
||||||
|
'loc' => $loc(22, 24),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => new Name(array(
|
'name' => [
|
||||||
'loc' => new Location(22, 24, $source),
|
'kind' => Node::NAME,
|
||||||
|
'loc' => $loc(22, 24),
|
||||||
'value' => 'id'
|
'value' => 'id'
|
||||||
)),
|
],
|
||||||
'arguments' => [],
|
'arguments' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => null
|
'selectionSet' => null
|
||||||
)),
|
],
|
||||||
new Field(array(
|
[
|
||||||
'loc' => new Location(30, 34, $source),
|
'kind' => Node::FIELD,
|
||||||
|
'loc' => $loc(30, 34),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => new Name(array(
|
'name' => [
|
||||||
'loc' => new Location(30, 34, $source),
|
'kind' => Node::NAME,
|
||||||
|
'loc' => $loc(30, 34),
|
||||||
'value' => 'name'
|
'value' => 'name'
|
||||||
)),
|
],
|
||||||
'arguments' => [],
|
'arguments' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => null
|
'selectionSet' => null
|
||||||
))
|
]
|
||||||
)
|
]
|
||||||
))
|
]
|
||||||
))
|
]
|
||||||
)
|
]
|
||||||
))
|
]
|
||||||
))
|
]
|
||||||
)
|
]
|
||||||
));
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $this->nodeToArray($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it allows parsing without source location information
|
||||||
|
*/
|
||||||
|
public function testAllowsParsingWithoutSourceLocationInformation()
|
||||||
|
{
|
||||||
|
$source = new Source('{ id }');
|
||||||
|
$result = Parser::parse($source, ['noLocation' => true]);
|
||||||
|
|
||||||
|
$this->assertEquals(null, $result->loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it contains location information that only stringifys start/end
|
||||||
|
*/
|
||||||
|
public function testConvertToArray()
|
||||||
|
{
|
||||||
|
$source = new Source('{ id }');
|
||||||
|
$result = Parser::parse($source);
|
||||||
|
$this->assertEquals(['start' => 0, 'end' => '6'], TestUtils::locationToArray($result->loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it contains references to source
|
||||||
|
*/
|
||||||
|
public function testContainsReferencesToSource()
|
||||||
|
{
|
||||||
|
$source = new Source('{ id }');
|
||||||
|
$result = Parser::parse($source);
|
||||||
|
$this->assertEquals($source, $result->loc->source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it contains references to start and end tokens
|
||||||
|
*/
|
||||||
|
public function testContainsReferencesToStartAndEndTokens()
|
||||||
|
{
|
||||||
|
$source = new Source('{ id }');
|
||||||
|
$result = Parser::parse($source);
|
||||||
|
$this->assertEquals('<SOF>', $result->loc->startToken->kind);
|
||||||
|
$this->assertEquals('<EOF>', $result->loc->endToken->kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe: parseValue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it parses list values
|
||||||
|
*/
|
||||||
|
public function testParsesListValues()
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'kind' => Node::LST,
|
||||||
|
'loc' => ['start' => 0, 'end' => 11],
|
||||||
|
'values' => [
|
||||||
|
[
|
||||||
|
'kind' => Node::INT,
|
||||||
|
'loc' => ['start' => 1, 'end' => 4],
|
||||||
|
'value' => '123'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'kind' => Node::STRING,
|
||||||
|
'loc' => ['start' => 5, 'end' => 10],
|
||||||
|
'value' => 'abc'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], $this->nodeToArray(Parser::parseValue('[123 "abc"]')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe: parseType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it parses well known types
|
||||||
|
*/
|
||||||
|
public function testParsesWellKnownTypes()
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'kind' => Node::NAMED_TYPE,
|
||||||
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
|
'name' => [
|
||||||
|
'kind' => Node::NAME,
|
||||||
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
|
'value' => 'String'
|
||||||
|
]
|
||||||
|
], $this->nodeToArray(Parser::parseType('String')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it parses custom types
|
||||||
|
*/
|
||||||
|
public function testParsesCustomTypes()
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'kind' => Node::NAMED_TYPE,
|
||||||
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
|
'name' => [
|
||||||
|
'kind' => Node::NAME,
|
||||||
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
|
'value' => 'MyType'
|
||||||
|
]
|
||||||
|
], $this->nodeToArray(Parser::parseType('MyType')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it parses list types
|
||||||
|
*/
|
||||||
|
public function testParsesListTypes()
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'kind' => Node::LIST_TYPE,
|
||||||
|
'loc' => ['start' => 0, 'end' => 8],
|
||||||
|
'type' => [
|
||||||
|
'kind' => Node::NAMED_TYPE,
|
||||||
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
|
'name' => [
|
||||||
|
'kind' => Node::NAME,
|
||||||
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
|
'value' => 'MyType'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], $this->nodeToArray(Parser::parseType('[MyType]')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it parses non-null types
|
||||||
|
*/
|
||||||
|
public function testParsesNonNullTypes()
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'kind' => Node::NON_NULL_TYPE,
|
||||||
|
'loc' => ['start' => 0, 'end' => 7],
|
||||||
|
'type' => [
|
||||||
|
'kind' => Node::NAMED_TYPE,
|
||||||
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
|
'name' => [
|
||||||
|
'kind' => Node::NAME,
|
||||||
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
|
'value' => 'MyType'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], $this->nodeToArray(Parser::parseType('MyType!')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @it parses nested types
|
||||||
|
*/
|
||||||
|
public function testParsesNestedTypes()
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'kind' => Node::LIST_TYPE,
|
||||||
|
'loc' => ['start' => 0, 'end' => 9],
|
||||||
|
'type' => [
|
||||||
|
'kind' => Node::NON_NULL_TYPE,
|
||||||
|
'loc' => ['start' => 1, 'end' => 8],
|
||||||
|
'type' => [
|
||||||
|
'kind' => Node::NAMED_TYPE,
|
||||||
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
|
'name' => [
|
||||||
|
'kind' => Node::NAME,
|
||||||
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
|
'value' => 'MyType'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], $this->nodeToArray(Parser::parseType('[MyType!]')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Node $node
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function nodeToArray(Node $node)
|
||||||
|
{
|
||||||
|
return TestUtils::nodeToArray($node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ use GraphQL\Language\AST\ListType;
|
|||||||
use GraphQL\Language\AST\Location;
|
use GraphQL\Language\AST\Location;
|
||||||
use GraphQL\Language\AST\Name;
|
use GraphQL\Language\AST\Name;
|
||||||
use GraphQL\Language\AST\NamedType;
|
use GraphQL\Language\AST\NamedType;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Language\AST\NonNullType;
|
use GraphQL\Language\AST\NonNullType;
|
||||||
use GraphQL\Language\AST\ObjectTypeDefinition;
|
use GraphQL\Language\AST\ObjectTypeDefinition;
|
||||||
use GraphQL\Language\AST\ScalarTypeDefinition;
|
use GraphQL\Language\AST\ScalarTypeDefinition;
|
||||||
@ -23,6 +24,8 @@ use GraphQL\Language\Source;
|
|||||||
|
|
||||||
class SchemaParserTest extends \PHPUnit_Framework_TestCase
|
class SchemaParserTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
// Describe: Schema Parser
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @it Simple type
|
* @it Simple type
|
||||||
*/
|
*/
|
||||||
@ -33,13 +36,16 @@ type Hello {
|
|||||||
world: String
|
world: String
|
||||||
}';
|
}';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
||||||
'interfaces' => [],
|
'interfaces' => [],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->fieldNode(
|
$this->fieldNode(
|
||||||
$this->nameNode('world', $loc(16, 21)),
|
$this->nameNode('world', $loc(16, 21)),
|
||||||
@ -48,11 +54,11 @@ type Hello {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 31)
|
'loc' => $loc(1, 31)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 31)
|
'loc' => $loc(0, 31)
|
||||||
]);
|
];
|
||||||
$this->assertEquals($doc, $expected);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,15 +69,22 @@ type Hello {
|
|||||||
$body = '
|
$body = '
|
||||||
extend type Hello {
|
extend type Hello {
|
||||||
world: String
|
world: String
|
||||||
}';
|
}
|
||||||
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {
|
||||||
$expected = new Document([
|
return TestUtils::locArray($start, $end);
|
||||||
|
};
|
||||||
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new TypeExtensionDefinition([
|
[
|
||||||
'definition' => new ObjectTypeDefinition([
|
'kind' => Node::TYPE_EXTENSION_DEFINITION,
|
||||||
|
'definition' => [
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(13, 18)),
|
'name' => $this->nameNode('Hello', $loc(13, 18)),
|
||||||
'interfaces' => [],
|
'interfaces' => [],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->fieldNode(
|
$this->fieldNode(
|
||||||
$this->nameNode('world', $loc(23, 28)),
|
$this->nameNode('world', $loc(23, 28)),
|
||||||
@ -80,13 +93,13 @@ extend type Hello {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(8, 38)
|
'loc' => $loc(8, 38)
|
||||||
]),
|
|
||||||
'loc' => $loc(1, 38)
|
|
||||||
])
|
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 38)
|
'loc' => $loc(1, 38)
|
||||||
]);
|
]
|
||||||
$this->assertEquals($expected, $doc);
|
],
|
||||||
|
'loc' => $loc(0, 39)
|
||||||
|
];
|
||||||
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,31 +111,37 @@ extend type Hello {
|
|||||||
type Hello {
|
type Hello {
|
||||||
world: String!
|
world: String!
|
||||||
}';
|
}';
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {
|
||||||
|
return TestUtils::locArray($start, $end);
|
||||||
|
};
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(6,11)),
|
'name' => $this->nameNode('Hello', $loc(6,11)),
|
||||||
'interfaces' => [],
|
'interfaces' => [],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->fieldNode(
|
$this->fieldNode(
|
||||||
$this->nameNode('world', $loc(16, 21)),
|
$this->nameNode('world', $loc(16, 21)),
|
||||||
new NonNullType([
|
[
|
||||||
|
'kind' => Node::NON_NULL_TYPE,
|
||||||
'type' => $this->typeNode('String', $loc(23, 29)),
|
'type' => $this->typeNode('String', $loc(23, 29)),
|
||||||
'loc' => $loc(23, 30)
|
'loc' => $loc(23, 30)
|
||||||
]),
|
],
|
||||||
$loc(16,30)
|
$loc(16,30)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(1,32)
|
'loc' => $loc(1,32)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(1,32)
|
'loc' => $loc(0,32)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,24 +150,27 @@ type Hello {
|
|||||||
public function testSimpleTypeInheritingInterface()
|
public function testSimpleTypeInheritingInterface()
|
||||||
{
|
{
|
||||||
$body = 'type Hello implements World { }';
|
$body = 'type Hello implements World { }';
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) { return TestUtils::locArray($start, $end); };
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(5, 10)),
|
'name' => $this->nameNode('Hello', $loc(5, 10)),
|
||||||
'interfaces' => [
|
'interfaces' => [
|
||||||
$this->typeNode('World', $loc(22, 27))
|
$this->typeNode('World', $loc(22, 27))
|
||||||
],
|
],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [],
|
'fields' => [],
|
||||||
'loc' => $loc(0,31)
|
'loc' => $loc(0,31)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(0,31)
|
'loc' => $loc(0,31)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,25 +179,28 @@ type Hello {
|
|||||||
public function testSimpleTypeInheritingMultipleInterfaces()
|
public function testSimpleTypeInheritingMultipleInterfaces()
|
||||||
{
|
{
|
||||||
$body = 'type Hello implements Wo, rld { }';
|
$body = 'type Hello implements Wo, rld { }';
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(5, 10)),
|
'name' => $this->nameNode('Hello', $loc(5, 10)),
|
||||||
'interfaces' => [
|
'interfaces' => [
|
||||||
$this->typeNode('Wo', $loc(22,24)),
|
$this->typeNode('Wo', $loc(22,24)),
|
||||||
$this->typeNode('rld', $loc(26,29))
|
$this->typeNode('rld', $loc(26,29))
|
||||||
],
|
],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [],
|
'fields' => [],
|
||||||
'loc' => $loc(0, 33)
|
'loc' => $loc(0, 33)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(0, 33)
|
'loc' => $loc(0, 33)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,21 +209,24 @@ type Hello {
|
|||||||
public function testSingleValueEnum()
|
public function testSingleValueEnum()
|
||||||
{
|
{
|
||||||
$body = 'enum Hello { WORLD }';
|
$body = 'enum Hello { WORLD }';
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new EnumTypeDefinition([
|
[
|
||||||
|
'kind' => Node::ENUM_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(5, 10)),
|
'name' => $this->nameNode('Hello', $loc(5, 10)),
|
||||||
|
'directives' => [],
|
||||||
'values' => [$this->enumValueNode('WORLD', $loc(13, 18))],
|
'values' => [$this->enumValueNode('WORLD', $loc(13, 18))],
|
||||||
'loc' => $loc(0, 20)
|
'loc' => $loc(0, 20)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(0, 20)
|
'loc' => $loc(0, 20)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,24 +235,27 @@ type Hello {
|
|||||||
public function testDoubleValueEnum()
|
public function testDoubleValueEnum()
|
||||||
{
|
{
|
||||||
$body = 'enum Hello { WO, RLD }';
|
$body = 'enum Hello { WO, RLD }';
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new EnumTypeDefinition([
|
[
|
||||||
|
'kind' => Node::ENUM_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(5, 10)),
|
'name' => $this->nameNode('Hello', $loc(5, 10)),
|
||||||
|
'directives' => [],
|
||||||
'values' => [
|
'values' => [
|
||||||
$this->enumValueNode('WO', $loc(13, 15)),
|
$this->enumValueNode('WO', $loc(13, 15)),
|
||||||
$this->enumValueNode('RLD', $loc(17, 20))
|
$this->enumValueNode('RLD', $loc(17, 20))
|
||||||
],
|
],
|
||||||
'loc' => $loc(0, 22)
|
'loc' => $loc(0, 22)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(0, 22)
|
'loc' => $loc(0, 22)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -237,12 +268,15 @@ interface Hello {
|
|||||||
world: String
|
world: String
|
||||||
}';
|
}';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new InterfaceTypeDefinition([
|
[
|
||||||
|
'kind' => Node::INTERFACE_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(11, 16)),
|
'name' => $this->nameNode('Hello', $loc(11, 16)),
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->fieldNode(
|
$this->fieldNode(
|
||||||
$this->nameNode('world', $loc(21, 26)),
|
$this->nameNode('world', $loc(21, 26)),
|
||||||
@ -251,11 +285,11 @@ interface Hello {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 36)
|
'loc' => $loc(1, 36)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(1,36)
|
'loc' => $loc(0,36)
|
||||||
]);
|
];
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,13 +302,16 @@ type Hello {
|
|||||||
world(flag: Boolean): String
|
world(flag: Boolean): String
|
||||||
}';
|
}';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
||||||
'interfaces' => [],
|
'interfaces' => [],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->fieldNodeWithArgs(
|
$this->fieldNodeWithArgs(
|
||||||
$this->nameNode('world', $loc(16, 21)),
|
$this->nameNode('world', $loc(16, 21)),
|
||||||
@ -291,12 +328,12 @@ type Hello {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 46)
|
'loc' => $loc(1, 46)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 46)
|
'loc' => $loc(0, 46)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,13 +346,16 @@ type Hello {
|
|||||||
world(flag: Boolean = true): String
|
world(flag: Boolean = true): String
|
||||||
}';
|
}';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
||||||
'interfaces' => [],
|
'interfaces' => [],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->fieldNodeWithArgs(
|
$this->fieldNodeWithArgs(
|
||||||
$this->nameNode('world', $loc(16, 21)),
|
$this->nameNode('world', $loc(16, 21)),
|
||||||
@ -324,7 +364,7 @@ type Hello {
|
|||||||
$this->inputValueNode(
|
$this->inputValueNode(
|
||||||
$this->nameNode('flag', $loc(22, 26)),
|
$this->nameNode('flag', $loc(22, 26)),
|
||||||
$this->typeNode('Boolean', $loc(28, 35)),
|
$this->typeNode('Boolean', $loc(28, 35)),
|
||||||
new BooleanValue(['value' => true, 'loc' => $loc(38, 42)]),
|
['kind' => Node::BOOLEAN, 'value' => true, 'loc' => $loc(38, 42)],
|
||||||
$loc(22, 42)
|
$loc(22, 42)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -332,11 +372,11 @@ type Hello {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 53)
|
'loc' => $loc(1, 53)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 53)
|
'loc' => $loc(0, 53)
|
||||||
]);
|
];
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -349,13 +389,16 @@ type Hello {
|
|||||||
world(things: [String]): String
|
world(things: [String]): String
|
||||||
}';
|
}';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
||||||
'interfaces' => [],
|
'interfaces' => [],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->fieldNodeWithArgs(
|
$this->fieldNodeWithArgs(
|
||||||
$this->nameNode('world', $loc(16, 21)),
|
$this->nameNode('world', $loc(16, 21)),
|
||||||
@ -363,7 +406,7 @@ type Hello {
|
|||||||
[
|
[
|
||||||
$this->inputValueNode(
|
$this->inputValueNode(
|
||||||
$this->nameNode('things', $loc(22,28)),
|
$this->nameNode('things', $loc(22,28)),
|
||||||
new ListType(['type' => $this->typeNode('String', $loc(31, 37)), 'loc' => $loc(30, 38)]),
|
['kind' => Node::LIST_TYPE, 'type' => $this->typeNode('String', $loc(31, 37)), 'loc' => $loc(30, 38)],
|
||||||
null,
|
null,
|
||||||
$loc(22, 38)
|
$loc(22, 38)
|
||||||
)
|
)
|
||||||
@ -372,12 +415,12 @@ type Hello {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 49)
|
'loc' => $loc(1, 49)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 49)
|
'loc' => $loc(0, 49)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -390,13 +433,16 @@ type Hello {
|
|||||||
world(argOne: Boolean, argTwo: Int): String
|
world(argOne: Boolean, argTwo: Int): String
|
||||||
}';
|
}';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
||||||
'interfaces' => [],
|
'interfaces' => [],
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->fieldNodeWithArgs(
|
$this->fieldNodeWithArgs(
|
||||||
$this->nameNode('world', $loc(16, 21)),
|
$this->nameNode('world', $loc(16, 21)),
|
||||||
@ -419,12 +465,12 @@ type Hello {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 61)
|
'loc' => $loc(1, 61)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 61)
|
'loc' => $loc(0, 61)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -434,19 +480,22 @@ type Hello {
|
|||||||
{
|
{
|
||||||
$body = 'union Hello = World';
|
$body = 'union Hello = World';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new UnionTypeDefinition([
|
[
|
||||||
|
'kind' => Node::UNION_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
||||||
|
'directives' => [],
|
||||||
'types' => [$this->typeNode('World', $loc(14, 19))],
|
'types' => [$this->typeNode('World', $loc(14, 19))],
|
||||||
'loc' => $loc(0, 19)
|
'loc' => $loc(0, 19)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(0, 19)
|
'loc' => $loc(0, 19)
|
||||||
]);
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -456,22 +505,25 @@ type Hello {
|
|||||||
{
|
{
|
||||||
$body = 'union Hello = Wo | Rld';
|
$body = 'union Hello = Wo | Rld';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new UnionTypeDefinition([
|
[
|
||||||
|
'kind' => Node::UNION_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
'name' => $this->nameNode('Hello', $loc(6, 11)),
|
||||||
|
'directives' => [],
|
||||||
'types' => [
|
'types' => [
|
||||||
$this->typeNode('Wo', $loc(14, 16)),
|
$this->typeNode('Wo', $loc(14, 16)),
|
||||||
$this->typeNode('Rld', $loc(19, 22))
|
$this->typeNode('Rld', $loc(19, 22))
|
||||||
],
|
],
|
||||||
'loc' => $loc(0, 22)
|
'loc' => $loc(0, 22)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(0, 22)
|
'loc' => $loc(0, 22)
|
||||||
]);
|
];
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -481,17 +533,20 @@ type Hello {
|
|||||||
{
|
{
|
||||||
$body = 'scalar Hello';
|
$body = 'scalar Hello';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new ScalarTypeDefinition([
|
[
|
||||||
|
'kind' => Node::SCALAR_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(7, 12)),
|
'name' => $this->nameNode('Hello', $loc(7, 12)),
|
||||||
|
'directives' => [],
|
||||||
'loc' => $loc(0, 12)
|
'loc' => $loc(0, 12)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(0, 12)
|
'loc' => $loc(0, 12)
|
||||||
]);
|
];
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -504,12 +559,15 @@ input Hello {
|
|||||||
world: String
|
world: String
|
||||||
}';
|
}';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$loc = $this->createLocFn($body);
|
$loc = function($start, $end) {return TestUtils::locArray($start, $end);};
|
||||||
|
|
||||||
$expected = new Document([
|
$expected = [
|
||||||
|
'kind' => Node::DOCUMENT,
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
new InputObjectTypeDefinition([
|
[
|
||||||
|
'kind' => Node::INPUT_OBJECT_TYPE_DEFINITION,
|
||||||
'name' => $this->nameNode('Hello', $loc(7, 12)),
|
'name' => $this->nameNode('Hello', $loc(7, 12)),
|
||||||
|
'directives' => [],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->inputValueNode(
|
$this->inputValueNode(
|
||||||
$this->nameNode('world', $loc(17, 22)),
|
$this->nameNode('world', $loc(17, 22)),
|
||||||
@ -519,11 +577,11 @@ input Hello {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 32)
|
'loc' => $loc(1, 32)
|
||||||
])
|
]
|
||||||
],
|
],
|
||||||
'loc' => $loc(1, 32)
|
'loc' => $loc(0, 32)
|
||||||
]);
|
];
|
||||||
$this->assertEquals($expected, $doc);
|
$this->assertEquals($expected, TestUtils::nodeToArray($doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -539,28 +597,22 @@ input Hello {
|
|||||||
Parser::parse($body);
|
Parser::parse($body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function createLocFn($body)
|
|
||||||
{
|
|
||||||
return function($start, $end) use ($body) {
|
|
||||||
return new Location($start, $end, new Source($body));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private function typeNode($name, $loc)
|
private function typeNode($name, $loc)
|
||||||
{
|
{
|
||||||
return new NamedType([
|
return [
|
||||||
'name' => new Name(['value' => $name, 'loc' => $loc]),
|
'kind' => Node::NAMED_TYPE,
|
||||||
|
'name' => ['kind' => Node::NAME, 'value' => $name, 'loc' => $loc],
|
||||||
'loc' => $loc
|
'loc' => $loc
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function nameNode($name, $loc)
|
private function nameNode($name, $loc)
|
||||||
{
|
{
|
||||||
return new Name([
|
return [
|
||||||
|
'kind' => Node::NAME,
|
||||||
'value' => $name,
|
'value' => $name,
|
||||||
'loc' => $loc
|
'loc' => $loc
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fieldNode($name, $type, $loc)
|
private function fieldNode($name, $type, $loc)
|
||||||
@ -570,29 +622,35 @@ input Hello {
|
|||||||
|
|
||||||
private function fieldNodeWithArgs($name, $type, $args, $loc)
|
private function fieldNodeWithArgs($name, $type, $args, $loc)
|
||||||
{
|
{
|
||||||
return new FieldDefinition([
|
return [
|
||||||
|
'kind' => Node::FIELD_DEFINITION,
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'arguments' => $args,
|
'arguments' => $args,
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
|
'directives' => [],
|
||||||
'loc' => $loc
|
'loc' => $loc
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function enumValueNode($name, $loc)
|
private function enumValueNode($name, $loc)
|
||||||
{
|
{
|
||||||
return new EnumValueDefinition([
|
return [
|
||||||
|
'kind' => Node::ENUM_VALUE_DEFINITION,
|
||||||
'name' => $this->nameNode($name, $loc),
|
'name' => $this->nameNode($name, $loc),
|
||||||
|
'directives' => [],
|
||||||
'loc' => $loc
|
'loc' => $loc
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function inputValueNode($name, $type, $defaultValue, $loc)
|
private function inputValueNode($name, $type, $defaultValue, $loc)
|
||||||
{
|
{
|
||||||
return new InputValueDefinition([
|
return [
|
||||||
|
'kind' => Node::INPUT_VALUE_DEFINITION,
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'defaultValue' => $defaultValue,
|
'defaultValue' => $defaultValue,
|
||||||
|
'directives' => [],
|
||||||
'loc' => $loc
|
'loc' => $loc
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,29 +65,54 @@ type Foo implements Bar {
|
|||||||
six(argument: InputType = {key: "value"}): Type
|
six(argument: InputType = {key: "value"}): Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AnnotatedObject @onObject(arg: "value") {
|
||||||
|
annotatedField(arg: Type = "default" @onArg): Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
interface Bar {
|
interface Bar {
|
||||||
one: Type
|
one: Type
|
||||||
four(argument: String = "string"): String
|
four(argument: String = "string"): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AnnotatedInterface @onInterface {
|
||||||
|
annotatedField(arg: Type @onArg): Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
union Feed = Story | Article | Advert
|
union Feed = Story | Article | Advert
|
||||||
|
|
||||||
|
union AnnotatedUnion @onUnion = A | B
|
||||||
|
|
||||||
scalar CustomScalar
|
scalar CustomScalar
|
||||||
|
|
||||||
|
scalar AnnotatedScalar @onScalar
|
||||||
|
|
||||||
enum Site {
|
enum Site {
|
||||||
DESKTOP
|
DESKTOP
|
||||||
MOBILE
|
MOBILE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AnnotatedEnum @onEnum {
|
||||||
|
ANNOTATED_VALUE @onEnumValue
|
||||||
|
OTHER_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
input InputType {
|
input InputType {
|
||||||
key: String!
|
key: String!
|
||||||
answer: Int = 42
|
answer: Int = 42
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input AnnotatedInput @onInputObjectType {
|
||||||
|
annotatedField: Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
extend type Foo {
|
extend type Foo {
|
||||||
seven(argument: [String]): Type
|
seven(argument: [String]): Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extend type Foo @onType {}
|
||||||
|
|
||||||
|
type NoFields {}
|
||||||
|
|
||||||
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||||
|
|
||||||
directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||||
|
64
tests/Language/TestUtils.php
Normal file
64
tests/Language/TestUtils.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
namespace GraphQL\Tests\Language;
|
||||||
|
|
||||||
|
|
||||||
|
use GraphQL\Language\AST\Location;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
|
|
||||||
|
class TestUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Node $node
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function nodeToArray(Node $node)
|
||||||
|
{
|
||||||
|
$result = [
|
||||||
|
'kind' => $node->kind,
|
||||||
|
'loc' => self::locationToArray($node->loc)
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach (get_object_vars($node) as $prop => $propValue) {
|
||||||
|
if (isset($result[$prop]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (is_array($propValue)) {
|
||||||
|
$tmp = [];
|
||||||
|
foreach ($propValue as $tmp1) {
|
||||||
|
$tmp[] = $tmp1 instanceof Node ? self::nodeToArray($tmp1) : (array) $tmp1;
|
||||||
|
}
|
||||||
|
} else if ($propValue instanceof Node) {
|
||||||
|
$tmp = self::nodeToArray($propValue);
|
||||||
|
} else if (is_scalar($propValue) || null === $propValue) {
|
||||||
|
$tmp = $propValue;
|
||||||
|
} else {
|
||||||
|
$tmp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[$prop] = $tmp;
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Location $loc
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function locationToArray(Location $loc)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'start' => $loc->start,
|
||||||
|
'end' => $loc->end
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $start
|
||||||
|
* @param $end
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function locArray($start, $end)
|
||||||
|
{
|
||||||
|
return ['start' => $start, 'end' => $end];
|
||||||
|
}
|
||||||
|
}
|
@ -19,29 +19,54 @@ type Foo implements Bar {
|
|||||||
six(argument: InputType = {key: "value"}): Type
|
six(argument: InputType = {key: "value"}): Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AnnotatedObject @onObject(arg: "value") {
|
||||||
|
annotatedField(arg: Type = "default" @onArg): Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
interface Bar {
|
interface Bar {
|
||||||
one: Type
|
one: Type
|
||||||
four(argument: String = "string"): String
|
four(argument: String = "string"): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AnnotatedInterface @onInterface {
|
||||||
|
annotatedField(arg: Type @onArg): Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
union Feed = Story | Article | Advert
|
union Feed = Story | Article | Advert
|
||||||
|
|
||||||
|
union AnnotatedUnion @onUnion = A | B
|
||||||
|
|
||||||
scalar CustomScalar
|
scalar CustomScalar
|
||||||
|
|
||||||
|
scalar AnnotatedScalar @onScalar
|
||||||
|
|
||||||
enum Site {
|
enum Site {
|
||||||
DESKTOP
|
DESKTOP
|
||||||
MOBILE
|
MOBILE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AnnotatedEnum @onEnum {
|
||||||
|
ANNOTATED_VALUE @onEnumValue
|
||||||
|
OTHER_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
input InputType {
|
input InputType {
|
||||||
key: String!
|
key: String!
|
||||||
answer: Int = 42
|
answer: Int = 42
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input AnnotatedInput @onInputObjectType {
|
||||||
|
annotatedField: Type @onField
|
||||||
|
}
|
||||||
|
|
||||||
extend type Foo {
|
extend type Foo {
|
||||||
seven(argument: [String]): Type
|
seven(argument: [String]): Type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extend type Foo @onType {}
|
||||||
|
|
||||||
|
type NoFields {}
|
||||||
|
|
||||||
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
|
||||||
|
|
||||||
directive @include(if: Boolean!)
|
directive @include(if: Boolean!)
|
||||||
|
Loading…
Reference in New Issue
Block a user