Promise::progress()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Thruster\Component\Promise;
4
5
/**
6
 * Class Promise
7
 *
8
 * @package Thruster\Component\Promise
9
 * @author  Aurimas Niekis <[email protected]>
10
 */
11
class Promise implements ExtendedPromiseInterface, CancellablePromiseInterface
12
{
13
    /**
14
     * @var callable
15
     */
16
    private $canceller;
17
18
    private $result;
19
20
    /**
21
     * @var array
22
     */
23
    private $handlers;
24
25
    /**
26
     * @var array
27
     */
28
    private $progressHandlers;
29
30
    /**
31
     * @var int
32
     */
33
    private $requiredCancelRequests;
34
35
    /**
36
     * @var int
37
     */
38
    private $cancelRequests;
39
40 383
    public function __construct(callable $resolver, callable $canceller = null)
41
    {
42 383
        $this->handlers = [];
43 383
        $this->progressHandlers = [];
44
45 383
        $this->requiredCancelRequests = 0;
46 383
        $this->cancelRequests         = 0;
47
48 383
        $this->canceller = $canceller;
49 383
        $this->call($resolver);
50 383
    }
51
52 271
    public function then(
53
        callable $onFulfilled = null,
54
        callable $onRejected = null,
55
        callable $onProgress = null
56
    ) : PromiseInterface {
57 271
        if (null !== $this->result) {
58 141
            return $this->result()->then($onFulfilled, $onRejected, $onProgress);
59
        }
60
61 139
        if (null === $this->canceller) {
62 112
            return new static($this->resolver($onFulfilled, $onRejected, $onProgress));
63
        }
64
65 27
        $this->requiredCancelRequests++;
66
67
        return new static($this->resolver($onFulfilled, $onRejected, $onProgress), function () {
68 9
            if (++$this->cancelRequests < $this->requiredCancelRequests) {
69 6
                return;
70
            }
71
72 9
            $this->cancel();
73 27
        });
74
    }
75
76 94
    public function done(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
77
    {
78 94
        if (null !== $this->result) {
79 36
            return $this->result()->done($onFulfilled, $onRejected, $onProgress);
80
        }
81
82
        $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected) {
83
            $promise
84 40
                ->done($onFulfilled, $onRejected);
85 16
        };
86
87 58
        if ($onProgress) {
88 16
            $this->progressHandlers[] = $onProgress;
89
        }
90 58
    }
91
92 21
    public function otherwise(callable $onRejected) : ExtendedPromiseInterface
93
    {
94
        return $this->then(null, function ($reason) use ($onRejected) {
95 15
            if (!_checkTypehint($onRejected, $reason)) {
96 3
                return new RejectedPromise($reason);
97
            }
98
99 12
            return $onRejected($reason);
100 21
        });
101
    }
102
103 66
    public function always(callable $onFulfilledOrRejected) : ExtendedPromiseInterface
104
    {
105
        return $this->then(function ($value) use ($onFulfilledOrRejected) {
106
            return resolve($onFulfilledOrRejected())->then(function () use ($value) {
107 21
                return $value;
108 27
            });
109
        }, function ($reason) use ($onFulfilledOrRejected) {
110
            return resolve($onFulfilledOrRejected())->then(function () use ($reason) {
111 18
                return new RejectedPromise($reason);
112 24
            });
113 66
        });
114
    }
115
116 6
    public function progress(callable $onProgress) : ExtendedPromiseInterface
117
    {
118 6
        return $this->then(null, null, $onProgress);
119
    }
120
121 54
    public function cancel()
122
    {
123 54
        if (null === $this->canceller || null !== $this->result) {
124 24
            return;
125
        }
126
127 33
        $this->call($this->canceller);
128 33
    }
129
130 139
    private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
131
    {
132
        return function ($resolve, $reject, $notify) use ($onFulfilled, $onRejected, $onProgress) {
133 139
            if ($onProgress) {
134
                $progressHandler = function ($update) use ($notify, $onProgress) {
135
                    try {
136 34
                        $notify($onProgress($update));
137 3
                    } catch (\Exception $e) {
138 3
                        $notify($e);
139
                    }
140 34
                };
141
            } else {
142 111
                $progressHandler = $notify;
143
            }
144
145
            $this->handlers[] = function (ExtendedPromiseInterface $promise) use (
146 93
                $onFulfilled,
147 93
                $onRejected,
148 93
                $resolve,
149 93
                $reject,
150 93
                $progressHandler
151
            ) {
152
                $promise
153 93
                    ->then($onFulfilled, $onRejected)
154 93
                    ->done($resolve, $reject, $progressHandler);
155 90
            };
156
157 139
            $this->progressHandlers[] = $progressHandler;
158 139
        };
159
    }
160
161 214
    private function resolve($value = null)
162
    {
163 214
        if (null !== $this->result) {
164 11
            return;
165
        }
166
167 214
        $this->settle(resolve($value));
168 208
    }
169
170 160
    private function reject($reason = null)
171
    {
172 160
        if (null !== $this->result) {
173 6
            return;
174
        }
175
176 160
        $this->settle(reject($reason));
177 142
    }
178
179 46
    private function notify($update = null)
180
    {
181 46
        if (null !== $this->result) {
182 6
            return;
183
        }
184
185 40
        foreach ($this->progressHandlers as $handler) {
186 40
            $handler($update);
187
        }
188 37
    }
189
190 321
    private function settle(ExtendedPromiseInterface $promise)
191
    {
192 321
        $handlers = $this->handlers;
193
194 321
        $this->progressHandlers = $this->handlers = [];
195 321
        $this->result           = $promise;
196
197 321
        foreach ($handlers as $handler) {
198 126
            $handler($promise);
199
        }
200 300
    }
201
202 177
    private function result()
203
    {
204 177
        while ($this->result instanceof self && null !== $this->result->result) {
205
            $this->result = $this->result->result;
206
        }
207
208 177
        return $this->result;
209
    }
210
211 383
    private function call(callable $callback)
212
    {
213
        try {
214 383
            $callback(
215
                function ($value = null) {
216 214
                    $this->resolve($value);
217 383
                },
218
                function ($reason = null) {
219 156
                    $this->reject($reason);
220 383
                },
221 383
                function ($update = null) {
222 46
                    $this->notify($update);
223 383
                }
224
            );
225 4
        } catch (\Exception $e) {
226 4
            $this->reject($e);
227
        }
228 383
    }
229
}
230