Completed
Pull Request — master (#335)
by Christoffer
10:21 queued 05:59
created

Execution::execute()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 47
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 23
nc 8
nop 8
dl 0
loc 47
rs 8.9297
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Digia\GraphQL\Execution;
4
5
use Digia\GraphQL\Error\ErrorHandlerInterface;
6
use Digia\GraphQL\Execution\Strategy\FieldCollector;
7
use Digia\GraphQL\Execution\Strategy\ParallelExecutionStrategy;
8
use Digia\GraphQL\Execution\Strategy\SerialExecutionStrategy;
9
use Digia\GraphQL\Language\Node\DocumentNode;
10
use Digia\GraphQL\Language\Node\FragmentDefinitionNode;
11
use Digia\GraphQL\Language\Node\FragmentSpreadNode;
12
use Digia\GraphQL\Language\Node\OperationDefinitionNode;
13
use Digia\GraphQL\Schema\Schema;
14
use React\Promise\PromiseInterface;
15
16
class Execution implements ExecutionInterface
17
{
18
    /**
19
     * @inheritdoc
20
     */
21
    public function execute(
22
        Schema $schema,
23
        DocumentNode $documentNode,
24
        $rootValue = null,
25
        $contextValue = null,
26
        array $variableValues = [],
27
        ?string $operationName = null,
28
        ?callable $fieldResolver = null,
29
        ?ErrorHandlerInterface $errorHandler = null
30
    ): ExecutionResult {
31
        try {
32
            $context = $this->createContext(
33
                $schema,
34
                $documentNode,
35
                $rootValue,
36
                $contextValue,
37
                $variableValues,
38
                $operationName,
39
                $fieldResolver
40
            );
41
42
            // Return early errors if execution context failed.
43
            if (!empty($context->getErrors())) {
44
                return new ExecutionResult(null, $context->getErrors());
45
            }
46
        } catch (ExecutionException $error) {
47
            return new ExecutionResult(null, [$error]);
48
        }
49
50
        $valuesResolver = new ValuesResolver();
51
        $fieldCollector = new FieldCollector($context, $valuesResolver);
52
53
        $data = $this->executeOperation($operationName, $context, $fieldCollector, $valuesResolver);
54
55
        if ($data instanceof PromiseInterface) {
56
            $data->then(function ($resolvedData) use (&$data) {
57
                $data = $resolvedData;
58
            });
59
        }
60
61
        if (null !== $errorHandler) {
62
            foreach ($context->getErrors() as $error) {
63
                $errorHandler->handleError($error);
64
            }
65
        }
66
67
        return new ExecutionResult($data, $context->getErrors());
68
    }
69
70
    /**
71
     * @param null|string                $operationName
72
     * @param ExecutionContext           $context
73
     * @param FieldCollector             $fieldCollector
74
     * @param ValuesResolver             $valuesResolver
75
     * @param ErrorHandlerInterface|null $errorHandler
76
     * @return array|mixed|null|PromiseInterface
77
     */
78
    protected function executeOperation(
79
        ?string $operationName,
80
        ExecutionContext $context,
81
        FieldCollector $fieldCollector,
82
        ValuesResolver $valuesResolver,
83
        ?ErrorHandlerInterface $errorHandler = null
0 ignored issues
show
Unused Code introduced by
The parameter $errorHandler is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

83
        /** @scrutinizer ignore-unused */ ?ErrorHandlerInterface $errorHandler = null

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
84
    ) {
85
        $strategy = $operationName === 'mutation'
86
            ? new SerialExecutionStrategy($context, $fieldCollector, $valuesResolver)
87
            : new ParallelExecutionStrategy($context, $fieldCollector, $valuesResolver);
88
89
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
90
91
        try {
92
            $result = $strategy->execute();
93
        } catch (ExecutionException $exception) {
94
            $context->addError($exception);
95
        } catch (\Throwable $exception) {
96
            $context->addError(
97
                new ExecutionException($exception->getMessage(), null, null, null, null, null, $exception)
98
            );
99
        }
100
101
        if ($result instanceof PromiseInterface) {
102
            return $result->then(null, function (ExecutionException $exception) use ($context) {
103
                $context->addError($exception);
104
                return \React\Promise\resolve(null);
105
            });
106
        }
107
108
        return $result;
109
    }
110
111
    /**
112
     * @param Schema        $schema
113
     * @param DocumentNode  $documentNode
114
     * @param mixed         $rootValue
115
     * @param mixed         $contextValue
116
     * @param mixed         $rawVariableValues
117
     * @param null|string   $operationName
118
     * @param callable|null $fieldResolver
119
     * @return ExecutionContext
120
     * @throws ExecutionException
121
     */
122
    protected function createContext(
123
        Schema $schema,
124
        DocumentNode $documentNode,
125
        $rootValue,
126
        $contextValue,
127
        $rawVariableValues,
128
        ?string $operationName = null,
129
        ?callable $fieldResolver = null
130
    ): ExecutionContext {
131
        $errors    = [];
132
        $fragments = [];
133
        $operation = null;
134
135
        foreach ($documentNode->getDefinitions() as $definition) {
136
            if ($definition instanceof OperationDefinitionNode) {
137
                if (null === $operationName && null !== $operation) {
138
                    throw new ExecutionException(
139
                        'Must provide operation name if query contains multiple operations.'
140
                    );
141
                }
142
143
                if (null === $operationName || $definition->getNameValue() === $operationName) {
144
                    $operation = $definition;
145
                }
146
147
                continue;
148
            }
149
150
            if ($definition instanceof FragmentDefinitionNode || $definition instanceof FragmentSpreadNode) {
151
                $fragments[$definition->getNameValue()] = $definition;
152
153
                continue;
154
            }
155
        }
156
157
        if (null === $operation) {
158
            if (null !== $operationName) {
159
                throw new ExecutionException(sprintf('Unknown operation named "%s".', $operationName));
160
            }
161
162
            throw new ExecutionException('Must provide an operation.');
163
        }
164
165
        $coercedVariableValues = (new ValuesResolver())->coerceVariableValues(
166
            $schema,
167
            $operation->getVariableDefinitions(),
168
            $rawVariableValues
169
        );
170
171
        $variableValues = $coercedVariableValues->getValue();
172
173
        if ($coercedVariableValues->hasErrors()) {
174
            $errors = $coercedVariableValues->getErrors();
175
        }
176
177
        return new ExecutionContext(
178
            $schema,
179
            $fragments,
180
            $rootValue,
181
            $contextValue,
182
            $variableValues,
183
            $fieldResolver,
184
            $operation,
185
            $errors
186
        );
187
    }
188
}
189