/**
* Coroutine to walk the query and schema in DFS manner (see AbstractQueryVisitor docs for more info) and yield a
* tuple of (queryNode, schemaNode, childScore)
*
* childScore costs are accumulated via values sent into the coroutine.
*
* Most of the branching in this function is just to handle the different types in a query: Queries, Unions,
* Fragments (anonymous and named), and Fields. The core of the function is simple: recurse until we hit the base
* case of a Field and yield that back up to the visitor up in `doVisit`.
*
* @param Query|Field|\Youshido\GraphQL\Parser\Ast\Interfaces\FragmentInterface $queryNode
* @param FieldInterface $currentLevelAST
*
* @return \Generator
*/
protected function walkQuery($queryNode, FieldInterface $currentLevelAST)
{
$childrenScore = 0;
if (!$queryNode instanceof FieldAst) {
foreach ($queryNode->getFields() as $queryField) {
if ($queryField instanceof FragmentInterface) {
if ($queryField instanceof FragmentReference) {
$queryField = $this->executionContext->getRequest()->getFragment($queryField->getName());
}
// the next 7 lines are essentially equivalent to `yield from $this->walkQuery(...)` in PHP7.
// for backwards compatibility this is equivalent.
// This pattern is repeated multiple times in this function, and unfortunately cannot be extracted or
// made less verbose.
$gen = $this->walkQuery($queryField, $currentLevelAST);
$next = $gen->current();
while ($next) {
$received = (yield $next);
$childrenScore += (int) $received;
$next = $gen->send($received);
}
} else {
$fieldType = $currentLevelAST->getType()->getNamedType();
if ($fieldType instanceof AbstractUnionType) {
foreach ($fieldType->getTypes() as $unionFieldType) {
if ($fieldAst = $unionFieldType->getField($queryField->getName())) {
$gen = $this->walkQuery($queryField, $fieldAst);
$next = $gen->current();
while ($next) {
$received = (yield $next);
$childrenScore += (int) $received;
$next = $gen->send($received);
}
}
}
} elseif ($fieldType instanceof AbstractObjectType && ($fieldAst = $fieldType->getField($queryField->getName()))) {
$gen = $this->walkQuery($queryField, $fieldAst);
$next = $gen->current();
while ($next) {
$received = (yield $next);
$childrenScore += (int) $received;
$next = $gen->send($received);
}
}
}
}
}
// sanity check. don't yield fragments; they don't contribute to cost
if ($queryNode instanceof Query || $queryNode instanceof FieldAst) {
// BASE CASE. If we're here we're done recursing -
// this node is either a field, or a query that we've finished recursing into.
(yield [$queryNode, $currentLevelAST, $childrenScore]);
}
}