ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Promise.php
Go to the documentation of this file.
1<?php
2
3namespace Sabre\Event;
4
5use Exception;
6
23class Promise {
24
28 const PENDING = 0;
29
33 const FULFILLED = 1;
34
38 const REJECTED = 2;
39
46
58 function __construct(callable $executor = null) {
59
60 if ($executor) {
61 $executor(
62 [$this, 'fulfill'],
63 [$this, 'reject']
64 );
65 }
66
67 }
68
92 function then(callable $onFulfilled = null, callable $onRejected = null) {
93
94 // This new subPromise will be returned from this function, and will
95 // be fulfilled with the result of the onFulfilled or onRejected event
96 // handlers.
97 $subPromise = new self();
98
99 switch ($this->state) {
100 case self::PENDING :
101 // The operation is pending, so we keep a reference to the
102 // event handlers so we can call them later.
103 $this->subscribers[] = [$subPromise, $onFulfilled, $onRejected];
104 break;
105 case self::FULFILLED :
106 // The async operation is already fulfilled, so we trigger the
107 // onFulfilled callback asap.
108 $this->invokeCallback($subPromise, $onFulfilled);
109 break;
110 case self::REJECTED :
111 // The async operation failed, so we call teh onRejected
112 // callback asap.
113 $this->invokeCallback($subPromise, $onRejected);
114 break;
115 }
116 return $subPromise;
117
118 }
119
129 function otherwise(callable $onRejected) {
130
131 return $this->then(null, $onRejected);
132
133 }
134
141 function fulfill($value = null) {
142 if ($this->state !== self::PENDING) {
143 throw new PromiseAlreadyResolvedException('This promise is already resolved, and you\'re not allowed to resolve a promise more than once');
144 }
145 $this->state = self::FULFILLED;
146 $this->value = $value;
147 foreach ($this->subscribers as $subscriber) {
148 $this->invokeCallback($subscriber[0], $subscriber[1]);
149 }
150 }
151
161 function reject($reason = null) {
162 if ($this->state !== self::PENDING) {
163 throw new PromiseAlreadyResolvedException('This promise is already resolved, and you\'re not allowed to resolve a promise more than once');
164 }
165 $this->state = self::REJECTED;
166 $this->value = $reason;
167 foreach ($this->subscribers as $subscriber) {
168 $this->invokeCallback($subscriber[0], $subscriber[2]);
169 }
170
171 }
172
187 function wait() {
188
189 $hasEvents = true;
190 while ($this->state === self::PENDING) {
191
192 if (!$hasEvents) {
193 throw new \LogicException('There were no more events in the loop. This promise will never be fulfilled.');
194 }
195
196 // As long as the promise is not fulfilled, we tell the event loop
197 // to handle events, and to block.
198 $hasEvents = Loop\tick(true);
199
200 }
201
202 if ($this->state === self::FULFILLED) {
203 // If the state of this promise is fulfilled, we can return the value.
204 return $this->value;
205 } else {
206 // If we got here, it means that the asynchronous operation
207 // errored. Therefore we need to throw an exception.
208 $reason = $this->value;
209 if ($reason instanceof Exception) {
210 throw $reason;
211 } elseif (is_scalar($reason)) {
212 throw new Exception($reason);
213 } else {
214 $type = is_object($reason) ? get_class($reason) : gettype($reason);
215 throw new Exception('Promise was rejected with reason of type: ' . $type);
216 }
217 }
218
219
220 }
221
222
229 protected $subscribers = [];
230
239 protected $value = null;
240
252 private function invokeCallback(Promise $subPromise, callable $callBack = null) {
253
254 // We use 'nextTick' to ensure that the event handlers are always
255 // triggered outside of the calling stack in which they were originally
256 // passed to 'then'.
257 //
258 // This makes the order of execution more predictable.
259 Loop\nextTick(function() use ($callBack, $subPromise) {
260 if (is_callable($callBack)) {
261 try {
262
263 $result = $callBack($this->value);
264 if ($result instanceof self) {
265 // If the callback (onRejected or onFulfilled)
266 // returned a promise, we only fulfill or reject the
267 // chained promise once that promise has also been
268 // resolved.
269 $result->then([$subPromise, 'fulfill'], [$subPromise, 'reject']);
270 } else {
271 // If the callback returned any other value, we
272 // immediately fulfill the chained promise.
273 $subPromise->fulfill($result);
274 }
275 } catch (Exception $e) {
276 // If the event handler threw an exception, we need to make sure that
277 // the chained promise is rejected as well.
278 $subPromise->reject($e);
279 }
280 } else {
281 if ($this->state === self::FULFILLED) {
282 $subPromise->fulfill($this->value);
283 } else {
284 $subPromise->reject($this->value);
285 }
286 }
287 });
288 }
289
299 function error(callable $onRejected) {
300
301 return $this->otherwise($onRejected);
302
303 }
304
314 static function all(array $promises) {
315
316 return Promise\all($promises);
317
318 }
319
320}
$result
An exception for terminatinating execution or to throw for unit testing.
tick($block=false)
Executes all pending events.
Definition: Loop.php:226
nextTick(callable $cb)
Runs a function immediately at the next iteration of the loop.
Definition: Loop.php:112
This exception is thrown when the user tried to reject or fulfill a promise, after either of these ac...
An implementation of the Promise pattern.
Definition: Promise.php:23
static all(array $promises)
Deprecated.
Definition: Promise.php:314
fulfill($value=null)
Marks this promise as fulfilled and sets its return value.
Definition: Promise.php:141
invokeCallback(Promise $subPromise, callable $callBack=null)
This method is used to call either an onFulfilled or onRejected callback.
Definition: Promise.php:252
const FULFILLED
The asynchronous operation has completed, and has a result.
Definition: Promise.php:33
__construct(callable $executor=null)
Creates the promise.
Definition: Promise.php:58
otherwise(callable $onRejected)
Add a callback for when this promise is rejected.
Definition: Promise.php:129
const PENDING
The asynchronous operation is pending.
Definition: Promise.php:28
then(callable $onFulfilled=null, callable $onRejected=null)
This method allows you to specify the callback that will be called after the promise has been fulfill...
Definition: Promise.php:92
const REJECTED
The asynchronous operation has completed with an error.
Definition: Promise.php:38
reject($reason=null)
Marks this promise as rejected, and set it's rejection reason.
Definition: Promise.php:161
error(callable $onRejected)
Alias for 'otherwise'.
Definition: Promise.php:299
wait()
Stops execution until this promise is resolved.
Definition: Promise.php:187
$type