Completed
Push — master ( b7907c...708c0e )
by Alexandr
15:31 queued 11:30
created

Processor::collectResult()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 54

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 10.0045

Importance

Changes 0
Metric Value
dl 0
loc 54
ccs 27
cts 28
cp 0.9643
rs 7.1369
c 0
b 0
f 0
cc 10
nc 10
nop 4
crap 10.0045

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Date: 03.11.16
4
 *
5
 * @author Portey Vasil <[email protected]>
6
 */
7
8
namespace Youshido\GraphQL\Execution;
9
10
11
use Youshido\GraphQL\Exception\ResolveException;
12
use Youshido\GraphQL\Execution\Container\Container;
13
use Youshido\GraphQL\Execution\Context\ExecutionContext;
14
use Youshido\GraphQL\Execution\Visitor\MaxComplexityQueryVisitor;
15
use Youshido\GraphQL\Field\Field;
16
use Youshido\GraphQL\Field\FieldInterface;
17
use Youshido\GraphQL\Field\InputField;
18
use Youshido\GraphQL\Parser\Ast\ArgumentValue\InputList as AstInputList;
19
use Youshido\GraphQL\Parser\Ast\ArgumentValue\InputObject as AstInputObject;
20
use Youshido\GraphQL\Parser\Ast\ArgumentValue\Literal as AstLiteral;
21
use Youshido\GraphQL\Parser\Ast\ArgumentValue\VariableReference;
22
use Youshido\GraphQL\Parser\Ast\Field as AstField;
23
use Youshido\GraphQL\Parser\Ast\FragmentReference;
24
use Youshido\GraphQL\Parser\Ast\Interfaces\FieldInterface as AstFieldInterface;
25
use Youshido\GraphQL\Parser\Ast\Mutation as AstMutation;
26
use Youshido\GraphQL\Parser\Ast\Query as AstQuery;
27
use Youshido\GraphQL\Parser\Ast\TypedFragmentReference;
28
use Youshido\GraphQL\Parser\Parser;
29
use Youshido\GraphQL\Schema\AbstractSchema;
30
use Youshido\GraphQL\Type\AbstractType;
31
use Youshido\GraphQL\Type\Enum\AbstractEnumType;
32
use Youshido\GraphQL\Type\InputObject\AbstractInputObjectType;
33
use Youshido\GraphQL\Type\InterfaceType\AbstractInterfaceType;
34
use Youshido\GraphQL\Type\ListType\AbstractListType;
35
use Youshido\GraphQL\Type\Object\AbstractObjectType;
36
use Youshido\GraphQL\Type\Scalar\AbstractScalarType;
37
use Youshido\GraphQL\Type\TypeMap;
38
use Youshido\GraphQL\Type\Union\AbstractUnionType;
39
use Youshido\GraphQL\Validator\RequestValidator\RequestValidator;
40
use Youshido\GraphQL\Validator\ResolveValidator\ResolveValidator;
41
use Youshido\GraphQL\Validator\ResolveValidator\ResolveValidatorInterface;
42
43
class Processor
44
{
45
46
    const TYPE_NAME_QUERY = '__typename';
47
48
    /** @var ExecutionContext */
49
    protected $executionContext;
50
51
    /** @var ResolveValidatorInterface */
52
    protected $resolveValidator;
53
54
    /** @var  array */
55
    protected $data;
56
57
    /** @var int */
58
    protected $maxComplexity;
59
60
    /** @var array DeferredResult[] */
61
    protected $deferredResultsLeaf = [];
62
63
    /** @var array DeferredResult[] */
64
    protected $deferredResultsComplex = [];
65
66 71
    public function __construct(AbstractSchema $schema)
67
    {
68 71
        if (empty($this->executionContext)) {
69 71
            $this->executionContext = new ExecutionContext($schema);
70 71
            $this->executionContext->setContainer(new Container());
71
        }
72
73 71
        $this->resolveValidator = new ResolveValidator($this->executionContext);
0 ignored issues
show
Unused Code introduced by
The call to ResolveValidator::__construct() has too many arguments starting with $this->executionContext.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
74 71
    }
75
76 69
    public function processPayload($payload, $variables = [], $reducers = [])
77
    {
78 69
        $this->data = [];
79
80
        try {
81 69
            $this->parseAndCreateRequest($payload, $variables);
82
83 68
            if ($this->maxComplexity) {
84 1
                $reducers[] = new MaxComplexityQueryVisitor($this->maxComplexity);
85
            }
86
87 68
            if ($reducers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $reducers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
88 2
                $reducer = new Reducer();
89 2
                $reducer->reduceQuery($this->executionContext, $reducers);
90
            }
91
92 68
            foreach ($this->executionContext->getRequest()->getAllOperations() as $query) {
93 68
                if ($operationResult = $this->resolveQuery($query)) {
94 68
                    $this->data = array_replace_recursive($this->data, $operationResult);
95
                };
96
            }
97
98
            // If the processor found any deferred results, resolve them now.
99 68
            if (!empty($this->data) && (!empty($this->deferredResultsLeaf) || !empty($this->deferredResultsComplex))) {
100
              try {
101 4
                  while ($deferredResolver = array_shift($this->deferredResultsComplex)) {
102 4
                      $deferredResolver->resolve();
103
                  }
104
105
                  // Deferred scalar and enum fields should be resolved last to
106
                  // pick up as many as possible for a single batch.
107 4
                  while ($deferredResolver = array_shift($this->deferredResultsLeaf)) {
108
                      $deferredResolver->resolve();
109
                  }
110
              } catch (\Exception $e) {
111
                  $this->executionContext->addError($e);
112 4
              } finally {
113 68
                  $this->data = static::unpackDeferredResults($this->data);
114
              }
115
            }
116
117 5
        } catch (\Exception $e) {
118 5
            $this->executionContext->addError($e);
119
        }
120
121 69
        return $this;
122
    }
123
124
    /**
125
     * Unpack results stored inside deferred resolvers.
126
     *
127
     * @param mixed $result
128
     *   The result ree.
129
     *
130
     * @return mixed
131
     *   The unpacked result.
132
     */
133 4
    public static function unpackDeferredResults($result)
134
    {
135 4
        while ($result instanceof DeferredResult) {
136 4
            $result = $result->result;
137
        }
138
139 4
        if (is_array($result)) {
140 4
            foreach ($result as $key => $value) {
141 4
                $result[$key] = static::unpackDeferredResults($value);
142
            }
143
        }
144
145 4
        return $result;
146
    }
147
148 68
    protected function resolveQuery(AstQuery $query)
149
    {
150 68
        $schema = $this->executionContext->getSchema();
151 68
        $type   = $query instanceof AstMutation ? $schema->getMutationType() : $schema->getQueryType();
152 68
        $field  = new Field([
153 68
            'name' => $query instanceof AstMutation ? 'mutation' : 'query',
154 68
            'type' => $type
155
        ]);
156
157 68
        if (self::TYPE_NAME_QUERY == $query->getName()) {
158 1
            return [$this->getAlias($query) => $type->getName()];
159
        }
160
161 68
        $this->resolveValidator->assetTypeHasField($type, $query);
162 68
        $value = $this->resolveField($field, $query);
163
164 68
        return [$this->getAlias($query) => $value];
165
    }
166
167 68
    protected function resolveField(FieldInterface $field, AstFieldInterface $ast, $parentValue = null, $fromObject = false)
168
    {
169
        try {
170
            /** @var AbstractObjectType $type */
171 68
            $type        = $field->getType();
172 68
            $nonNullType = $type->getNullableType();
173
174 68
            if (self::TYPE_NAME_QUERY == $ast->getName()) {
175 2
                return $nonNullType->getName();
176
            }
177
178 68
            $this->resolveValidator->assetTypeHasField($nonNullType, $ast);
179
180 68
            $targetField = $nonNullType->getField($ast->getName());
181
182 68
            $this->prepareAstArguments($targetField, $ast, $this->executionContext->getRequest());
183 67
            $this->resolveValidator->assertValidArguments($targetField, $ast, $this->executionContext->getRequest());
184
185 62
            switch ($kind = $targetField->getType()->getNullableType()->getKind()) {
186 62
                case TypeMap::KIND_ENUM:
187 61
                case TypeMap::KIND_SCALAR:
188 54
                    if ($ast instanceof AstQuery && $ast->hasFields()) {
189 2
                        throw new ResolveException(sprintf('You can\'t specify fields for scalar type "%s"', $targetField->getType()->getNullableType()->getName()), $ast->getLocation());
190
                    }
191
192 54
                    return $this->resolveScalar($targetField, $ast, $parentValue);
193
194 45 View Code Duplication
                case TypeMap::KIND_OBJECT:
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...
195
                    /** @var $type AbstractObjectType */
196 31
                    if (!$ast instanceof AstQuery) {
197 1
                        throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation());
198
                    }
199
200 31
                    return $this->resolveObject($targetField, $ast, $parentValue);
201
202 31
                case TypeMap::KIND_LIST:
203 28
                    return $this->resolveList($targetField, $ast, $parentValue);
204
205 6
                case TypeMap::KIND_UNION:
206 5 View Code Duplication
                case TypeMap::KIND_INTERFACE:
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...
207 6
                    if (!$ast instanceof AstQuery) {
208
                        throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation());
209
                    }
210
211 6
                    return $this->resolveComposite($targetField, $ast, $parentValue);
212
213
                default:
214
                    throw new ResolveException(sprintf('Resolving type with kind "%s" not supported', $kind));
215
            }
216 17
        } catch (\Exception $e) {
217 17
            $this->executionContext->addError($e);
218
219 17
            if ($fromObject) {
220 4
                throw $e;
221
            }
222
223 15
            return null;
224
        }
