From 8b17953fe556c97d460dbc59f25df402f3fc588c Mon Sep 17 00:00:00 2001 From: Vladimir Razuvaev Date: Sat, 13 Jan 2018 15:45:09 +0700 Subject: [PATCH] Fixed bug preventing use of parser noLocation option for serialization / deserialization --- src/Language/AST/Node.php | 22 +- tests/Language/SerializationTest.php | 17 + tests/Language/kitchen-sink-noloc.ast | 672 ++++++++++++++++++++++++++ 3 files changed, 704 insertions(+), 7 deletions(-) create mode 100644 tests/Language/kitchen-sink-noloc.ast diff --git a/src/Language/AST/Node.php b/src/Language/AST/Node.php index 06d89bc..6036fae 100644 --- a/src/Language/AST/Node.php +++ b/src/Language/AST/Node.php @@ -99,10 +99,12 @@ abstract class Node } else { $tmp = (array) $this; - $tmp['loc'] = [ - 'start' => $this->loc->start, - 'end' => $this->loc->end - ]; + if ($this->loc) { + $tmp['loc'] = [ + 'start' => $this->loc->start, + 'end' => $this->loc->end + ]; + } return $tmp; } @@ -116,16 +118,22 @@ abstract class Node { $result = [ 'kind' => $node->kind, - 'loc' => [ + ]; + + if ($node->loc) { + $result['loc'] = [ 'start' => $node->loc->start, 'end' => $node->loc->end - ] - ]; + ]; + } foreach (get_object_vars($node) as $prop => $propValue) { if (isset($result[$prop])) continue; + if ($prop === 'loc' && $propValue === null) + continue; + if (is_array($propValue) || $propValue instanceof NodeList) { $tmp = []; foreach ($propValue as $tmp1) { diff --git a/tests/Language/SerializationTest.php b/tests/Language/SerializationTest.php index 29299f7..3a5e166 100644 --- a/tests/Language/SerializationTest.php +++ b/tests/Language/SerializationTest.php @@ -26,6 +26,23 @@ class SerializationTest extends \PHPUnit_Framework_TestCase $this->assertNodesAreEqual($parsedAst, $actualAst); } + public function testSerializeSupportsNoLocationOption() + { + $kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql'); + $ast = Parser::parse($kitchenSink, ['noLocation' => true]); + $expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true); + $this->assertEquals($expectedAst, $ast->toArray(true)); + } + + public function testUnserializeSupportsNoLocationOption() + { + $kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql'); + $serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true); + $actualAst = AST::fromArray($serializedAst); + $parsedAst = Parser::parse($kitchenSink, ['noLocation' => true]); + $this->assertNodesAreEqual($parsedAst, $actualAst); + } + /** * Compares two nodes by actually iterating over all NodeLists, properly comparing locations (ignoring tokens), etc * diff --git a/tests/Language/kitchen-sink-noloc.ast b/tests/Language/kitchen-sink-noloc.ast new file mode 100644 index 0000000..11c48de --- /dev/null +++ b/tests/Language/kitchen-sink-noloc.ast @@ -0,0 +1,672 @@ +{ + "kind": "Document", + "definitions": [ + { + "kind": "OperationDefinition", + "name": { + "kind": "Name", + "value": "queryName" + }, + "operation": "query", + "variableDefinitions": [ + { + "kind": "VariableDefinition", + "variable": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "foo" + } + }, + "type": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "ComplexType" + } + }, + "defaultValue": null + }, + { + "kind": "VariableDefinition", + "variable": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "site" + } + }, + "type": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "Site" + } + }, + "defaultValue": { + "kind": "EnumValue", + "value": "MOBILE" + } + } + ], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "node" + }, + "alias": { + "kind": "Name", + "value": "whoever123is" + }, + "arguments": [ + { + "kind": "Argument", + "value": { + "kind": "ListValue", + "values": [ + { + "kind": "IntValue", + "value": "123" + }, + { + "kind": "IntValue", + "value": "456" + } + ] + }, + "name": { + "kind": "Name", + "value": "id" + } + } + ], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "id" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + }, + { + "kind": "InlineFragment", + "typeCondition": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "User" + } + }, + "directives": [ + { + "kind": "Directive", + "name": { + "kind": "Name", + "value": "defer" + }, + "arguments": [] + } + ], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "field2" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "id" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + }, + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "field1" + }, + "alias": { + "kind": "Name", + "value": "alias" + }, + "arguments": [ + { + "kind": "Argument", + "value": { + "kind": "IntValue", + "value": "10" + }, + "name": { + "kind": "Name", + "value": "first" + } + }, + { + "kind": "Argument", + "value": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "foo" + } + }, + "name": { + "kind": "Name", + "value": "after" + } + } + ], + "directives": [ + { + "kind": "Directive", + "name": { + "kind": "Name", + "value": "include" + }, + "arguments": [ + { + "kind": "Argument", + "value": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "foo" + } + }, + "name": { + "kind": "Name", + "value": "if" + } + } + ] + } + ], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "id" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + }, + { + "kind": "FragmentSpread", + "name": { + "kind": "Name", + "value": "frag" + }, + "directives": [] + } + ] + } + } + ] + } + } + ] + } + }, + { + "kind": "InlineFragment", + "typeCondition": null, + "directives": [ + { + "kind": "Directive", + "name": { + "kind": "Name", + "value": "skip" + }, + "arguments": [ + { + "kind": "Argument", + "value": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "foo" + } + }, + "name": { + "kind": "Name", + "value": "unless" + } + } + ] + } + ], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "id" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + } + ] + } + }, + { + "kind": "InlineFragment", + "typeCondition": null, + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "id" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + } + ] + } + } + ] + } + } + ] + } + }, + { + "kind": "OperationDefinition", + "name": { + "kind": "Name", + "value": "likeStory" + }, + "operation": "mutation", + "variableDefinitions": [], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "like" + }, + "alias": null, + "arguments": [ + { + "kind": "Argument", + "value": { + "kind": "IntValue", + "value": "123" + }, + "name": { + "kind": "Name", + "value": "story" + } + } + ], + "directives": [ + { + "kind": "Directive", + "name": { + "kind": "Name", + "value": "defer" + }, + "arguments": [] + } + ], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "story" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "id" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + } + ] + } + } + ] + } + } + ] + } + }, + { + "kind": "OperationDefinition", + "name": { + "kind": "Name", + "value": "StoryLikeSubscription" + }, + "operation": "subscription", + "variableDefinitions": [ + { + "kind": "VariableDefinition", + "variable": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "input" + } + }, + "type": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "StoryLikeSubscribeInput" + } + }, + "defaultValue": null + } + ], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "storyLikeSubscribe" + }, + "alias": null, + "arguments": [ + { + "kind": "Argument", + "value": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "input" + } + }, + "name": { + "kind": "Name", + "value": "input" + } + } + ], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "story" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "likers" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "count" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + } + ] + } + }, + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "likeSentence" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "text" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + } + ] + } + } + ] + } + } + ] + } + } + ] + } + }, + { + "kind": "FragmentDefinition", + "name": { + "kind": "Name", + "value": "frag" + }, + "typeCondition": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "Friend" + } + }, + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "foo" + }, + "alias": null, + "arguments": [ + { + "kind": "Argument", + "value": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "size" + } + }, + "name": { + "kind": "Name", + "value": "size" + } + }, + { + "kind": "Argument", + "value": { + "kind": "Variable", + "name": { + "kind": "Name", + "value": "b" + } + }, + "name": { + "kind": "Name", + "value": "bar" + } + }, + { + "kind": "Argument", + "value": { + "kind": "ObjectValue", + "fields": [ + { + "kind": "ObjectField", + "name": { + "kind": "Name", + "value": "key" + }, + "value": { + "kind": "StringValue", + "value": "value" + } + } + ] + }, + "name": { + "kind": "Name", + "value": "obj" + } + } + ], + "directives": [], + "selectionSet": null + } + ] + } + }, + { + "kind": "OperationDefinition", + "name": null, + "operation": "query", + "variableDefinitions": null, + "directives": [], + "selectionSet": { + "kind": "SelectionSet", + "selections": [ + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "unnamed" + }, + "alias": null, + "arguments": [ + { + "kind": "Argument", + "value": { + "kind": "BooleanValue", + "value": true + }, + "name": { + "kind": "Name", + "value": "truthy" + } + }, + { + "kind": "Argument", + "value": { + "kind": "BooleanValue", + "value": false + }, + "name": { + "kind": "Name", + "value": "falsey" + } + }, + { + "kind": "Argument", + "value": { + "kind": "NullValue" + }, + "name": { + "kind": "Name", + "value": "nullish" + } + } + ], + "directives": [], + "selectionSet": null + }, + { + "kind": "Field", + "name": { + "kind": "Name", + "value": "query" + }, + "alias": null, + "arguments": [], + "directives": [], + "selectionSet": null + } + ] + } + } + ] +} \ No newline at end of file