public function __invoke(ValidationContext $context) { return [Node::FRAGMENT_SPREAD => function (FragmentSpread $node) use($context) { $fragmentName = $node->name->value; $fragment = $context->getFragment($fragmentName); if (!$fragment) { return new Error("Undefined fragment {$fragmentName}.", [$node->name]); } }]; }
public function __invoke(ValidationContext $context) { return [Node::FRAGMENT_SPREAD => function (FragmentSpread $node) use($context) { $fragmentName = $node->name->value; $fragment = $context->getFragment($fragmentName); if (!$fragment) { $context->reportError(new Error(self::unknownFragmentMessage($fragmentName), [$node->name])); } }]; }
private function detectCycleRecursive(FragmentDefinition $fragment, ValidationContext $context) { $fragmentName = $fragment->name->value; $this->visitedFrags[$fragmentName] = true; $spreadNodes = $context->getFragmentSpreads($fragment); if (empty($spreadNodes)) { return; } $this->spreadPathIndexByName[$fragmentName] = count($this->spreadPath); for ($i = 0; $i < count($spreadNodes); $i++) { $spreadNode = $spreadNodes[$i]; $spreadName = $spreadNode->name->value; $cycleIndex = isset($this->spreadPathIndexByName[$spreadName]) ? $this->spreadPathIndexByName[$spreadName] : null; if ($cycleIndex === null) { $this->spreadPath[] = $spreadNode; if (empty($this->visitedFrags[$spreadName])) { $spreadFragment = $context->getFragment($spreadName); if ($spreadFragment) { $this->detectCycleRecursive($spreadFragment, $context); } } array_pop($this->spreadPath); } else { $cyclePath = array_slice($this->spreadPath, $cycleIndex); $nodes = $cyclePath; if (is_array($spreadNode)) { $nodes = array_merge($nodes, $spreadNode); } else { $nodes[] = $spreadNode; } $context->reportError(new Error(self::cycleErrorMessage($spreadName, Utils::map($cyclePath, function ($s) { return $s->name->value; })), $nodes)); } } $this->spreadPathIndexByName[$fragmentName] = null; }
/** * This uses a specialized visitor which runs multiple visitors in parallel, * while maintaining the visitor skip and break API. * * @param Schema $schema * @param Document $documentAST * @param array $rules * @return array */ public static function visitUsingRules(Schema $schema, Document $documentAST, array $rules) { $typeInfo = new TypeInfo($schema); $context = new ValidationContext($schema, $documentAST, $typeInfo); $errors = []; // TODO: convert to class $visitInstances = function ($ast, $instances) use($typeInfo, $context, &$errors, &$visitInstances) { $skipUntil = new \SplFixedArray(count($instances)); $skipCount = 0; Visitor::visit($ast, ['enter' => function ($node, $key) use($typeInfo, $instances, $skipUntil, &$skipCount, &$errors, $context, $visitInstances) { $typeInfo->enter($node); for ($i = 0; $i < count($instances); $i++) { // Do not visit this instance if it returned false for a previous node if ($skipUntil[$i]) { continue; } $result = null; // Do not visit top level fragment definitions if this instance will // visit those fragments inline because it // provided `visitSpreadFragments`. if ($node->kind === Node::FRAGMENT_DEFINITION && $key !== null && !empty($instances[$i]['visitSpreadFragments'])) { $result = Visitor::skipNode(); } else { $enter = Visitor::getVisitFn($instances[$i], false, $node->kind); if ($enter instanceof \Closure) { // $enter = $enter->bindTo($instances[$i]); $result = call_user_func_array($enter, func_get_args()); } else { $result = null; } } if ($result instanceof VisitorOperation) { if ($result->doContinue) { $skipUntil[$i] = $node; $skipCount++; // If all instances are being skipped over, skip deeper traversal if ($skipCount === count($instances)) { for ($k = 0; $k < count($instances); $k++) { if ($skipUntil[$k] === $node) { $skipUntil[$k] = null; $skipCount--; } } return Visitor::skipNode(); } } else { if ($result->doBreak) { $instances[$i] = null; } } } else { if ($result && self::isError($result)) { self::append($errors, $result); for ($j = $i - 1; $j >= 0; $j--) { $leaveFn = Visitor::getVisitFn($instances[$j], true, $node->kind); if ($leaveFn) { // $leaveFn = $leaveFn->bindTo($instances[$j]) $result = call_user_func_array($leaveFn, func_get_args()); if ($result instanceof VisitorOperation) { if ($result->doBreak) { $instances[$j] = null; } } else { if (self::isError($result)) { self::append($errors, $result); } else { if ($result !== null) { throw new \Exception("Config cannot edit document."); } } } } } $typeInfo->leave($node); return Visitor::skipNode(); } else { if ($result !== null) { throw new \Exception("Config cannot edit document."); } } } } // If any validation instances provide the flag `visitSpreadFragments` // and this node is a fragment spread, validate the fragment from // this point. if ($node instanceof FragmentSpread) { $fragment = $context->getFragment($node->name->value); if ($fragment) { $fragVisitingInstances = []; foreach ($instances as $idx => $inst) { if (!empty($inst['visitSpreadFragments']) && !$skipUntil[$idx]) { $fragVisitingInstances[] = $inst; } } if (!empty($fragVisitingInstances)) { $visitInstances($fragment, $fragVisitingInstances); } } } }, 'leave' => function ($node) use($instances, $typeInfo, $skipUntil, &$skipCount, &$errors) { for ($i = count($instances) - 1; $i >= 0; $i--) { if ($skipUntil[$i]) { if ($skipUntil[$i] === $node) { $skipUntil[$i] = null; $skipCount--; } continue; } $leaveFn = Visitor::getVisitFn($instances[$i], true, $node->kind); if ($leaveFn) { // $leaveFn = $leaveFn.bindTo($instances[$i]); $result = call_user_func_array($leaveFn, func_get_args()); if ($result instanceof VisitorOperation) { if ($result->doBreak) { $instances[$i] = null; } } else { if (self::isError($result)) { self::append($errors, $result); } else { if ($result !== null) { throw new \Exception("Config cannot edit document."); } } } } } $typeInfo->leave($node); }]); }; // Visit the whole document with instances of all provided rules. $allRuleInstances = []; foreach ($rules as $rule) { $allRuleInstances[] = $rule($context); } $visitInstances($documentAST, $allRuleInstances); return $errors; }
private function getFragmentType(ValidationContext $context, $name) { $frag = $context->getFragment($name); return $frag ? TypeInfo::typeFromAST($context->getSchema(), $frag->typeCondition) : null; }
private function getFragmentType(ValidationContext $context, $name) { $frag = $context->getFragment($name); return $frag ? $context->getSchema()->getType($frag->typeCondition->value) : null; }