Issues (167)

src/Execution/Execution.php (1 issue)

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
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
    ): PromiseInterface {
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 resolve(new ExecutionResult(null, $context->getErrors()));
46
            }
47
        } catch (ExecutionException $error) {
48
            return resolve(new ExecutionResult(null, [$error]));
49
        }
50
51
        $fieldCollector = new FieldCollector($context);
52
53
        $data = $this->executeOperation($operationName, $context, $fieldCollector);
54
55
        if ($data instanceof PromiseInterface) {
56
            return $data->then(function ($resolvedData) use ($context) {
57
                return new ExecutionResult($resolvedData, $context->getErrors());
58
            });
59
        }
60
61
        if (null !== $errorHandler) {
62
            foreach ($context->getErrors() as $error) {
63
                $errorHandler->handleExecutionError($error, $context);
64
            }
65
        }
66
67
        return resolve(new ExecutionResult($data, $context->getErrors()));
68
    }
69
70
    /**
71
     * @param null|string      $operationName
72
     * @param ExecutionContext $context
73
     * @param FieldCollector   $fieldCollector
74
     * @return array|mixed|null|PromiseInterface
75
     */
76
    protected function executeOperation(
77
        ?string $operationName,
78
        ExecutionContext $context,
79
        FieldCollector $fieldCollector
80
    ) {
81
        $strategy = $operationName === 'mutation'
82
            ? new SerialExecutionStrategy($context, $fieldCollector)
83
            : new ParallelExecutionStrategy($context, $fieldCollector);
84
85
        $result = null;
0 ignored issues
show
The assignment to $result is dead and can be removed.
Loading history...
86
87
        try {
88
            $result = $strategy->execute();
89
        } catch (ExecutionException $exception) {
90
            $context->addError($exception);
91
        } catch (\Throwable $exception) {
92
            $context->addError(
93
                new ExecutionException($exception->getMessage(), null, null, null, null, null, $exception)
94
            );
95
        }
96
97
        if ($result instanceof PromiseInterface) {
98
            return $result->then(null, function (ExecutionException $exception) use ($context) {
99
                $context->addError($exception);
100
                return resolve(null);
101
            });
102
        }
103
104
        return $result;
105
    }
106
107
    /**
108
     * @param Schema        $schema
109
     * @param DocumentNode  $documentNode
110
     * @param mixed         $rootValue
111
     * @param mixed         $contextValue
112
     * @param mixed         $rawVariableValues
113
     * @param null|string   $operationName
114
     * @param callable|null $fieldResolver
115
     * @return ExecutionContext
116
     * @throws ExecutionException
117
     */
118
    protected function createContext(
119
        Schema $schema,
120
        DocumentNode $documentNode,
121
        $rootValue,
122
        $contextValue,
123
        $rawVariableValues,
124
        ?string $operationName = null,
125
        ?callable $fieldResolver = null
126
    ): ExecutionContext {
127
        $errors    = [];
128
        $fragments = [];
129
        $operation = null;
130
131
        foreach ($documentNode->getDefinitions() as $definition) {
132
            if ($definition instanceof OperationDefinitionNode) {
133
                if (null === $operationName && null !== $operation) {
134
                    throw new ExecutionException(
135
                        'Must provide operation name if query contains multiple operations.'
136
                    );
137
                }
138
139
                if (null === $operationName || $definition->getNameValue() === $operationName) {
140
                    $operation = $definition;
141
                }
142
143
                continue;
144
            }
145
146
            if ($definition instanceof FragmentDefinitionNode || $definition instanceof FragmentSpreadNode) {
147
                $fragments[$definition->getNameValue()] = $definition;
148
149
                continue;
150
            }
151
        }
152
153
        if (null === $operation) {
154
            if (null !== $operationName) {
155
                throw new ExecutionException(sprintf('Unknown operation named "%s".', $operationName));
156
            }
157
158
            throw new ExecutionException('Must provide an operation.');
159
        }
160
161
        $coercedVariableValues = ValuesResolver::coerceVariableValues(
162
            $schema,
163
            $operation->getVariableDefinitions(),
164
            $rawVariableValues
165
        );
166
167
        $variableValues = $coercedVariableValues->getValue();
168
169
        if ($coercedVariableValues->hasErrors()) {
170
            $errors = $coercedVariableValues->getErrors();
171
        }
172
173
        return new ExecutionContext(
174
            $schema,
175
            $fragments,
176
            $rootValue,
177
            $contextValue,
178
            $variableValues,
179
            $fieldResolver,
180
            $operation,
181
            $errors
182
        );
183
    }
184
}
185