GuzzleHttpPromisePromise

Promises/A+ implementation that avoids recursion when possible.

Defined (1)

The class is defined in the following location(s).

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