225
    }
226
227 68
    private function prepareAstArguments(FieldInterface $field, AstFieldInterface $query, Request $request)
228
    {
229 68
        foreach ($query->getArguments() as $astArgument) {
230 38
            if ($field->hasArgument($astArgument->getName())) {
231 38
                $argumentType = $field->getArgument($astArgument->getName())->getType()->getNullableType();
232
233 38
                $astArgument->setValue($this->prepareArgumentValue($astArgument->getValue(), $argumentType, $request));
234
            }
235
        }
236 67
    }
237
238 38
    private function prepareArgumentValue($argumentValue, AbstractType $argumentType, Request $request)
239
    {
240 38
        switch ($argumentType->getKind()) {
241 38
            case TypeMap::KIND_LIST:
242
                /** @var $argumentType AbstractListType */
243 12
                $result = [];
244 12
                if ($argumentValue instanceof AstInputList || is_array($argumentValue)) {
245 9
                    $list = is_array($argumentValue) ? $argumentValue : $argumentValue->getValue();
246 9
                    foreach ($list as $item) {
247 9
                        $result[] = $this->prepareArgumentValue($item, $argumentType->getItemType()->getNullableType(), $request);
248
                    }
249
                } else {
250 3
                    if ($argumentValue instanceof VariableReference) {
251 3
                        return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
252
                    }
253
                }
254
255 9
                return $result;
256
257 37
            case TypeMap::KIND_INPUT_OBJECT:
258
                /** @var $argumentType AbstractInputObjectType */
259 6
                $result = [];
260 6
                if ($argumentValue instanceof AstInputObject) {
261 5
                    foreach ($argumentType->getFields() as $field) {
262
                        /** @var $field Field */
263 5
                        if ($field->getConfig()->has('defaultValue')) {
264 5
                            $result[$field->getName()] = $field->getType()->getNullableType()->parseInputValue($field->getConfig()->get('defaultValue'));
265
                        }
266
                    }
267 5
                    foreach ($argumentValue->getValue() as $key => $item) {
268 5
                        if ($argumentType->hasField($key)) {
269 5
                            $result[$key] = $this->prepareArgumentValue($item, $argumentType->getField($key)->getType()->getNullableType(), $request);
270
                        } else {
271 5
                            $result[$key] = $item;
272
                        }
273
                    }
274
                } else {
275 2
                    if ($argumentValue instanceof VariableReference) {
276
                        return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
277
                    } else {
278 2
                        if (is_array($argumentValue)) {
279 1
                            return $argumentValue;
280
                        }
281
                    }
282
                }
283
284 6
                return $result;
285
286 36
            case TypeMap::KIND_SCALAR:
287 4
            case TypeMap::KIND_ENUM:
288
                /** @var $argumentValue AstLiteral|VariableReference */
289 36
                if ($argumentValue instanceof VariableReference) {
290 7
                    return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
291
                } else {
292 31
                    if ($argumentValue instanceof AstLiteral) {
293 19
                        return $argumentValue->getValue();
294
                    } else {
295 13
                        return $argumentValue;
296
                    }
297
                }
298
        }
299
300
        throw new ResolveException('Argument type not supported');
301
    }
