vendor/guzzlehttp/promises/src/Promise.php line 62

Open in your IDE?
  1. <?php
  2. namespace GuzzleHttp\Promise;
  3. /**
  4.  * Promises/A+ implementation that avoids recursion when possible.
  5.  *
  6.  * @link https://promisesaplus.com/
  7.  */
  8. class Promise implements PromiseInterface
  9. {
  10.     private $state self::PENDING;
  11.     private $result;
  12.     private $cancelFn;
  13.     private $waitFn;
  14.     private $waitList;
  15.     private $handlers = [];
  16.     /**
  17.      * @param callable $waitFn   Fn that when invoked resolves the promise.
  18.      * @param callable $cancelFn Fn that when invoked cancels the promise.
  19.      */
  20.     public function __construct(
  21.         callable $waitFn null,
  22.         callable $cancelFn null
  23.     ) {
  24.         $this->waitFn $waitFn;
  25.         $this->cancelFn $cancelFn;
  26.     }
  27.     public function then(
  28.         callable $onFulfilled null,
  29.         callable $onRejected null
  30.     ) {
  31.         if ($this->state === self::PENDING) {
  32.             $p = new Promise(null, [$this'cancel']);
  33.             $this->handlers[] = [$p$onFulfilled$onRejected];
  34.             $p->waitList $this->waitList;
  35.             $p->waitList[] = $this;
  36.             return $p;
  37.         }
  38.         // Return a fulfilled promise and immediately invoke any callbacks.
  39.         if ($this->state === self::FULFILLED) {
  40.             $promise Create::promiseFor($this->result);
  41.             return $onFulfilled $promise->then($onFulfilled) : $promise;
  42.         }
  43.         // It's either cancelled or rejected, so return a rejected promise
  44.         // and immediately invoke any callbacks.
  45.         $rejection Create::rejectionFor($this->result);
  46.         return $onRejected $rejection->then(null$onRejected) : $rejection;
  47.     }
  48.     public function otherwise(callable $onRejected)
  49.     {
  50.         return $this->then(null$onRejected);
  51.     }
  52.     public function wait($unwrap true)
  53.     {
  54.         $this->waitIfPending();
  55.         if ($this->result instanceof PromiseInterface) {
  56.             return $this->result->wait($unwrap);
  57.         }
  58.         if ($unwrap) {
  59.             if ($this->state === self::FULFILLED) {
  60.                 return $this->result;
  61.             }
  62.             // It's rejected so "unwrap" and throw an exception.
  63.             throw Create::exceptionFor($this->result);
  64.         }
  65.     }
  66.     public function getState()
  67.     {
  68.         return $this->state;
  69.     }
  70.     public function cancel()
  71.     {
  72.         if ($this->state !== self::PENDING) {
  73.             return;
  74.         }
  75.         $this->waitFn $this->waitList null;
  76.         if ($this->cancelFn) {
  77.             $fn $this->cancelFn;
  78.             $this->cancelFn null;
  79.             try {
  80.                 $fn();
  81.             } catch (\Throwable $e) {
  82.                 $this->reject($e);
  83.             } catch (\Exception $e) {
  84.                 $this->reject($e);
  85.             }
  86.         }
  87.         // Reject the promise only if it wasn't rejected in a then callback.
  88.         /** @psalm-suppress RedundantCondition */
  89.         if ($this->state === self::PENDING) {
  90.             $this->reject(new CancellationException('Promise has been cancelled'));
  91.         }
  92.     }
  93.     public function resolve($value)
  94.     {
  95.         $this->settle(self::FULFILLED$value);
  96.     }
  97.     public function reject($reason)
  98.     {
  99.         $this->settle(self::REJECTED$reason);
  100.     }
  101.     private function settle($state$value)
  102.     {
  103.         if ($this->state !== self::PENDING) {
  104.             // Ignore calls with the same resolution.
  105.             if ($state === $this->state && $value === $this->result) {
  106.                 return;
  107.             }
  108.             throw $this->state === $state
  109.                 ? new \LogicException("The promise is already {$state}.")
  110.                 : new \LogicException("Cannot change a {$this->state} promise to {$state}");
  111.         }
  112.         if ($value === $this) {
  113.             throw new \LogicException('Cannot fulfill or reject a promise with itself');
  114.         }
  115.         // Clear out the state of the promise but stash the handlers.
  116.         $this->state $state;
  117.         $this->result $value;
  118.         $handlers $this->handlers;
  119.         $this->handlers null;
  120.         $this->waitList $this->waitFn null;
  121.         $this->cancelFn null;
  122.         if (!$handlers) {
  123.             return;
  124.         }
  125.         // If the value was not a settled promise or a thenable, then resolve
  126.         // it in the task queue using the correct ID.
  127.         if (!is_object($value) || !method_exists($value'then')) {
  128.             $id $state === self::FULFILLED 2;
  129.             // It's a success, so resolve the handlers in the queue.
  130.             Utils::queue()->add(static function () use ($id$value$handlers) {
  131.                 foreach ($handlers as $handler) {
  132.                     self::callHandler($id$value$handler);
  133.                 }
  134.             });
  135.         } elseif ($value instanceof Promise && Is::pending($value)) {
  136.             // We can just merge our handlers onto the next promise.
  137.             $value->handlers array_merge($value->handlers$handlers);
  138.         } else {
  139.             // Resolve the handlers when the forwarded promise is resolved.
  140.             $value->then(
  141.                 static function ($value) use ($handlers) {
  142.                     foreach ($handlers as $handler) {
  143.                         self::callHandler(1$value$handler);
  144.                     }
  145.                 },
  146.                 static function ($reason) use ($handlers) {
  147.                     foreach ($handlers as $handler) {
  148.                         self::callHandler(2$reason$handler);
  149.                     }
  150.                 }
  151.             );
  152.         }
  153.     }
  154.     /**
  155.      * Call a stack of handlers using a specific callback index and value.
  156.      *
  157.      * @param int   $index   1 (resolve) or 2 (reject).
  158.      * @param mixed $value   Value to pass to the callback.
  159.      * @param array $handler Array of handler data (promise and callbacks).
  160.      */
  161.     private static function callHandler($index$value, array $handler)
  162.     {
  163.         /** @var PromiseInterface $promise */
  164.         $promise $handler[0];
  165.         // The promise may have been cancelled or resolved before placing
  166.         // this thunk in the queue.
  167.         if (Is::settled($promise)) {
  168.             return;
  169.         }
  170.         try {
  171.             if (isset($handler[$index])) {
  172.                 /*
  173.                  * If $f throws an exception, then $handler will be in the exception
  174.                  * stack trace. Since $handler contains a reference to the callable
  175.                  * itself we get a circular reference. We clear the $handler
  176.                  * here to avoid that memory leak.
  177.                  */
  178.                 $f $handler[$index];
  179.                 unset($handler);
  180.                 $promise->resolve($f($value));
  181.             } elseif ($index === 1) {
  182.                 // Forward resolution values as-is.
  183.                 $promise->resolve($value);
  184.             } else {
  185.                 // Forward rejections down the chain.
  186.                 $promise->reject($value);
  187.             }
  188.         } catch (\Throwable $reason) {
  189.             $promise->reject($reason);
  190.         } catch (\Exception $reason) {
  191.             $promise->reject($reason);
  192.         }
  193.     }
  194.     private function waitIfPending()
  195.     {
  196.         if ($this->state !== self::PENDING) {
  197.             return;
  198.         } elseif ($this->waitFn) {
  199.             $this->invokeWaitFn();
  200.         } elseif ($this->waitList) {
  201.             $this->invokeWaitList();
  202.         } else {
  203.             // If there's no wait function, then reject the promise.
  204.             $this->reject('Cannot wait on a promise that has '
  205.                 'no internal wait function. You must provide a wait '
  206.                 'function when constructing the promise to be able to '
  207.                 'wait on a promise.');
  208.         }
  209.         Utils::queue()->run();
  210.         /** @psalm-suppress RedundantCondition */
  211.         if ($this->state === self::PENDING) {
  212.             $this->reject('Invoking the wait callback did not resolve the promise');
  213.         }
  214.     }
  215.     private function invokeWaitFn()
  216.     {
  217.         try {
  218.             $wfn $this->waitFn;
  219.             $this->waitFn null;
  220.             $wfn(true);
  221.         } catch (\Exception $reason) {
  222.             if ($this->state === self::PENDING) {
  223.                 // The promise has not been resolved yet, so reject the promise
  224.                 // with the exception.
  225.                 $this->reject($reason);
  226.             } else {
  227.                 // The promise was already resolved, so there's a problem in
  228.                 // the application.
  229.                 throw $reason;
  230.             }
  231.         }
  232.     }
  233.     private function invokeWaitList()
  234.     {
  235.         $waitList $this->waitList;
  236.         $this->waitList null;
  237.         foreach ($waitList as $result) {
  238.             do {
  239.                 $result->waitIfPending();
  240.                 $result $result->result;
  241.             } while ($result instanceof Promise);
  242.             if ($result instanceof PromiseInterface) {
  243.                 $result->wait(false);
  244.             }
  245.         }
  246.     }
  247. }