Completed
Pull Request — master (#550)
by Jáchym
26:52 queued 22:59
created

SyncPromiseAdapter::create()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2.032

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 20
ccs 8
cts 10
cp 0.8
rs 9.9332
cc 2
nc 2
nop 1
crap 2.032
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Executor\Promise\Adapter;
6
7
use Exception;
8
use GraphQL\Deferred;
9
use GraphQL\Error\InvariantViolation;
10
use GraphQL\Executor\ExecutionResult;
11
use GraphQL\Executor\Promise\Promise;
12
use GraphQL\Executor\Promise\PromiseAdapter;
13
use GraphQL\Utils\Utils;
14
use Throwable;
15
use function count;
16
17
/**
18
 * Allows changing order of field resolution even in sync environments
19
 * (by leveraging queue of deferreds and promises)
20
 */
21
class SyncPromiseAdapter implements PromiseAdapter
22
{
23
    /**
24
     * @inheritdoc
25
     */
26 213
    public function isThenable($value)
27
    {
28 213
        return $value instanceof Deferred;
29
    }
30
31
    /**
32
     * @inheritdoc
33
     */
34 42
    public function convertThenable($thenable)
35
    {
36 42
        if (! $thenable instanceof Deferred) {
37 1
            throw new InvariantViolation('Expected instance of GraphQL\Deferred, got ' . Utils::printSafe($thenable));
38
        }
39
40 42
        return new Promise($thenable->promise, $this);
41
    }
42
43
    /**
44
     * @inheritdoc
45
     */
46 70
    public function then(Promise $promise, ?callable $onFulfilled = null, ?callable $onRejected = null)
47
    {
48
        /** @var SyncPromise $adoptedPromise */
49 70
        $adoptedPromise = $promise->adoptedPromise;
50
51 70
        return new Promise($adoptedPromise->then($onFulfilled, $onRejected), $this);
52
    }
53
54
    /**
55
     * @inheritdoc
56
     */
57 41
    public function create(callable $resolver)
58
    {
59 41
        $promise = new SyncPromise();
60
61
        try {
62 41
            $resolver(
63
                [
64 41
                    $promise,
65 41
                    'resolve',
66
                ],
67
                [
68 41
                    $promise,
69 41
                    'reject',
70
                ]
71
            );
72
        } catch (Throwable $e) {
73
            $promise->reject($e);
74
        }
75
76 41
        return new Promise($promise, $this);
77
    }
78
79
    /**
80
     * @inheritdoc
81
     */
82 208
    public function createFulfilled($value = null)
83
    {
84 208
        $promise = new SyncPromise();
85
86 208
        return new Promise($promise->resolve($value), $this);
87
    }
88
89
    /**
90
     * @inheritdoc
91
     */
92 1
    public function createRejected($reason)
93
    {
94 1
        $promise = new SyncPromise();
95
96 1
        return new Promise($promise->reject($reason), $this);
97
    }
98
99
    /**
100
     * @inheritdoc
101
     */
102 42
    public function all(array $promisesOrValues)
103
    {
104 42
        $all = new SyncPromise();
105
106 42
        $total  = count($promisesOrValues);
107 42
        $count  = 0;
108 42
        $result = [];
109
110 42
        foreach ($promisesOrValues as $index => $promiseOrValue) {
111 42
            if ($promiseOrValue instanceof Promise) {
112 42
                $result[$index] = null;
113 42
                $promiseOrValue->then(
114
                    static function ($value) use ($index, &$count, $total, &$result, $all) {
115 40
                        $result[$index] = $value;
116 40
                        $count++;
117 40
                        if ($count < $total) {
118 25
                            return;
119
                        }
120
121 40
                        $all->resolve($result);
122 42
                    },
123 42
                    [$all, 'reject']
124
                );
125
            } else {
126 11
                $result[$index] = $promiseOrValue;
127 42
                $count++;
128
            }
129
        }
130 42
        if ($count === $total) {
131 1
            $all->resolve($result);
132
        }
133
134 42
        return new Promise($all, $this);
135
    }
136
137
    /**
138
     * Synchronously wait when promise completes
139
     *
140
     * @return ExecutionResult
141
     */
142 238
    public function wait(Promise $promise)
143
    {
144 238
        $this->beforeWait($promise);
145 238
        $dfdQueue     = Deferred::getQueue();
146 238
        $promiseQueue = SyncPromise::getQueue();
147
148 238
        while ($promise->adoptedPromise->state === SyncPromise::PENDING &&
0 ignored issues
show
Bug introduced by
The property state does not seem to exist on React\Promise\Promise.
Loading history...
149 238
            ! ($dfdQueue->isEmpty() && $promiseQueue->isEmpty())
150
        ) {
151 65
            Deferred::runQueue();
152 65
            SyncPromise::runQueue();
153 65
            $this->onWait($promise);
154
        }
155
156
        /** @var SyncPromise $syncPromise */
157 238
        $syncPromise = $promise->adoptedPromise;
158
159 238
        if ($syncPromise->state === SyncPromise::FULFILLED) {
160 238
            return $syncPromise->result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $syncPromise->result also could return the type Throwable which is incompatible with the documented return type GraphQL\Executor\ExecutionResult.
Loading history...
161
        }
162
163
        if ($syncPromise->state === SyncPromise::REJECTED) {
164
            throw $syncPromise->result;
165
        }
166
167
        throw new InvariantViolation('Could not resolve promise');
168
    }
169
170
    /**
171
     * Execute just before starting to run promise completion
172
     */
173 238
    protected function beforeWait(Promise $promise)
0 ignored issues
show
Unused Code introduced by
The parameter $promise 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

173
    protected function beforeWait(/** @scrutinizer ignore-unused */ Promise $promise)

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...
174
    {
175 238
    }
176
177
    /**
178
     * Execute while running promise completion
179
     */
180 65
    protected function onWait(Promise $promise)
0 ignored issues
show
Unused Code introduced by
The parameter $promise 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

180
    protected function onWait(/** @scrutinizer ignore-unused */ Promise $promise)

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...
181
    {
182 65
    }
183
}
184