302
303 9
    private function getVariableReferenceArgumentValue(VariableReference $variableReference, AbstractType $argumentType, Request $request)
304
    {
305 9
        $variable = $variableReference->getVariable();
306 9
        if ($argumentType->getKind() === TypeMap::KIND_LIST) {
307
            if (
308 3
                (!$variable->isArray() && !is_array($variable->getValue())) ||
309 3
                ($variable->getTypeName() !== $argumentType->getNamedType()->getNullableType()->getName()) ||
310 3
                ($argumentType->getNamedType()->getKind() === TypeMap::KIND_NON_NULL && $variable->isArrayElementNullable())
311
            ) {
312 3
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getNamedType()->getNullableType()->getName()), $variable->getLocation());
313
            }
314
        } else {
315 7
            if ($variable->getTypeName() !== $argumentType->getName()) {
316 1
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getName()), $variable->getLocation());
317
            }
318
        }
319
320 8
        $requestValue = $request->getVariable($variable->getName());
321 8
        if ((null === $requestValue && $variable->isNullable()) && !$request->hasVariable($variable->getName())) {
322
            throw new ResolveException(sprintf('Variable "%s" does not exist in request', $variable->getName()), $variable->getLocation());
323
        }
324
325 8
        return $requestValue;
326
    }
327
328
329
    /**
330
     * @param FieldInterface     $field
331
     * @param AbstractObjectType $type
332
     * @param AstFieldInterface  $ast
333
     * @param                    $resolvedValue
334
     * @return array
335
     */
