diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 5fb1c03..5942005 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -8,6 +8,8 @@ use GraphQL\Executor\Promise\Promise; use GraphQL\Language\AST\DocumentNode; use GraphQL\Language\AST\FieldNode; use GraphQL\Language\AST\FragmentDefinitionNode; +use GraphQL\Language\AST\FragmentSpreadNode; +use GraphQL\Language\AST\InlineFragmentNode; use GraphQL\Language\AST\NodeKind; use GraphQL\Language\AST\OperationDefinitionNode; use GraphQL\Language\AST\SelectionSetNode; @@ -508,7 +510,7 @@ class Executor foreach ($selectionSet->selections as $selection) { switch ($selection->kind) { case NodeKind::FIELD: - if (!$this->shouldIncludeNode($selection->directives)) { + if (!$this->shouldIncludeNode($selection)) { continue; } $name = self::getFieldEntryKey($selection); @@ -518,7 +520,7 @@ class Executor $fields[$name][] = $selection; break; case NodeKind::INLINE_FRAGMENT: - if (!$this->shouldIncludeNode($selection->directives) || + if (!$this->shouldIncludeNode($selection) || !$this->doesFragmentConditionMatch($selection, $runtimeType) ) { continue; @@ -532,7 +534,7 @@ class Executor break; case NodeKind::FRAGMENT_SPREAD: $fragName = $selection->name->value; - if (!empty($visitedFragmentNames[$fragName]) || !$this->shouldIncludeNode($selection->directives)) { + if (!empty($visitedFragmentNames[$fragName]) || !$this->shouldIncludeNode($selection)) { continue; } $visitedFragmentNames[$fragName] = true; @@ -558,43 +560,35 @@ class Executor * Determines if a field should be included based on the @include and @skip * directives, where @skip has higher precedence than @include. * - * @param $directives + * @param FragmentSpreadNode | FieldNode | InlineFragmentNode $node * @return bool */ - private function shouldIncludeNode($directives) + private function shouldIncludeNode($node) { - $exeContext = $this->exeContext; + $variableValues = $this->exeContext->variableValues; $skipDirective = Directive::skipDirective(); + + $skip = Values::getDirectiveValues( + $skipDirective, + $node, + $variableValues + ); + + if (isset($skip['if']) && $skip['if'] === true) { + return false; + } + $includeDirective = Directive::includeDirective(); - /** @var \GraphQL\Language\AST\DirectiveNode $skipNode */ - $skipNode = $directives - ? Utils::find($directives, function(\GraphQL\Language\AST\DirectiveNode $directive) use ($skipDirective) { - return $directive->name->value === $skipDirective->name; - }) - : null; + $include = Values::getDirectiveValues( + $includeDirective, + $node, + $variableValues + ); - if ($skipNode) { - $argValues = Values::getArgumentValues($skipDirective, $skipNode, $exeContext->variableValues); - if (isset($argValues['if']) && $argValues['if'] === true) { - return false; - } + if (isset($include['if']) && $include['if'] === false) { + return false; } - - /** @var \GraphQL\Language\AST\DirectiveNode $includeNode */ - $includeNode = $directives - ? Utils::find($directives, function(\GraphQL\Language\AST\DirectiveNode $directive) use ($includeDirective) { - return $directive->name->value === $includeDirective->name; - }) - : null; - - if ($includeNode) { - $argValues = Values::getArgumentValues($includeDirective, $includeNode, $exeContext->variableValues); - if (isset($argValues['if']) && $argValues['if'] === false) { - return false; - } - } - return true; } diff --git a/src/Executor/Values.php b/src/Executor/Values.php index 552c3ea..52b36a1 100644 --- a/src/Executor/Values.php +++ b/src/Executor/Values.php @@ -5,15 +5,17 @@ namespace GraphQL\Executor; use GraphQL\Error\Error; use GraphQL\Error\InvariantViolation; use GraphQL\Language\AST\ArgumentNode; +use GraphQL\Language\AST\DirectiveNode; +use GraphQL\Language\AST\EnumValueDefinitionNode; +use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\FieldNode; -use GraphQL\Language\AST\NullValueNode; -use GraphQL\Language\AST\ValueNode; +use GraphQL\Language\AST\FragmentSpreadNode; +use GraphQL\Language\AST\InlineFragmentNode; use GraphQL\Language\AST\VariableNode; use GraphQL\Language\AST\VariableDefinitionNode; use GraphQL\Language\Printer; use GraphQL\Schema; use GraphQL\Type\Definition\Directive; -use GraphQL\Type\Definition\FieldArgument; use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\InputType; @@ -161,6 +163,33 @@ class Values return $coercedValues; } + /** + * Prepares an object map of argument values given a directive definition + * and a AST node which may contain directives. Optionally also accepts a map + * of variable values. + * + * If the directive does not exist on the node, returns undefined. + * + * @param Directive $directiveDef + * @param FragmentSpreadNode | FieldNode | InlineFragmentNode | EnumValueDefinitionNode | FieldDefinitionNode $node + * @param array|null $variableValues + * + * @return array|null + */ + public static function getDirectiveValues(Directive $directiveDef, $node, $variableValues = null) + { + if (isset($node->directives) && is_array($node->directives)) { + $directiveNode = Utils::find($node->directives, function(DirectiveNode $directive) use ($directiveDef) { + return $directive->name->value === $directiveDef->name; + }); + + if ($directiveNode) { + return self::getArgumentValues($directiveDef, $directiveNode, $variableValues); + } + } + return null; + } + /** * @deprecated as of 8.0 (Moved to Utils\AST::valueFromAST) * diff --git a/src/Utils/BuildSchema.php b/src/Utils/BuildSchema.php index 4601628..e2463ff 100644 --- a/src/Utils/BuildSchema.php +++ b/src/Utils/BuildSchema.php @@ -6,6 +6,8 @@ use GraphQL\Executor\Values; use GraphQL\Language\AST\DirectiveDefinitionNode; use GraphQL\Language\AST\DocumentNode; use GraphQL\Language\AST\EnumTypeDefinitionNode; +use GraphQL\Language\AST\EnumValueDefinitionNode; +use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InputObjectTypeDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode; use GraphQL\Language\AST\NodeKind; @@ -359,7 +361,7 @@ class BuildSchema 'type' => $this->produceOutputType($field->type), 'description' => $this->getDescription($field), 'args' => $this->makeInputValues($field->arguments), - 'deprecationReason' => $this->getDeprecationReason($field->directives) + 'deprecationReason' => $this->getDeprecationReason($field) ]; } ); @@ -416,7 +418,7 @@ class BuildSchema function($enumValue) { return [ 'description' => $this->getDescription($enumValue), - 'deprecationReason' => $this->getDeprecationReason($enumValue->directives) + 'deprecationReason' => $this->getDeprecationReason($enumValue) ]; } ) @@ -461,23 +463,13 @@ class BuildSchema * Given a collection of directives, returns the string value for the * deprecation reason. * - * @param $directives + * @param EnumValueDefinitionNode | FieldDefinitionNode $node + * @return string */ - private function getDeprecationReason($directives) + private function getDeprecationReason($node) { - $deprecatedAST = $directives ? Utils::find( - $directives, - function($directive) { - return $directive->name->value === Directive::deprecatedDirective()->name; - } - ) : null; - if (!$deprecatedAST) { - return; - } - return Values::getArgumentValues( - Directive::deprecatedDirective(), - $deprecatedAST - )['reason']; + $deprecated = Values::getDirectiveValues(Directive::deprecatedDirective(), $node); + return isset($deprecated['reason']) ? $deprecated['reason'] : null; } /**