Passed
Pull Request — master (#345)
by
unknown
02:49
created

Execution::promiseExecute()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 47
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 23
nc 7
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\Handler\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
use function React\Promise\resolve;
16
17
class Execution implements ExecutionInterface, PromisedExecutionInterface
0 ignored issues
show
Bug introduced by
The type Digia\GraphQL\Execution\PromisedExecutionInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
{
19
    /**
20
     * @inheritdoc
21
     */
22
    public function execute(
23
        Schema $schema,
24
        DocumentNode $documentNode,
25
        $rootValue = null,
26
        $contextValue = null,
27
        array $variableValues = [],
28
        ?string $operationName = null,
29
        ?callable $fieldResolver = null,
30
        ?ErrorHandlerInterface $errorHandler = null
31
    ): ExecutionResult {
32
        try {
33
            $context = $this->createContext(
34
                $schema,
35
                $documentNode,
36
                $rootValue,
37
                $contextValue,
38
                $variableValues,
39
                $operationName,
40
                $fieldResolver
41
            );
42
43
            // Return early errors if execution context failed.
44
            if (!empty($context->getErrors())) {
45
                return new ExecutionResult(null, $context->getErrors());
46
            }
47
        } catch (ExecutionException $error) {
48
            return new ExecutionResult(null, [$error]);
49
        }
50
51
        $valuesResolver = new ValuesResolver();
52
        $fieldCollector = new FieldCollector($context, $valuesResolver);
53
54
        $data = $this->executeOperation($operationName, $context, $fieldCollector, $valuesResolver);
55
56
        if ($data instanceof PromiseInterface) {
57
            $data->then(function ($resolvedData) use (&$data) {
58
                $data = $resolvedData;
59
            });
60
        }
61
62
        if (null !== $errorHandler) {
63
            foreach ($context->getErrors() as $error) {
64
                $errorHandler->handleExecutionError($error, $context);
65
            }
66
        }
67
68
        return new ExecutionResult($data, $context->getErrors());
69
    }
70
71
    /**
72
     * @inheritdoc
73
     */
74
    public function promiseExecute(
75
        Schema $schema,
76
        DocumentNode $documentNode,
77
        $rootValue = null,
78
        $contextValue = null,
79
        array $variableValues = [],
80
        ?string $operationName = null,
81
        ?callable $fieldResolver = null,
82
        ?ErrorHandlerInterface $errorHandler = null
83
    ): PromiseInterface {
84
        try {
85
            $context = $this->createContext(
86
                $schema,
87
                $documentNode,
88
                $rootValue,
89
                $contextValue,
90
                $variableValues,
91
                $operationName,
92
                $fieldResolver
93
            );
94
95
            // Return early errors if execution context failed.
96
            if (!empty($context->getErrors())) {
97
                return resolve(new ExecutionResult(null, $context->getErrors()));
98
            }
99
        } catch (ExecutionException $error) {
100
            return resolve(new ExecutionResult(null, [$error]));
101
        }
102
103
        $valuesResolver = new ValuesResolver();
104
        $fieldCollector = new FieldCollector($context, $valuesResolver);
105
106
        $data = $this->executeOperation($operationName, $context, $fieldCollector, $valuesResolver);
107
108
        if ($data instanceof PromiseInterface) {
109
            return $data->then(function ($resolvedData) use ($context) {
110
                return new ExecutionResult($resolvedData, $context->getErrors());
111
            });
112
        }
113
114
        if (null !== $errorHandler) {
115
            foreach ($context->getErrors() as $error) {
116
                $errorHandler->handleExecutionError($error, $context);
117
            }
118
        }
119
120
        return resolve(new ExecutionResult($data, $context->getErrors()));
121
    }
122
123
    /**
124
     * @param null|string      $operationName
125
     * @param ExecutionContext $context
126
     * @param FieldCollector   $fieldCollector
127
     * @param ValuesResolver   $valuesResolver
128
     * @return array|mixed|null|PromiseInterface
129
     */
130
    protected function executeOperation(
131
        ?string $operationName,
132
        ExecutionContext $context,
133
        FieldCollector $fieldCollector,
134
        ValuesResolver $valuesResolver
135
    ) {
136
        $strategy = $operationName === 'mutation'
137
            ? new SerialExecutionStrategy($context, $fieldCollector, $valuesResolver)
138
            : new ParallelExecutionStrategy($context, $fieldCollector, $valuesResolver);
139
140
        $result = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
141
142
        try {
143
            $result = $strategy->execute();
144
        } catch (ExecutionException $exception) {
145
            $context->addError($exception);
146
        } catch (\Throwable $exception) {
147
            $context->addError(
148
                new ExecutionException($exception->getMessage(), null, null, null, null, null, $exception)
149
            );
150
        }
151
152
        if ($result instanceof PromiseInterface) {
153
            return $result->then(null, function (ExecutionException $exception) use ($context) {
154
                $context->addError($exception);
155
                return \React\Promise\resolve(null);
156
            });
157
        }
158
159
        return $result;
160
    }
161
162
    /**
163
     * @param Schema        $schema
164
     * @param DocumentNode  $documentNode
165
     * @param mixed         $rootValue
166
     * @param mixed         $contextValue
167
     * @param mixed         $rawVariableValues
168
     * @param null|string   $operationName
169
     * @param callable|null $fieldResolver
170
     * @return ExecutionContext
171
     * @throws ExecutionException
172
     */
173
    protected function createContext(
174
        Schema $schema,
175
        DocumentNode $documentNode,
176
        $rootValue,
177
        $contextValue,
178
        $rawVariableValues,
179
        ?string $operationName = null,
180
        ?callable $fieldResolver = null
181
    ): ExecutionContext {
182
        $errors    = [];
183
        $fragments = [];
184
        $operation = null;
185
186
        foreach ($documentNode->getDefinitions() as $definition) {
187
            if ($definition instanceof OperationDefinitionNode) {
188
                if (null === $operationName && null !== $operation) {
189
                    throw new ExecutionException(
190
                        'Must provide operation name if query contains multiple operations.'
191
                    );
192
                }
193
194
                if (null === $operationName || $definition->getNameValue() === $operationName) {
195
                    $operation = $definition;
196
                }
197
198
                continue;
199
            }
200
201
            if ($definition instanceof FragmentDefinitionNode || $definition instanceof FragmentSpreadNode) {
202
                $fragments[$definition->getNameValue()] = $definition;
203
204
                continue;
205
            }
206
        }
207
208
        if (null === $operation) {
209
            if (null !== $operationName) {
210
                throw new ExecutionException(sprintf('Unknown operation named "%s".', $operationName));
211
            }
212
213
            throw new ExecutionException('Must provide an operation.');
214
        }
215
216
        $coercedVariableValues = (new ValuesResolver())->coerceVariableValues(
217
            $schema,
218
            $operation->getVariableDefinitions(),
219
            $rawVariableValues
220
        );
221
222
        $variableValues = $coercedVariableValues->getValue();
223
224
        if ($coercedVariableValues->hasErrors()) {
225
            $errors = $coercedVariableValues->getErrors();
226
        }
227
228
        return new ExecutionContext(
229
            $schema,
230
            $fragments,
231
            $rootValue,
232
            $contextValue,
233
            $variableValues,
234
            $fieldResolver,
235
            $operation,
236
            $errors
237
        );
238
    }
239
}
240