336 40
    private function collectResult(FieldInterface $field, AbstractObjectType $type, $ast, $resolvedValue)
337
    {
338 40
        $result = [];
339
340 40
        foreach ($ast->getFields() as $astField) {
341
            switch (true) {
342 40
                case $astField instanceof TypedFragmentReference:
343 2
                    $astName  = $astField->getTypeName();
344 2
                    $typeName = $type->getName();
345
346 2
                    if ($typeName !== $astName) {
347 2
                        foreach ($type->getInterfaces() as $interface) {
348 1
                            if ($interface->getName() === $astName) {
349
                                $result = array_replace_recursive($result, $this->collectResult($field, $type, $astField, $resolvedValue));
0 ignored issues
show
Documentation introduced by
$astField is of type object<Youshido\GraphQL\...TypedFragmentReference>, but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
350
351 1
                                break;
352
                            }
353
                        }
354
355 2
                        continue 2;
356
                    }
357
358 2
                    $result = array_replace_recursive($result, $this->collectResult($field, $type, $astField, $resolvedValue));
0 ignored issues
show
Documentation introduced by
$astField is of type object<Youshido\GraphQL\...TypedFragmentReference>, but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
359
360 2
                    break;
361
362 40
                case $astField instanceof FragmentReference:
363 8
                    $astFragment      = $this->executionContext->getRequest()->getFragment($astField->getName());
364 8
                    $astFragmentModel = $astFragment->getModel();
365 8
                    $typeName         = $type->getName();
366
367 8
                    if ($typeName !== $astFragmentModel) {
368 2
                        foreach ($type->getInterfaces() as $interface) {
369 1
                            if ($interface->getName() === $astFragmentModel) {
370 1
                                $result = array_replace_recursive($result, $this->collectResult($field, $type, $astFragment, $resolvedValue));
0 ignored issues
show
Documentation introduced by
$astFragment is of type object<Youshido\GraphQL\Parser\Ast\Fragment>|null, but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
371
372 1
                                break;
373
                            }
374
                        }
375
376 2
                        continue 2;
377
                    }
378
379 7
                    $result = array_replace_recursive($result, $this->collectResult($field, $type, $astFragment, $resolvedValue));
0 ignored issues
show
Documentation introduced by
$astFragment is of type object<Youshido\GraphQL\Parser\Ast\Fragment>|null, but the function expects a object<Youshido\GraphQL\...erfaces\FieldInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
380
381 7
                    break;
382
383
                default:
384 39
                    $result = array_replace_recursive($result, [$this->getAlias($astField) => $this->resolveField($field, $astField, $resolvedValue, true)]);
385
            }
386
        }
387
388 40
        return $result;
389
    }
390
391
    /**
392
     * Apply post-process callbacks to all deferred resolvers.
393
     */
394 62
    protected function deferredResolve($resolvedValue, FieldInterface $field, callable $callback) {
395 62
        if ($resolvedValue instanceof DeferredResolverInterface) {
396 4
            $deferredResult = new DeferredResult($resolvedValue, $callback);
397
398
            // Whenever we stumble upon a deferred resolver, add it to the queue
399
            // to be resolved later.
400 4
            $type = $field->getType()->getNamedType();
401 4
            if ($type instanceof AbstractScalarType || $type instanceof AbstractEnumType) {
402
                array_push($this->deferredResultsLeaf, $deferredResult);
403
            } else {
404 4
                array_push($this->deferredResultsComplex, $deferredResult);
405
            }
406
407 4
            return $deferredResult;
408
        }
409
        // For simple values, invoke the callback immediately.
410 62
        return $callback($resolvedValue);
411
    }
