Completed
Pull Request — master (#22)
by
unknown
03:33
created

Processor::resolveFieldValue()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 8
nc 3
nop 3
crap 3
1
<?php
2
/*
3
* This file is a part of graphql-youshido project.
4
*
5
* @author Portey Vasil <[email protected]>
6
* @author Alexandr Viniychuk <[email protected]>
7
* created: 11/28/15 1:05 AM
8
*/
9
10
namespace Youshido\GraphQL\Execution;
11
12
use Youshido\GraphQL\Execution\Context\ExecutionContext;
13
use Youshido\GraphQL\Execution\Visitor\AbstractQueryVisitor;
14
use Youshido\GraphQL\Field\AbstractField;
15
use Youshido\GraphQL\Field\Field;
16
use Youshido\GraphQL\Introspection\Field\SchemaField;
17
use Youshido\GraphQL\Introspection\Field\TypeDefinitionField;
18
use Youshido\GraphQL\Parser\Ast\Field as FieldAst;
19
use Youshido\GraphQL\Parser\Ast\Fragment;
20
use Youshido\GraphQL\Parser\Ast\FragmentInterface;
21
use Youshido\GraphQL\Parser\Ast\FragmentReference;
22
use Youshido\GraphQL\Parser\Ast\Mutation;
23
use Youshido\GraphQL\Parser\Ast\Query;
24
use Youshido\GraphQL\Parser\Ast\TypedFragmentReference;
25
use Youshido\GraphQL\Parser\Parser;
26
use Youshido\GraphQL\Schema\AbstractSchema;
27
use Youshido\GraphQL\Type\AbstractType;
28
use Youshido\GraphQL\Type\Object\AbstractObjectType;
29
use Youshido\GraphQL\Type\TypeInterface;
30
use Youshido\GraphQL\Type\TypeMap;
31
use Youshido\GraphQL\Type\TypeService;
32
use Youshido\GraphQL\Type\Union\AbstractUnionType;
33
use Youshido\GraphQL\Validator\Exception\ResolveException;
34
use Youshido\GraphQL\Validator\ResolveValidator\ResolveValidator;
35
use Youshido\GraphQL\Validator\ResolveValidator\ResolveValidatorInterface;
36
use Youshido\GraphQL\Validator\SchemaValidator\SchemaValidator;
37
38
class Processor
39
{
40
41
    const TYPE_NAME_QUERY = '__typename';
42
43
    /** @var  array */
44
    protected $data;
45
46
    /** @var ResolveValidatorInterface */
47
    protected $resolveValidator;
48
49
    /** @var ExecutionContext */
50
    protected $executionContext;
51
52
    /** @var int */
53
    protected $maxComplexity;
54
55 24
    public function __construct(AbstractSchema $schema)
56
    {
57 24
        (new SchemaValidator())->validate($schema);
58
59 23
        $this->introduceIntrospectionFields($schema);
60 23
        $this->executionContext = new ExecutionContext();
61 23
        $this->executionContext->setSchema($schema);
62
63 23
        $this->resolveValidator = new ResolveValidator($this->executionContext);
64 23
    }
65
66
67 23
    public function processPayload($payload, $variables = [], $reducers = [])
68
    {
69 23
        if ($this->executionContext->hasErrors()) {
70 5
            $this->executionContext->clearErrors();
71 5
        }
72
73 23
        $this->data = [];
74
75
        try {
76 23
            $this->parseAndCreateRequest($payload, $variables);
77
78 23
            $queryType    = $this->executionContext->getSchema()->getQueryType();
79 23
            $mutationType = $this->executionContext->getSchema()->getMutationType();
80
81 23
            if ($this->maxComplexity) {
82 1
                $reducers[] = new \Youshido\GraphQL\Execution\Visitor\MaxComplexityQueryVisitor($this->maxComplexity);
83 1
            }
84
85 23
            $this->reduceQuery($queryType, $mutationType, $reducers);
86
87 23
            foreach ($this->executionContext->getRequest()->getOperationsInOrder() as $operation) {
88 23
                if ($operationResult = $this->executeOperation($operation, $operation instanceof Mutation ? $mutationType : $queryType)) {
89 21
                    $this->data = array_merge($this->data, $operationResult);
90 21
                };
91 23
            }
92
93 23
        } catch (\Exception $e) {
94 4
            $this->executionContext->addError($e);
95
        }
96
97 23
        return $this;
98
    }
99
100 23
    protected function parseAndCreateRequest($payload, $variables = [])
101
    {
102 23
        if (empty($payload)) {
103 1
            throw new \Exception('Must provide an operation.');
104
        }
105 23
        $parser = new Parser();
106
107 23
        $data = $parser->parse($payload);
108 23
        $this->executionContext->setRequest(new Request($data, $variables));
109 23
    }
110
111
    /**
112
     * @param Query|Field        $query
113
     * @param AbstractObjectType $currentLevelSchema
114
     * @return array|bool|mixed
115
     */
116 23
    protected function executeOperation(Query $query, $currentLevelSchema)
117
    {
118 23
        if (!$this->resolveValidator->objectHasField($currentLevelSchema, $query)) {
119 1
            return null;
120
        }
121
122
        /** @var AbstractField $field */
123 23
        $operationField = $currentLevelSchema->getField($query->getName());
124 23
        $alias          = $query->getAlias() ?: $query->getName();
125
126 23
        if (!$this->resolveValidator->validateArguments($operationField, $query, $this->executionContext->getRequest())) {
127 3
            return null;
128
        }
129
130 21
        return [$alias => $this->processQueryAST($query, $operationField)];
131
    }
132
133
    /**
134
     * @param Query         $query
135
     * @param AbstractField $field
136
     * @param               $contextValue
137
     * @return array|mixed|null
138
     */
139 21
    protected function processQueryAST(Query $query, AbstractField $field, $contextValue = null)
140
    {
141 21
        if (!$this->resolveValidator->validateArguments($field, $query, $this->executionContext->getRequest())) {
142
            return null;
143
        }
144
145 21
        $resolvedValue = $this->resolveFieldValue($field, $contextValue, $query);
146
147 21
        if (!$this->resolveValidator->isValidValueForField($field, $resolvedValue)) {
148 2
            return null;
149
        }
150
151 21
        return $this->collectValueForQueryWithType($query, $field->getType(), $resolvedValue);
152
    }
153
154
    /**
155
     * @param Query|Mutation $query
156
     * @param AbstractType   $fieldType
157
     * @param mixed          $resolvedValue
158
     * @return array|mixed
159
     */
160 21
    protected function collectValueForQueryWithType(Query $query, AbstractType $fieldType, $resolvedValue)
161
    {
162 21
        $fieldType = $this->resolveValidator->resolveTypeIfAbstract($fieldType, $resolvedValue);
163 21
        if (is_null($resolvedValue)) return null;
164
165 19
        $value = [];
166 19
        if ($fieldType->getKind() == TypeMap::KIND_LIST) {
167 8
            if (!$this->resolveValidator->hasArrayAccess($resolvedValue)) return null;
168 8
            foreach ($resolvedValue as $resolvedValueItem) {
0 ignored issues
show
Bug introduced by
The expression $resolvedValue of type object|integer|double|string|array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
169 7
                $value[] = [];
170 7
                $index   = count($value) - 1;
171
172
173 7
                $namedType = $fieldType->getNamedType();
174 7
                $namedType = $this->resolveValidator->resolveTypeIfAbstract($namedType, $resolvedValueItem);
175 7
                if (!$namedType->isValidValue($resolvedValueItem)) {
176 1
                    $this->executionContext->addError(new ResolveException(sprintf('Not valid resolve value in %s field', $query->getName())));
177 1
                    $value[$index] = null;
178 1
                    continue;
179
                }
180
181 6
                $value[$index] = $this->processQueryFields($query, $namedType, $resolvedValueItem, $value[$index]);
182 8
            }
183 8
        } else {
184 19
            if (!$query->hasFields()) {
185 2
                return $this->getOutputValue($fieldType, $resolvedValue);
186
            }
187
188 19
            $value = $this->processQueryFields($query, $fieldType, $resolvedValue, $value);
189
        }
190
191 19
        return $value;
192
    }
193
194
    /**
195
     * @param FieldAst      $fieldAst
196
     * @param AbstractField $field
197
     *
198
     * @param mixed         $contextValue
199
     * @return array|mixed|null
200
     * @throws ResolveException
201
     * @throws \Exception
202
     */
203 18
    protected function processFieldAST(FieldAst $fieldAst, AbstractField $field, $contextValue)
204
    {
205 18
        $value            = null;
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
206 18
        $fieldType        = $field->getType();
207 18
        $preResolvedValue = $this->getPreResolvedValue($contextValue, $fieldAst, $field);
208
209 18
        if ($fieldType->getKind() == TypeMap::KIND_LIST) {
210 1
            $listValue = [];
211 1
            foreach ($preResolvedValue as $resolvedValueItem) {
212 1
                $type = $fieldType->getNamedType();
213
214 1
                if (!$type->isValidValue($resolvedValueItem)) {
215
                    $this->executionContext->addError(new ResolveException(sprintf('Not valid resolve value in %s field', $field->getName())));
216
217
                    $listValue = null;
218
                    break;
219
                }
220 1
                $listValue[] = $this->getOutputValue($type, $resolvedValueItem);
221 1
            }
222
223 1
            $value = $listValue;
224 1
        } else {
225 18
            $value = $this->getFieldValidatedValue($field, $preResolvedValue);
226
        }
227
228 18
        return $value;
229
    }
230
231
    /**
232
     * @param AbstractField $field
233
     * @param mixed         $contextValue
234
     * @param Query         $query
235
     *
236
     * @return mixed
237
     */
238 21
    protected function resolveFieldValue(AbstractField $field, $contextValue, Query $query)
239
    {
240 21
        $resolveInfo = new ResolveInfo($field, $query->getFields(), $field->getType(), $this->executionContext);
241
242 21
        if ($resolveFunc = $field->getConfig()->getResolveFunction()) {
243 19
            return $resolveFunc($contextValue, $this->parseArgumentsValues($field, $query), $resolveInfo);
244 9
        } elseif ($propertyValue = TypeService::getPropertyValue($contextValue, $field->getName())) {
245 1
            return $propertyValue;
246
        } else {
247 8
            return $field->resolve($contextValue, $this->parseArgumentsValues($field, $query), $resolveInfo);
248
        }
249
    }
250
251
    /**
252
     * @param               $contextValue
253
     * @param FieldAst      $fieldAst
254
     * @param AbstractField $field
255
     *
256
     * @throws \Exception
257
     *
258
     * @return mixed
259
     */
260 18
    protected function getPreResolvedValue($contextValue, FieldAst $fieldAst, AbstractField $field)
261
    {
262 18
        $resolved      = false;
263 18
        $resolverValue = null;
264
265 18
        if (is_array($contextValue) && array_key_exists($fieldAst->getName(), $contextValue)) {
266 14
            $resolverValue = $contextValue[$fieldAst->getName()];
267 14
            $resolved      = true;
268 18
        } elseif (is_object($contextValue)) {
269 6
            $resolverValue = TypeService::getPropertyValue($contextValue, $fieldAst->getName());
270 6
            $resolved      = true;
271 6
        }
272
273 18
        if (!$resolved && $field->getType()->getNamedType()->getKind() == TypeMap::KIND_SCALAR) {
274 3
            $resolved = true;
275 3
        }
276
277 18
        if ($resolveFunction = $field->getConfig()->getResolveFunction()) {
278 3
            $resolveInfo = new ResolveInfo($field, [$fieldAst], $field->getType(), $this->executionContext);
279
280 3
            if (!$this->resolveValidator->validateArguments($field, $fieldAst, $this->executionContext->getRequest())) {
281
                throw new \Exception(sprintf('Not valid arguments for the field "%s"', $fieldAst->getName()));
282
283
            } else {
284 3
                $resolverValue = $resolveFunction($resolved ? $resolverValue : $contextValue, $fieldAst->getKeyValueArguments(), $resolveInfo);
285
            }
286
287 3
        }
288
289 18
        if (!$resolverValue && !$resolved) {
290 1
            throw new \Exception(sprintf('Property "%s" not found in resolve result', $fieldAst->getName()));
291
        }
292
293 18
        return $resolverValue;
294
    }
295
296
    /**
297
     * @param $field     AbstractField
298
     * @param $query     Query
299
     *
300
     * @return array
301
     */
302 21
    protected function parseArgumentsValues(AbstractField $field, Query $query)
303
    {
304 21
        $args = [];
305 21
        foreach ($query->getArguments() as $argument) {
306 9
            if ($configArgument = $field->getConfig()->getArgument($argument->getName())) {
307 9
                $args[$argument->getName()] = $configArgument->getType()->parseValue($argument->getValue()->getValue());
308 9
            }
309 21
        }
310
311 21
        return $args;
312
    }
313
314
    /**
315
     * @param $query         Query|FragmentInterface
316
     * @param $queryType     AbstractObjectType|TypeInterface|Field|AbstractType
317
     * @param $resolvedValue mixed
318
     * @param $value         array
319
     *
320
     * @throws \Exception
321
     *
322
     * @return array
323
     */
324 19
    protected function processQueryFields($query, AbstractType $queryType, $resolvedValue, $value)
325
    {
326 19
        foreach ($query->getFields() as $fieldAst) {
327 19
            $fieldResolvedValue = null;
328
329 19
            if ($fieldAst instanceof FragmentInterface) {
330
                /** @var TypedFragmentReference $fragment */
331 3
                $fragment = $fieldAst;
332 3
                if ($fieldAst instanceof FragmentReference) {
333
                    /** @var Fragment $fragment */
334 2
                    $fragment = $this->executionContext->getRequest()->getFragment($fieldAst->getName());
335 2
                    $this->resolveValidator->assertValidFragmentForField($fragment, $fieldAst, $queryType);
336 3
                } elseif ($fragment->getTypeName() !== $queryType->getName()) {
337 1
                    continue;
338
                }
339
340 3
                $fragmentValue      = $this->processQueryFields($fragment, $queryType, $resolvedValue, $value);
341 3
                $fieldResolvedValue = is_array($fragmentValue) ? $fragmentValue : [];
342 3
            } else {
343 19
                $alias       = $fieldAst->getAlias() ?: $fieldAst->getName();
344 19
                $currentType = $queryType->getNullableType();
345
346 19
                if ($fieldAst->getName() == self::TYPE_NAME_QUERY) {
347 1
                    $fieldResolvedValue = [$alias => $queryType->getName()];
348 1
                } else {
349 19
                    if (!$this->resolveValidator->objectHasField($currentType, $fieldAst)) {
350 3
                        $fieldResolvedValue = null;
351 3
                    } else {
352 19
                        if ($fieldAst instanceof Query) {
353 10
                            $queryAst           = $currentType->getField($fieldAst->getName());
354 10
                            $fieldValue         = $queryAst ? $this->processQueryAST($fieldAst, $queryAst, $resolvedValue) : null;
355 10
                            $fieldResolvedValue = [$alias => $fieldValue];
356 19
                        } elseif ($fieldAst instanceof FieldAst) {
357
                            $fieldResolvedValue = [
358 18
                                $alias => $this->processFieldAST($fieldAst, $currentType->getField($fieldAst->getName()), $resolvedValue)
359 18
                            ];
360 18
                        }
361
                    }
362
363
364
                }
365
            }
366
367 19
            $value = $this->collectValue($value, $fieldResolvedValue);
368 19
        }
369
370 19
        return $value;
371
    }
372
373 18
    protected function getFieldValidatedValue(AbstractField $field, $value)
374
    {
375 18
        return ($this->resolveValidator->isValidValueForField($field, $value)) ? $this->getOutputValue($field->getType(), $value) : null;
376
    }
377
378 18
    protected function getOutputValue(AbstractType $type, $value)
379
    {
380 18
        return in_array($type->getKind(), [TypeMap::KIND_OBJECT, TypeMap::KIND_NON_NULL]) ? $value : $type->serialize($value);
381
    }
382
383 19
    protected function collectValue($value, $queryValue)
384
    {
385 19
        if ($queryValue && is_array($queryValue)) {
386 19
            $value = array_merge(is_array($value) ? $value : [], $queryValue);
387 19
        } else {
388 3
            $value = $queryValue;
389
        }
390
391 19
        return $value;
392
    }
393
394 23
    protected function introduceIntrospectionFields(AbstractSchema $schema)
395
    {
396 23
        $schemaField = new SchemaField();
397 23
        $schemaField->setSchema($schema);
398
399 23
        $schema->addQueryField($schemaField);
400 23
        $schema->addQueryField(new TypeDefinitionField());
401 23
    }
402
403 23
    public function getResponseData()
404
    {
405 23
        $result = [];
406
407 23
        if (!empty($this->data)) {
408 21
            $result['data'] = $this->data;
409 21
        }
410
411 23
        if ($this->executionContext->hasErrors()) {
412 7
            $result['errors'] = $this->executionContext->getErrorsArray();
413 7
        }
414
415 23
        return $result;
416
    }
417
418 1
    public function setMaxComplexity($max) {
419 1
        $this->maxComplexity = $max;
420 1
    }
421
422
    /**
423
     * @param AbstractType $queryType
424
     * @param AbstractType $mutationType
425
     * @param array        $reducers
426
     */
427 23
    protected function reduceQuery($queryType, $mutationType, array $reducers) {
428 23
        foreach ($reducers as $reducer) {
429 2
            foreach ($this->executionContext->getRequest()->getOperationsInOrder() as $operation) {
430 2
                $this->doVisit($operation, $operation instanceof Mutation ? $mutationType : $queryType, $reducer);
431 2
            }
432 23
        }
433 23
    }
434
435
    /**
436
     * @param Query                $query
437
     * @param AbstractType         $currentLevelSchema
438
     * @param AbstractQueryVisitor $reducer
439
     */
440 2
    protected function doVisit(Query $query, $currentLevelSchema, $reducer)
441
    {
442 2
        if (!($currentLevelSchema instanceof AbstractObjectType) || !$currentLevelSchema->hasField($query->getName())) {
443
            return;
444
        }
445
446 2
        if ($operationField = $currentLevelSchema->getField($query->getName())) {
447
448 2
            $coroutine = $this->walkQuery($query, $operationField);
449
450 2
            if ($results = $coroutine->current()) {
451 2
                $queryCost = 0;
452 2
                while ($results) {
453
                    // initial values come from advancing the generator via ->current, subsequent values come from ->send()
454 2
                    list($queryField, $astField, $childCost) = $results;
455
456
                    /**
457
                     * @var Query|FieldAst $queryField
458
                     * @var Field          $astField
459
                     */
460 2
                    $cost = $reducer->visit($queryField->getKeyValueArguments(), $astField->getConfig(), $childCost);
461 2
                    $queryCost += $cost;
462 2
                    $results = $coroutine->send($cost);
463 2
                }
464 2
            }
465 2
        }
466 2
    }
467
468
    /**
469
     * Coroutine to walk the query and schema in DFS manner and yield a tuple of (queryNode, schemaNode, childScore)
470
     *
471
     * childScore costs are accumulated via values sent into the coroutine.
472
     *
473
     * @param Query|Field|FragmentInterface $queryNode
474
     * @param AbstractField                 $currentLevelAST
475
     *
476
     * @return \Generator
477
     */
478 2
    protected function walkQuery($queryNode, AbstractField $currentLevelAST) {
479 2
        $childrenScore = 0;
480 2
        if (!($queryNode instanceof FieldAst)) {
481 2
            foreach ($queryNode->getFields() as $queryField) {
0 ignored issues
show
Bug introduced by
The method getFields does only exist in Youshido\GraphQL\Field\F...raphQL\Parser\Ast\Query, but not in Youshido\GraphQL\Parser\Ast\FragmentInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
482 2
                if ($queryField instanceof FragmentInterface) {
483 1
                    if ($queryField instanceof FragmentReference) {
484
                        $queryField = $this->executionContext->getRequest()->getFragment($queryField->getName());
485
                    }
486
                    // the next 7 lines are essentially equivalent to `yield from $this->walkQuery(...)` in PHP7.
487
                    // for backwards compatibility this is equivalent.
488
                    // This pattern is repeated multiple times in this function, and unfortunately cannot be extracted or
489
                    // made less verbose.
490 1
                    $gen  = $this->walkQuery($queryField, $currentLevelAST);
0 ignored issues
show
Bug introduced by
It seems like $queryField defined by $this->executionContext-...$queryField->getName()) on line 484 can also be of type null or object<Youshido\GraphQL\Parser\Ast\Fragment>; however, Youshido\GraphQL\Execution\Processor::walkQuery() does only seem to accept object<Youshido\GraphQL\...\Ast\FragmentInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
491 1
                    $next = $gen->current();
492 1
                    while ($next) {
493 1
                        $received = (yield $next);
494 1
                        $childrenScore += (int)$received;
495 1
                        $next     = $gen->send($received);
496 1
                    }
497 1
                } else {
498 2
                    $fieldType = $currentLevelAST->getType()->getNamedType();
499 2
                    if ($fieldType instanceof AbstractUnionType) {
500 1
                        foreach ($fieldType->getTypes() as $unionFieldType) {
501 1 View Code Duplication
                            if ($fieldAst = $unionFieldType->getField($queryField->getName())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
502 1
                                $gen  = $this->walkQuery($queryField, $fieldAst);
503 1
                                $next = $gen->current();
504 1
                                while ($next) {
505 1
                                    $received = (yield $next);
506 1
                                    $childrenScore += (int)$received;
507 1
                                    $next = $gen->send($received);
508 1
                                }
509 1
                            }
510 1
                        }
511 2 View Code Duplication
                    } elseif ($fieldType instanceof AbstractObjectType && $fieldAst = $fieldType->getField($queryField->getName())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
512 1
                        $gen  = $this->walkQuery($queryField, $fieldAst);
513 1
                        $next = $gen->current();
514 1
                        while ($next) {
515 1
                            $received = (yield $next);
516 1
                            $childrenScore += (int)$received;
517 1
                            $next = $gen->send($received);
518 1
                        }
519 1
                    }
520
                }
521 2
            }
522 2
        }
523
        // sanity check.  don't yield fragments; they don't contribute to cost
524 2
        if ($queryNode instanceof Query || $queryNode instanceof FieldAst) {
525 2
            yield [$queryNode, $currentLevelAST, $childrenScore];
526 2
        }
527 2
    }
528
}
529