Failed Conditions
Push — master ( 48b44f...392b56 )
by Vladimir
04:42
created

SyncPromise::enqueueWaitingPromises()   B

Complexity

Conditions 10
Paths 2

Size

Total Lines 36
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 10.2537

Importance

Changes 0
Metric Value
eloc 24
dl 0
loc 36
ccs 19
cts 22
cp 0.8636
rs 7.6666
c 0
b 0
f 0
cc 10
nc 2
nop 0
crap 10.2537

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Executor\Promise\Adapter;
6
7
use GraphQL\Executor\ExecutionResult;
8
use GraphQL\Utils\Utils;
9
use Throwable;
10
use function is_object;
11
use function method_exists;
12
13
/**
14
 * Class SyncPromise
15
 *
16
 * Simplistic (yet full-featured) implementation of Promises A+ spec for regular PHP `sync` mode
17
 * (using queue to defer promises execution)
18
 */
19
class SyncPromise
20
{
21
    const PENDING   = 'pending';
22
    const FULFILLED = 'fulfilled';
23
    const REJECTED  = 'rejected';
24
25
    /** @var \SplQueue */
26
    public static $queue;
27
28
    /** @var string */
29
    public $state = self::PENDING;
30
31
    /** @var ExecutionResult|Throwable */
32
    public $result;
33
34
    /**
35
     * Promises created in `then` method of this promise and awaiting for resolution of this promise
36
     * @var mixed[][]
37
     */
38
    private $waiting = [];
39
40 209
    public static function runQueue()
41
    {
42 209
        $q = self::$queue;
43 209
        while ($q && ! $q->isEmpty()) {
44 209
            $task = $q->dequeue();
45 209
            $task();
46
        }
47 209
    }
48
49 246
    public function resolve($value)
50
    {
51 246
        switch ($this->state) {
52 246
            case self::PENDING:
53 241
                if ($value === $this) {
54 1
                    throw new \Exception('Cannot resolve promise with self');
55
                }
56 241
                if (is_object($value) && method_exists($value, 'then')) {
57 43
                    $value->then(
58
                        function ($resolvedValue) {
59 43
                            $this->resolve($resolvedValue);
60 43
                        },
61
                        function ($reason) {
62 26
                            $this->reject($reason);
63 43
                        }
64
                    );
65
66 43
                    return $this;
67
                }
68
69 241
                $this->state  = self::FULFILLED;
70 241
                $this->result = $value;
71 241
                $this->enqueueWaitingPromises();
72 241
                break;
73 15
            case self::FULFILLED:
74 5
                if ($this->result !== $value) {
75 5
                    throw new \Exception('Cannot change value of fulfilled promise');
76
                }
77
                break;
78 10
            case self::REJECTED:
79 10
                throw new \Exception('Cannot resolve rejected promise');
80
        }
81
82 241
        return $this;
83
    }
84
85 49
    public function reject($reason)
86
    {
87 49
        if (! $reason instanceof \Exception && ! $reason instanceof \Throwable) {
88 1
            throw new \Exception('SyncPromise::reject() has to be called with an instance of \Throwable');
89
        }
90
91 49
        switch ($this->state) {
92 49
            case self::PENDING:
93 44
                $this->state  = self::REJECTED;
94 44
                $this->result = $reason;
95 44
                $this->enqueueWaitingPromises();
96 44
                break;
97 15
            case self::REJECTED:
98 10
                if ($reason !== $this->result) {
99 10
                    throw new \Exception('Cannot change rejection reason');
100
                }
101
                break;
102 5
            case self::FULFILLED:
103 5
                throw new \Exception('Cannot reject fulfilled promise');
104
        }
105
106 44
        return $this;
107
    }
108
109 251
    private function enqueueWaitingPromises()
110
    {
111 251
        Utils::invariant(
112 251
            $this->state !== self::PENDING,
113 251
            'Cannot enqueue derived promises when parent is still pending'
114
        );
115
116 251
        foreach ($this->waiting as $descriptor) {
117
            self::getQueue()->enqueue(function () use ($descriptor) {
118
                /** @var $promise self */
119 209
                list($promise, $onFulfilled, $onRejected) = $descriptor;
120
121 209
                if ($this->state === self::FULFILLED) {
122
                    try {
123 205
                        $promise->resolve($onFulfilled ? $onFulfilled($this->result) : $this->result);
124 14
                    } catch (\Exception $e) {
125 14
                        $promise->reject($e);
126
                    } catch (\Throwable $e) {
127 205
                        $promise->reject($e);
128
                    }
129 34
                } elseif ($this->state === self::REJECTED) {
130
                    try {
131 34
                        if ($onRejected) {
132 34
                            $promise->resolve($onRejected($this->result));
133
                        } else {
134 34
                            $promise->reject($this->result);
135
                        }
136 2
                    } catch (\Exception $e) {
137 2
                        $promise->reject($e);
138
                    } catch (\Throwable $e) {
139
                        $promise->reject($e);
140
                    }
141
                }
142 209
            });
143
        }
144 251
        $this->waiting = [];
145 251
    }
146
147 231
    public static function getQueue()
148
    {
149 231
        return self::$queue ?: self::$queue = new \SplQueue();
150
    }
151
152 210
    public function then(?callable $onFulfilled = null, ?callable $onRejected = null)
153
    {
154 210
        if ($this->state === self::REJECTED && ! $onRejected) {
155 5
            return $this;
156
        }
157 210
        if ($this->state === self::FULFILLED && ! $onFulfilled) {
158 152
            return $this;
159
        }
160 210
        $tmp             = new self();
161 210
        $this->waiting[] = [$tmp, $onFulfilled, $onRejected];
162
163 210
        if ($this->state !== self::PENDING) {
164 198
            $this->enqueueWaitingPromises();
165
        }
166
167 210
        return $tmp;
168
    }
169
}
170