ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Promise.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Sabre\Event;
4 
5 use Exception;
6 
23 class Promise {
24 
28  const PENDING = 0;
29 
33  const FULFILLED = 1;
34 
38  const REJECTED = 2;
39 
45  public $state = self::PENDING;
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
$type
otherwise(callable $onRejected)
Add a callback for when this promise is rejected.
Definition: Promise.php:129
An implementation of the Promise pattern.
Definition: Promise.php:23
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
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
reject($reason=null)
Marks this promise as rejected, and set it&#39;s rejection reason.
Definition: Promise.php:161
This exception is thrown when the user tried to reject or fulfill a promise, after either of these ac...
nextTick(callable $cb)
Runs a function immediately at the next iteration of the loop.
Definition: functions.php:52
const REJECTED
The asynchronous operation has completed with an error.
Definition: Promise.php:38
wait()
Stops execution until this promise is resolved.
Definition: Promise.php:187
tick($block=false)
Executes all pending events.
Definition: functions.php:151
error(callable $onRejected)
Alias for &#39;otherwise&#39;.
Definition: Promise.php:299
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
const PENDING
The asynchronous operation is pending.
Definition: Promise.php:28