GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 21c599...486f90 )
by Šimon
03:24
created

SyncPromise::__construct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 1
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Executor\Promise\Adapter;
6
7
use Exception;
8
use GraphQL\Utils\Utils;
9
use SplQueue;
10
use Throwable;
11
use function is_object;
12
use function method_exists;
13
14
/**
15
 * Simplistic (yet full-featured) implementation of Promises A+ spec for regular PHP `sync` mode
16
 * (using queue to defer promises execution)
17
 *
18
 * Note:
19
 * Library users are not supposed to use SyncPromise class in their resolvers.
20
 * Instead they should use GraphQL\Deferred which enforces $executor callback in the constructor.
21
 *
22
 * Root SyncPromise without explicit $executor will never resolve (actually throw while trying).
23
 * The whole point of Deferred is to ensure it never happens and that any resolver creates
24
 * at least one $executor to start the promise chain.
25
 */
26
class SyncPromise
27
{
28
    const PENDING   = 'pending';
29
    const FULFILLED = 'fulfilled';
30
    const REJECTED  = 'rejected';
31
32
    /** @var SplQueue */
33
    public static $queue;
34
35
    /** @var string */
36
    public $state = self::PENDING;
37
38
    /** @var mixed */
39
    public $result;
40
41 82
    /**
42
     * Promises created in `then` method of this promise and awaiting for resolution of this promise
43 82
     *
44 82
     * @var mixed[][]
45 82
     */
46 82
    private $waiting = [];
47
48 82
    public static function runQueue() : void
49
    {
50 279
        $q = self::$queue;
51
        while ($q !== null && ! $q->isEmpty()) {
52 279
            $task = $q->dequeue();
53 279
            $task();
54 274
        }
55 1
    }
56
57 274
    /**
58 25
     * @param callable() : mixed $executor
0 ignored issues
show
Documentation Bug introduced by
The doc comment : mixed at position 0 could not be parsed: Unknown type name ':' at position 0 in : mixed.
Loading history...
59
     */
60 14
    public function __construct(?callable $executor = null)
61 25
    {
62
        if ($executor === null) {
63 13
            return;
64 25
        }
65
        self::getQueue()->enqueue(function () use ($executor) : void {
66
            try {
67 25
                $this->resolve($executor());
68
            } catch (Throwable $e) {
69
                $this->reject($e);
70 274
            }
71 274
        });
72 274
    }
73 274
74 15
    public function resolve($value) : self
75 5
    {
76 5
        switch ($this->state) {
77
            case self::PENDING:
78
                if ($value === $this) {
79 10
                    throw new Exception('Cannot resolve promise with self');
80 10
                }
81
                if (is_object($value) && method_exists($value, 'then')) {
82
                    $value->then(
83 274
                        function ($resolvedValue) : void {
84
                            $this->resolve($resolvedValue);
85
                        },
86 49
                        function ($reason) : void {
87
                            $this->reject($reason);
88 49
                        }
89 1
                    );
90
91
                    return $this;
92 49
                }
93 49
94 44
                $this->state  = self::FULFILLED;
95 44
                $this->result = $value;
96 44
                $this->enqueueWaitingPromises();
97 44
                break;
98 15
            case self::FULFILLED:
99 10
                if ($this->result !== $value) {
100 10
                    throw new Exception('Cannot change value of fulfilled promise');
101
                }
102
                break;
103 5
            case self::REJECTED:
104 5
                throw new Exception('Cannot resolve rejected promise');
105
        }
106
107 44
        return $this;
108
    }
109
110 284
    public function reject($reason) : self
111
    {
112 284
        if (! $reason instanceof Throwable) {
113 284
            throw new Exception('SyncPromise::reject() has to be called with an instance of \Throwable');
114 284
        }
115
116
        switch ($this->state) {
117 284
            case self::PENDING:
118
                $this->state  = self::REJECTED;
119
                $this->result = $reason;
120 82
                $this->enqueueWaitingPromises();
121
                break;
122 82
            case self::REJECTED:
123
                if ($reason !== $this->result) {
124 78
                    throw new Exception('Cannot change rejection reason');
125 14
                }
126 78
                break;
127
            case self::FULFILLED:
128 34
                throw new Exception('Cannot reject fulfilled promise');
129
        }
130 34
131 23
        return $this;
132
    }
133 34
134
    private function enqueueWaitingPromises() : void
135 14
    {
136 14
        Utils::invariant(
137
            $this->state !== self::PENDING,
138
            'Cannot enqueue derived promises when parent is still pending'
139 82
        );
140
141 284
        foreach ($this->waiting as $descriptor) {
142 284
            self::getQueue()->enqueue(function () use ($descriptor) : void {
143
                /** @var self $promise */
144 258
                [$promise, $onFulfilled, $onRejected] = $descriptor;
145
146 258
                if ($this->state === self::FULFILLED) {
147
                    try {
148
                        $promise->resolve($onFulfilled === null ? $this->result : $onFulfilled($this->result));
149 83
                    } catch (Throwable $e) {
150
                        $promise->reject($e);
151 83
                    }
152 5
                } elseif ($this->state === self::REJECTED) {
153
                    try {
154 83
                        if ($onRejected === null) {
155 5
                            $promise->reject($this->result);
156
                        } else {
157 83
                            $promise->resolve($onRejected($this->result));
158 83
                        }
159
                    } catch (Throwable $e) {
160 83
                        $promise->reject($e);
161 57
                    }
162
                }
163
            });
164 83
        }
165
        $this->waiting = [];
166
    }
167
168
    public static function getQueue() : SplQueue
169
    {
170
        return self::$queue ?: self::$queue = new SplQueue();
171
    }
172
173
    /**
174
     * @param callable(mixed) : mixed     $onFulfilled
0 ignored issues
show
Documentation Bug introduced by
The doc comment : mixed at position 0 could not be parsed: Unknown type name ':' at position 0 in : mixed.
Loading history...
175
     * @param callable(Throwable) : mixed $onRejected
176
     */
177
    public function then(?callable $onFulfilled = null, ?callable $onRejected = null) : self
178
    {
179
        if ($this->state === self::REJECTED && $onRejected === null) {
180
            return $this;
181
        }
182
        if ($this->state === self::FULFILLED && $onFulfilled === null) {
183
            return $this;
184
        }
185
        $tmp             = new self();
186
        $this->waiting[] = [$tmp, $onFulfilled, $onRejected];
187
188
        if ($this->state !== self::PENDING) {
189
            $this->enqueueWaitingPromises();
190
        }
191
192
        return $tmp;
193
    }
194
195
    /**
196
     * @param callable(Throwable) : mixed $onRejected
0 ignored issues
show
Documentation Bug introduced by
The doc comment : mixed at position 0 could not be parsed: Unknown type name ':' at position 0 in : mixed.
Loading history...
197
     */
198
    public function catch(callable $onRejected) : self
199
    {
200
        return $this->then(null, $onRejected);
201
    }
202
}
203