Completed
Push — master ( eea4af...ac3565 )
by Alexandr
13:12
created

Processor   F

Complexity

Total Complexity 106

Size/Duplication

Total Lines 535
Duplicated Lines 6.73 %

Coupling/Cohesion

Components 1
Dependencies 32

Test Coverage

Coverage 94.26%

Importance

Changes 0
Metric Value
wmc 106
lcom 1
cbo 32
dl 36
loc 535
ccs 279
cts 296
cp 0.9426
rs 1.3043
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A resolveQuery() 0 18 4
A __construct() 0 9 2
B processPayload() 0 27 6
C resolveField() 13 59 14
A prepareAstArguments() 0 10 3
C prepareArgumentValue() 0 64 19
A resolveObject() 0 21 4
C collectResult() 23 55 10
A resolveScalar() 0 11 1
C resolveList() 0 66 10
B resolveComposite() 0 33 4
A parseAndCreateRequest() 0 13 2
A doResolve() 0 8 2
B parseArgumentsValues() 0 25 5
A getAlias() 0 4 2
A createResolveInfo() 0 4 1
A getExecutionContext() 0 4 1
A getResponseData() 0 14 3
A getMaxComplexity() 0 4 1
A setMaxComplexity() 0 4 1
C getVariableReferenceArgumentValue() 0 24 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Processor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Processor, and based on these observations, apply Extract Interface, too.

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\Query;
28
use Youshido\GraphQL\Parser\Ast\TypedFragmentReference;
29
use Youshido\GraphQL\Parser\Parser;
30
use Youshido\GraphQL\Schema\AbstractSchema;
31
use Youshido\GraphQL\Type\AbstractType;
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 61
    public function __construct(AbstractSchema $schema)
61
    {
62 61
        if (empty($this->executionContext)) {
63 61
            $this->executionContext = new ExecutionContext($schema);
64 61
            $this->executionContext->setContainer(new Container());
65 61
        }
66
67 61
        $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...
68 61
    }
69
70 59
    public function processPayload($payload, $variables = [], $reducers = [])
71
    {
72 59
        $this->data = [];
73
74
        try {
75 59
            $this->parseAndCreateRequest($payload, $variables);
76
77 58
            if ($this->maxComplexity) {
78 1
                $reducers[] = new MaxComplexityQueryVisitor($this->maxComplexity);
79 1
            }
80
81 58
            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...
82 2
                $reducer = new Reducer();
83 2
                $reducer->reduceQuery($this->executionContext, $reducers);
84 2
            }
85
86 58
            foreach ($this->executionContext->getRequest()->getAllOperations() as $query) {
87 58
                if ($operationResult = $this->resolveQuery($query)) {
88 58
                    $this->data = array_merge($this->data, $operationResult);
89 58
                };
90 58
            }
91 59
        } catch (\Exception $e) {
92 5
            $this->executionContext->addError($e);
93
        }
94
95 59
        return $this;
96
    }
97
98 58
    protected function resolveQuery(AstQuery $query)
99
    {
100 58
        $schema = $this->executionContext->getSchema();
101 58
        $type   = $query instanceof AstMutation ? $schema->getMutationType() : $schema->getQueryType();
102 58
        $field  = new Field([
103 58
            'name' => $query instanceof AstMutation ? 'mutation' : 'query',
104
            'type' => $type
105 58
        ]);
106
107 58
        if (self::TYPE_NAME_QUERY == $query->getName()) {
108 1
            return [$this->getAlias($query) => $type->getName()];
109
        }
110
111 58
        $this->resolveValidator->assetTypeHasField($type, $query);
112 58
        $value = $this->resolveField($field, $query);
113
114 58
        return [$this->getAlias($query) => $value];
115
    }
116
117 58
    protected function resolveField(FieldInterface $field, AstFieldInterface $ast, $parentValue = null, $fromObject = false)