412
413 55
    protected function resolveScalar(FieldInterface $field, AstFieldInterface $ast, $parentValue)
414
    {
415 55
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
416
        return $this->deferredResolve($resolvedValue, $field, function($resolvedValue) use ($field, $ast, $parentValue) {
417 55
            $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
418
419
            /** @var AbstractScalarType $type */
420 54
            $type = $field->getType()->getNullableType();
421
422 54
            return $type->serialize($resolvedValue);
423 55
        });
424
    }
425
426 28
    protected function resolveList(FieldInterface $field, AstFieldInterface $ast, $parentValue)
427
    {
428
        /** @var AstQuery $ast */
429 28
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
430
431
        return $this->deferredResolve($resolvedValue, $field, function ($resolvedValue) use ($field, $ast, $parentValue) {
432 28
            $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
433
434 26
            if (null === $resolvedValue) {
435 8
                return null;
436
            }
437
438
            /** @var AbstractListType $type */
439 25
            $type     = $field->getType()->getNullableType();
440 25
            $itemType = $type->getNamedType();
441
442 25
            $fakeAst = clone $ast;
443 25
            if ($fakeAst instanceof AstQuery) {
444 24
                $fakeAst->setArguments([]);
445
            }
446
447 25
            $fakeField = new Field([
448 25
              'name' => $field->getName(),
449 25
              'type' => $itemType,
450 25
              'args' => $field->getArguments(),
451
            ]);
452
453 25
            $result = [];
454 25
            foreach ($resolvedValue as $resolvedValueItem) {
455
                try {
456
                    $fakeField->getConfig()->set('resolve', function () use ($resolvedValueItem) {
457 24
                        return $resolvedValueItem;
458 24
                    });
459
460 24
                    switch ($itemType->getNullableType()->getKind()) {
461 24
                        case TypeMap::KIND_ENUM:
462 23
                        case TypeMap::KIND_SCALAR:
463 6
                            $value = $this->resolveScalar($fakeField, $fakeAst, $resolvedValueItem);
464
465 5
                            break;
466
467
468 20
                        case TypeMap::KIND_OBJECT:
469 17
                            $value = $this->resolveObject($fakeField, $fakeAst, $resolvedValueItem);
470
471 17
                            break;
472
473 4
                        case TypeMap::KIND_UNION:
474 3
                        case TypeMap::KIND_INTERFACE:
475 4
                            $value = $this->resolveComposite($fakeField, $fakeAst, $resolvedValueItem);
476
477 4
                            break;
478
479
                        default:
480 23
                            $value = null;
481
                    }
482 1
                } catch (\Exception $e) {
483 1
                    $this->executionContext->addError($e);
484
485 1
                    $value = null;
486
                }
487
488 24
                $result[] = $value;
489
            }
490
491 25
            return $result;
492 28
        });
493
    }
494
495 41
    protected function resolveObject(FieldInterface $field, AstFieldInterface $ast, $parentValue, $fromUnion = false)
496
    {
497 41
        $resolvedValue = $parentValue;
498 41
        if (!$fromUnion) {
499 35
            $resolvedValue = $this->doResolve($field, $ast, $parentValue);
500
        }
501
502
        return $this->deferredResolve($resolvedValue, $field, function ($resolvedValue) use ($field, $ast, $parentValue) {
503 41
            $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
504
505 41
            if (null === $resolvedValue) {
506 8
                return null;
507
            }
508
            /** @var AbstractObjectType $type */
509 40
            $type = $field->getType()->getNullableType();
510
511
            try {
512 40
                return $this->collectResult($field, $type, $ast, $resolvedValue);
513 4
            } catch (\Exception $e) {
514 4
                return null;
515
            }
516 41
        });
517
    }
518
519 8
    protected function resolveComposite(FieldInterface $field, AstFieldInterface $ast, $parentValue)