118
    {
119
        try {
120
            /** @var AbstractObjectType $type */
121 58
            $type        = $field->getType();
122 58
            $nonNullType = $type->getNullableType();
123
124 58
            if (self::TYPE_NAME_QUERY == $ast->getName()) {
125 2
                return $nonNullType->getName();
126
            }
127
128 58
            $this->resolveValidator->assetTypeHasField($nonNullType, $ast);
129
130 58
            $targetField = $nonNullType->getField($ast->getName());
131
132 58
            $this->prepareAstArguments($targetField, $ast, $this->executionContext->getRequest());
133 57
            $this->resolveValidator->assertValidArguments($targetField, $ast, $this->executionContext->getRequest());
134
135 52
            switch ($kind = $targetField->getType()->getNullableType()->getKind()) {
136 52
                case TypeMap::KIND_ENUM:
137 52
                case TypeMap::KIND_SCALAR:
138 45
                    if ($ast instanceof AstQuery && $ast->hasFields()) {
139 2
                        throw new ResolveException(sprintf('You can\'t specify fields for scalar type "%s"', $targetField->getType()->getNullableType()->getName()), $ast->getLocation());
140
                    }
141
142 45
                    return $this->resolveScalar($targetField, $ast, $parentValue);
143
144 36 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...
145
                    /** @var $type AbstractObjectType */
146 27
                    if (!$ast instanceof AstQuery) {
147 1
                        throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation());
148
                    }
149
150 27
                    return $this->resolveObject($targetField, $ast, $parentValue);
151
152 22
                case TypeMap::KIND_LIST:
153 19
                    return $this->resolveList($targetField, $ast, $parentValue);
154
155 6
                case TypeMap::KIND_UNION:
156 6 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...
157 6
                    if (!$ast instanceof AstQuery) {
158
                        throw new ResolveException(sprintf('You have to specify fields for "%s"', $ast->getName()), $ast->getLocation());
159
                    }
160
161 6
                    return $this->resolveComposite($targetField, $ast, $parentValue);
162
163
                default:
164
                    throw new ResolveException(sprintf('Resolving type with kind "%s" not supported', $kind));
165
            }
166 17
        } catch (\Exception $e) {
167 17
            $this->executionContext->addError($e);
168
169 17
            if ($fromObject) {
170 4
                throw $e;
171
            }
172
173 15
            return null;
174
        }
175
    }
176
177 58
    private function prepareAstArguments(FieldInterface $field, AstFieldInterface $query, Request $request)
178
    {
179 58
        foreach ($query->getArguments() as $astArgument) {
180 33
            if ($field->hasArgument($astArgument->getName())) {
181 33
                $argumentType = $field->getArgument($astArgument->getName())->getType()->getNullableType();
182
183 33
                $astArgument->setValue($this->prepareArgumentValue($astArgument->getValue(), $argumentType, $request));
184 32
            }
185 57
        }
186 57
    }
187
188 33
    private function prepareArgumentValue($argumentValue, AbstractType $argumentType, Request $request)
189
    {
190 33
        switch ($argumentType->getKind()) {
191 33
            case TypeMap::KIND_LIST:
192
                /** @var $argumentType AbstractListType */
193 7
                $result = [];
194 7
                if ($argumentValue instanceof AstInputList || is_array($argumentValue)) {
195 5
                    $list = is_array($argumentValue) ? $argumentValue : $argumentValue->getValue();
196 5
                    foreach ($list as $item) {
197 5
                        $result[] = $this->prepareArgumentValue($item, $argumentType->getItemType()->getNullableType(), $request);
198 5
                    }
199 5
                } else {
200 2
                    if ($argumentValue instanceof VariableReference) {
201 2
                        return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
202
                    }
203
                }
204
205 5
                return $result;
206
207 32
            case TypeMap::KIND_INPUT_OBJECT:
208
                /** @var $argumentType AbstractInputObjectType */
209 6
                $result = [];
210 6
                if ($argumentValue instanceof AstInputObject) {
211 5
                    foreach ($argumentType->getFields() as $field) {
212
                        /** @var $field Field */
213 5
                        if ($field->getConfig()->has('defaultValue')) {
214 1
                            $result[$field->getName()] = $field->getType()->getNullableType()->parseInputValue($field->getConfig()->get('defaultValue'));
215 1
                        }
216 5
                    }
217 5
                    foreach ($argumentValue->getValue() as $key => $item) {
218 5
                        if ($argumentType->hasField($key)) {
219 5
                            $result[$key] = $this->prepareArgumentValue($item, $argumentType->getField($key)->getType()->getNullableType(), $request);
220 5
                        } else {
221
                            $result[$key] = $item;
222
                        }
223 5
                    }
224 5
                } else {
225 2
                    if ($argumentValue instanceof VariableReference) {
226
                        return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
227
                    } else {
228 2
                        if (is_array($argumentValue)) {
229 1
                            return $argumentValue;
230
                        }
231
                    }
232
                }
233
234 6
                return $result;
235
236 31
            case TypeMap::KIND_SCALAR:
237 31
            case TypeMap::KIND_ENUM:
238
                /** @var $argumentValue AstLiteral|VariableReference */
239 31
                if ($argumentValue instanceof VariableReference) {
240 6
                    return $this->getVariableReferenceArgumentValue($argumentValue, $argumentType, $request);
241
                } else {
242 27
                    if ($argumentValue instanceof AstLiteral) {
243 19
                        return $argumentValue->getValue();
244
                    } else {
245 9
                        return $argumentValue;
246
                    }
247
                }
248
        }
249
250
        throw new ResolveException('Argument type not supported');
251
    }
252
253 8
    private function getVariableReferenceArgumentValue(VariableReference $variableReference, AbstractType $argumentType, Request $request)
254
    {
255 8
        $variable = $variableReference->getVariable();
256 8
        if ($argumentType->getKind() === TypeMap::KIND_LIST) {
257
            if (
258 2
                (!$variable->isArray() && !is_array($variable->getValue())) ||
259 2
                ($variable->getTypeName() !== $argumentType->getNamedType()->getNullableType()->getName()) ||
260 2
                ($argumentType->getNamedType()->getKind() === TypeMap::KIND_NON_NULL && $variable->isArrayElementNullable())
261 2
            ) {
262 1
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getNamedType()->getNullableType()->getName()), $variable->getLocation());
263
            }
264 2
        } else {
265 6
            if ($variable->getTypeName() !== $argumentType->getName()) {
266 1
                throw new ResolveException(sprintf('Invalid variable "%s" type, allowed type is "%s"', $variable->getName(), $argumentType->getName()), $variable->getLocation());
267
            }
268
        }
269
270 7
        $requestValue = $request->getVariable($variable->getName());
271 7
        if ((null === $requestValue && $variable->isNullable()) && !$request->hasVariable($variable->getName())) {
272
            throw new ResolveException(sprintf('Variable "%s" does not exist in request', $variable->getName()), $variable->getLocation());
273
        }
274
275 7
        return $requestValue;
276
    }
277
278 32
    protected function resolveObject(FieldInterface $field, AstFieldInterface $ast, $parentValue, $fromUnion = false)
279
    {
280 32
        $resolvedValue = $parentValue;
281 32
        if (!$fromUnion) {
282 27
            $resolvedValue = $this->doResolve($field, $ast, $parentValue);
283 27
        }
284
285 32
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
286
287 32
        if (null === $resolvedValue) {
288 5
            return null;
289
        }
290
        /** @var AbstractObjectType $type */
291 31
        $type = $field->getType()->getNullableType();
292
293
        try {
294 31
            return $this->collectResult($field, $type, $ast, $resolvedValue);
295 4
        } catch (\Exception $e) {
296 4
            return null;
297
        }
298
    }
299
300
    /**
301
     * @param FieldInterface     $field
302
     * @param AbstractObjectType $type
303
     * @param AstFieldInterface  $ast
304
     * @param                    $resolvedValue
305
     * @return array
306
     */
307 31
    private function collectResult(FieldInterface $field, AbstractObjectType $type, $ast, $resolvedValue)