520
    {
521
        /** @var AstQuery $ast */
522 8
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
523 8
        return $this->deferredResolve($resolvedValue, $field, function ($resolvedValue) use ($field, $ast, $parentValue) {
524 8
            $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
525
526 8
            if (null === $resolvedValue) {
527
                return null;
528
            }
529
530
            /** @var AbstractUnionType $type */
531 8
            $type         = $field->getType()->getNullableType();
532 8
            $resolveInfo = new ResolveInfo(
533 8
                $field,
534 8
                $ast instanceof AstQuery ? $ast->getFields() : [],
535 8
                $this->executionContext
536
            );
537 8
            $resolvedType = $type->resolveType($resolvedValue, $resolveInfo);
0 ignored issues
show
Unused Code introduced by
The call to AbstractUnionType::resolveType() has too many arguments starting with $resolveInfo.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
538
539 8
            if (!$resolvedType) {
540
                throw new ResolveException('Resolving function must return type');
541
            }
542
543 8
            if ($type instanceof AbstractInterfaceType) {
544 6
                $this->resolveValidator->assertTypeImplementsInterface($resolvedType, $type);
545
            } else {
546 2
                $this->resolveValidator->assertTypeInUnionTypes($resolvedType, $type);
547
            }
548
549 8
            $fakeField = new Field([
550 8
              'name' => $field->getName(),
551 8
              'type' => $resolvedType,
552 8
              'args' => $field->getArguments(),
553
            ]);
554
555 8
            return $this->resolveObject($fakeField, $ast, $resolvedValue, true);
556 8
        });
557
    }
558
559 69
    protected function parseAndCreateRequest($payload, $variables = [])
560
    {
561 69
        if (empty($payload)) {
562 1
            throw new \InvalidArgumentException('Must provide an operation.');
563
        }
564
565 69
        $parser  = new Parser();
566 69
        $request = new Request($parser->parse($payload), $variables);
567
568 69
        (new RequestValidator())->validate($request);
569
570 68
        $this->executionContext->setRequest($request);
571 68
    }
572
573 62
    protected function doResolve(FieldInterface $field, AstFieldInterface $ast, $parentValue = null)
574
    {
575
        /** @var AstQuery|AstField $ast */
576 62
        $arguments = $this->parseArgumentsValues($field, $ast);
577 62
        $astFields = $ast instanceof AstQuery ? $ast->getFields() : [];
578
579 62
        return $field->resolve($parentValue, $arguments, $this->createResolveInfo($field, $astFields));
580
    }
581
582 62
    protected function parseArgumentsValues(FieldInterface $field, AstFieldInterface $ast)
583
    {
584 62
        $values   = [];
585 62
        $defaults = [];
586
587 62
        foreach ($field->getArguments() as $argument) {
588
            /** @var $argument InputField */
589 45
            if ($argument->getConfig()->has('defaultValue')) {
590 45
                $defaults[$argument->getName()] = $argument->getConfig()->getDefaultValue();
591
            }
592
        }
593
594 62
        foreach ($ast->getArguments() as $astArgument) {
595 34
            $argument     = $field->getArgument($astArgument->getName());
596 34
            $argumentType = $argument->getType()->getNullableType();
597
598 34
            $values[$argument->getName()] = $argumentType->parseValue($astArgument->getValue());
599
600 34
            if (isset($defaults[$argument->getName()])) {
601 34
                unset($defaults[$argument->getName()]);
602
            }
603
        }
604
605 62
        return array_merge($values, $defaults);
606
    }
607
608 68
    private function getAlias(AstFieldInterface $ast)
609
    {
610 68
        return $ast->getAlias() ?: $ast->getName();
611
    }
612
613 62
    protected function createResolveInfo(FieldInterface $field, array $astFields)
614
    {
615 62
        return new ResolveInfo($field, $astFields, $this->executionContext);
616
    }
617
618
619
    /**
620
     * You can access ExecutionContext to check errors and inject dependencies
621
     *
622
     * @return ExecutionContext
623
     */
624 11
    public function getExecutionContext()
625
    {
626 11
        return $this->executionContext;
627
    }
628
629 68
    public function getResponseData()
630
    {
631 68
        $result = [];
632
633 68
        if (!empty($this->data)) {
634 67
            $result['data'] = $this->data;
635
        }
636
637 68
        if ($this->executionContext->hasErrors()) {
638 19
            $result['errors'] = $this->executionContext->getErrorsArray();
639
        }
640
641 68
        return $result;
642
    }
643
644
    /**
645
     * @return int
646
     */
647
    public function getMaxComplexity()
648
    {
649
        return $this->maxComplexity;
650
    }
651
652
    /**
653
     * @param int $maxComplexity
654
     */
655 1
    public function setMaxComplexity($maxComplexity)
656
    {
657 1
        $this->maxComplexity = $maxComplexity;
658 1
    }
659
660
}
661