308
    {
309 31
        $result = [];
310
311 31
        foreach ($ast->getFields() as $astField) {
312 31
            switch (true) {
313 31
                case $astField instanceof TypedFragmentReference:
314 2
                    $astName  = $astField->getTypeName();
315 2
                    $typeName = $type->getName();
316
317 2 View Code Duplication
                    if ($typeName !== $astName) {
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...
318 2
                        foreach ($type->getInterfaces() as $interface) {
319 1
                            if ($interface->getName() === $astName) {
320
                                $result = array_merge($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...
321
322
                                break;
323
                            }
324 2
                        }
325
326 2
                        continue;
327
                    }
328
329 2
                    $result = array_merge($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...
330
331 2
                    break;
332
333 31
                case $astField instanceof FragmentReference:
334 4
                    $astFragment      = $this->executionContext->getRequest()->getFragment($astField->getName());
335 4
                    $astFragmentModel = $astFragment->getModel();
336 4
                    $typeName         = $type->getName();
337
338 4 View Code Duplication
                    if ($typeName !== $astFragmentModel) {
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...
339 1
                        foreach ($type->getInterfaces() as $interface) {
340 1
                            if ($interface->getName() === $astFragmentModel) {
341 1
                                $result = array_merge($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...
342
343 1
                                break;
344
                            }
345
346 1
                        }
347
348 1
                        continue;
349
                    }
350
351 4
                    $result = array_merge($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...
352
353 4
                    break;
354
355 31
                default:
356 31
                    $result[$this->getAlias($astField)] = $this->resolveField($field, $astField, $resolvedValue, true);
357 31
            }
358 31
        }
359
360 31
        return $result;
361
    }
362
363 46
    protected function resolveScalar(FieldInterface $field, AstFieldInterface $ast, $parentValue)
364
    {
365 46
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
366
367 46
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
368
369
        /** @var AbstractScalarType $type */
370 45
        $type = $field->getType()->getNullableType();
371
372 45
        return $type->serialize($resolvedValue);
373
    }
374
375 19
    protected function resolveList(FieldInterface $field, AstFieldInterface $ast, $parentValue)
376
    {
377
        /** @var AstQuery $ast */
378 19
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
379
380 19
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
381
382 17
        if (null === $resolvedValue) {
383 5
            return null;
384
        }
385
386
        /** @var AbstractListType $type */
387 16
        $type     = $field->getType()->getNullableType();
388 16
        $itemType = $type->getNamedType();
389
390 16
        $fakeAst = clone $ast;
391 16
        if ($fakeAst instanceof AstQuery) {
392 15
            $fakeAst->setArguments([]);
393 15
        }
394
395 16
        $fakeField = new Field([
396 16
            'name' => $field->getName(),
397 16
            'type' => $itemType,
398 16
            'args' => $field->getArguments(),
399 16
        ]);
400
401 16
        $result = [];
402 16
        foreach ($resolvedValue as $resolvedValueItem) {
403
            try {
404 15
                $fakeField->getConfig()->set('resolve', function () use ($resolvedValueItem) {
405 15
                    return $resolvedValueItem;
406 15
                });
407
408 15
                switch ($itemType->getNullableType()->getKind()) {
409 15
                    case TypeMap::KIND_ENUM:
410 15
                    case TypeMap::KIND_SCALAR:
411 5
                        $value = $this->resolveScalar($fakeField, $fakeAst, $resolvedValueItem);
412
413 4
                        break;
414
415
416 11
                    case TypeMap::KIND_OBJECT:
417 9
                        $value = $this->resolveObject($fakeField, $fakeAst, $resolvedValueItem);
418
419 9
                        break;
420
421 3
                    case TypeMap::KIND_UNION:
422 3
                    case TypeMap::KIND_INTERFACE:
423 3
                        $value = $this->resolveComposite($fakeField, $fakeAst, $resolvedValueItem);
424
425 3
                        break;
426
427
                    default:
428
                        $value = null;
429 14
                }
430 15
            } catch (\Exception $e) {
431 1
                $this->executionContext->addError($e);
432
433 1
                $value = null;
434
            }
435
436 15
            $result[] = $value;
437 16
        }
438
439 16
        return $result;
440
    }
441
442 7
    protected function resolveComposite(FieldInterface $field, AstFieldInterface $ast, $parentValue)
443
    {
444
        /** @var AstQuery $ast */
445 7
        $resolvedValue = $this->doResolve($field, $ast, $parentValue);
446
447 7
        $this->resolveValidator->assertValidResolvedValueForField($field, $resolvedValue);
448
449 7
        if (null === $resolvedValue) {
450
            return null;
451
        }
452
453
        /** @var AbstractUnionType $type */
454 7
        $type         = $field->getType()->getNullableType();
455 7
        $resolvedType = $type->resolveType($resolvedValue);
456
457 7
        if (!$resolvedType) {
458
            throw new ResolveException('Resolving function must return type');
459
        }
460
461 7
        if ($type instanceof AbstractInterfaceType) {
462 6
            $this->resolveValidator->assertTypeImplementsInterface($resolvedType, $type);
463 6
        } else {
464 1
            $this->resolveValidator->assertTypeInUnionTypes($resolvedType, $type);
465
        }
466
467 7
        $fakeField = new Field([
468 7
            'name' => $field->getName(),
469 7
            'type' => $resolvedType,
470 7
            'args' => $field->getArguments(),
471 7
        ]);
472
473 7
        return $this->resolveObject($fakeField, $ast, $resolvedValue, true);
474
    }
475
476 59
    protected function parseAndCreateRequest($payload, $variables = [])
477
    {
478 59
        if (empty($payload)) {
479 1
            throw new \InvalidArgumentException('Must provide an operation.');
480
        }
481
482 59
        $parser  = new Parser();
483 59
        $request = new Request($parser->parse($payload), $variables);
484
485 59
        (new RequestValidator())->validate($request);
486
487 58
        $this->executionContext->setRequest($request);
488 58
    }
489
490 52
    protected function doResolve(FieldInterface $field, AstFieldInterface $ast, $parentValue = null)
491
    {
492
        /** @var AstQuery|AstField $ast */
493 52
        $arguments = $this->parseArgumentsValues($field, $ast);
494 52
        $astFields = $ast instanceof AstQuery ? $ast->getFields() : [];
495
496 52
        return $field->resolve($parentValue, $arguments, $this->createResolveInfo($field, $astFields));
497
    }
498
499 52
    protected function parseArgumentsValues(FieldInterface $field, AstFieldInterface $ast)
500
    {
501 52
        $values   = [];
502 52
        $defaults = [];
503
504 52
        foreach ($field->getArguments() as $argument) {
505
            /** @var $argument InputField */
506 37
            if ($argument->getConfig()->has('defaultValue')) {
507 6
                $defaults[$argument->getName()] = $argument->getConfig()->getDefaultValue();
508 6
            }
509 52
        }
510
511 52
        foreach ($ast->getArguments() as $astArgument) {
512 29
            $argument     = $field->getArgument($astArgument->getName());
513 29
            $argumentType = $argument->getType()->getNullableType();
514
515 29
            $values[$argument->getName()] = $argumentType->parseValue($astArgument->getValue());
516
517 29
            if (isset($defaults[$argument->getName()])) {
518 3
                unset($defaults[$argument->getName()]);
519 3
            }
520 52
        }
521
522 52
        return array_merge($values, $defaults);
523
    }
524
525 58
    private function getAlias(AstFieldInterface $ast)
526
    {
527 58
        return $ast->getAlias() ?: $ast->getName();
528
    }
529
530 52
    protected function createResolveInfo(FieldInterface $field, array $astFields)
531
    {
532 52
        return new ResolveInfo($field, $astFields, $this->executionContext);
533
    }
534
535
536
    /**
537
     * You can access ExecutionContext to check errors and inject dependencies
538
     *
539
     * @return ExecutionContext
540
     */
541 11
    public function getExecutionContext()
542
    {
543 11
        return $this->executionContext;
544
    }
545
546 59
    public function getResponseData()
547
    {
548 59
        $result = [];
549
550 59
        if (!empty($this->data)) {
551 58
            $result['data'] = $this->data;
552 58
        }
553
554 59
        if ($this->executionContext->hasErrors()) {
555 19
            $result['errors'] = $this->executionContext->getErrorsArray();
556 19
        }
557
558 59
        return $result;
559
    }
560
561
    /**
562
     * @return int
563
     */
564
    public function getMaxComplexity()
565
    {
566
        return $this->maxComplexity;
567
    }
568
569
    /**
570
     * @param int $maxComplexity
571
     */
572 1
    public function setMaxComplexity($maxComplexity)
573
    {
574 1
        $this->maxComplexity = $maxComplexity;
575 1
    }
576
577
}
578