Utils.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <?php
  2. namespace GuzzleHttp\Promise;
  3. final class Utils
  4. {
  5. /**
  6. * Get the global task queue used for promise resolution.
  7. *
  8. * This task queue MUST be run in an event loop in order for promises to be
  9. * settled asynchronously. It will be automatically run when synchronously
  10. * waiting on a promise.
  11. *
  12. * <code>
  13. * while ($eventLoop->isRunning()) {
  14. * GuzzleHttp\Promise\Utils::queue()->run();
  15. * }
  16. * </code>
  17. *
  18. * @param TaskQueueInterface $assign Optionally specify a new queue instance.
  19. *
  20. * @return TaskQueueInterface
  21. */
  22. public static function queue(TaskQueueInterface $assign = null)
  23. {
  24. static $queue;
  25. if ($assign) {
  26. $queue = $assign;
  27. } elseif (!$queue) {
  28. $queue = new TaskQueue();
  29. }
  30. return $queue;
  31. }
  32. /**
  33. * Adds a function to run in the task queue when it is next `run()` and
  34. * returns a promise that is fulfilled or rejected with the result.
  35. *
  36. * @param callable $task Task function to run.
  37. *
  38. * @return PromiseInterface
  39. */
  40. public static function task(callable $task)
  41. {
  42. $queue = self::queue();
  43. $promise = new Promise([$queue, 'run']);
  44. $queue->add(function () use ($task, $promise) {
  45. try {
  46. $promise->resolve($task());
  47. } catch (\Throwable $e) {
  48. $promise->reject($e);
  49. } catch (\Exception $e) {
  50. $promise->reject($e);
  51. }
  52. });
  53. return $promise;
  54. }
  55. /**
  56. * Synchronously waits on a promise to resolve and returns an inspection
  57. * state array.
  58. *
  59. * Returns a state associative array containing a "state" key mapping to a
  60. * valid promise state. If the state of the promise is "fulfilled", the
  61. * array will contain a "value" key mapping to the fulfilled value of the
  62. * promise. If the promise is rejected, the array will contain a "reason"
  63. * key mapping to the rejection reason of the promise.
  64. *
  65. * @param PromiseInterface $promise Promise or value.
  66. *
  67. * @return array
  68. */
  69. public static function inspect(PromiseInterface $promise)
  70. {
  71. try {
  72. return [
  73. 'state' => PromiseInterface::FULFILLED,
  74. 'value' => $promise->wait()
  75. ];
  76. } catch (RejectionException $e) {
  77. return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
  78. } catch (\Throwable $e) {
  79. return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
  80. } catch (\Exception $e) {
  81. return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
  82. }
  83. }
  84. /**
  85. * Waits on all of the provided promises, but does not unwrap rejected
  86. * promises as thrown exception.
  87. *
  88. * Returns an array of inspection state arrays.
  89. *
  90. * @see inspect for the inspection state array format.
  91. *
  92. * @param PromiseInterface[] $promises Traversable of promises to wait upon.
  93. *
  94. * @return array
  95. */
  96. public static function inspectAll($promises)
  97. {
  98. $results = [];
  99. foreach ($promises as $key => $promise) {
  100. $results[$key] = inspect($promise);
  101. }
  102. return $results;
  103. }
  104. /**
  105. * Waits on all of the provided promises and returns the fulfilled values.
  106. *
  107. * Returns an array that contains the value of each promise (in the same
  108. * order the promises were provided). An exception is thrown if any of the
  109. * promises are rejected.
  110. *
  111. * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
  112. *
  113. * @return array
  114. *
  115. * @throws \Exception on error
  116. * @throws \Throwable on error in PHP >=7
  117. */
  118. public static function unwrap($promises)
  119. {
  120. $results = [];
  121. foreach ($promises as $key => $promise) {
  122. $results[$key] = $promise->wait();
  123. }
  124. return $results;
  125. }
  126. /**
  127. * Given an array of promises, return a promise that is fulfilled when all
  128. * the items in the array are fulfilled.
  129. *
  130. * The promise's fulfillment value is an array with fulfillment values at
  131. * respective positions to the original array. If any promise in the array
  132. * rejects, the returned promise is rejected with the rejection reason.
  133. *
  134. * @param mixed $promises Promises or values.
  135. * @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
  136. *
  137. * @return PromiseInterface
  138. */
  139. public static function all($promises, $recursive = false)
  140. {
  141. $results = [];
  142. $promise = Each::of(
  143. $promises,
  144. function ($value, $idx) use (&$results) {
  145. $results[$idx] = $value;
  146. },
  147. function ($reason, $idx, Promise $aggregate) {
  148. $aggregate->reject($reason);
  149. }
  150. )->then(function () use (&$results) {
  151. ksort($results);
  152. return $results;
  153. });
  154. if (true === $recursive) {
  155. $promise = $promise->then(function ($results) use ($recursive, &$promises) {
  156. foreach ($promises as $promise) {
  157. if (Is::pending($promise)) {
  158. return self::all($promises, $recursive);
  159. }
  160. }
  161. return $results;
  162. });
  163. }
  164. return $promise;
  165. }
  166. /**
  167. * Initiate a competitive race between multiple promises or values (values
  168. * will become immediately fulfilled promises).
  169. *
  170. * When count amount of promises have been fulfilled, the returned promise
  171. * is fulfilled with an array that contains the fulfillment values of the
  172. * winners in order of resolution.
  173. *
  174. * This promise is rejected with a {@see AggregateException} if the number
  175. * of fulfilled promises is less than the desired $count.
  176. *
  177. * @param int $count Total number of promises.
  178. * @param mixed $promises Promises or values.
  179. *
  180. * @return PromiseInterface
  181. */
  182. public static function some($count, $promises)
  183. {
  184. $results = [];
  185. $rejections = [];
  186. return Each::of(
  187. $promises,
  188. function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
  189. if (Is::settled($p)) {
  190. return;
  191. }
  192. $results[$idx] = $value;
  193. if (count($results) >= $count) {
  194. $p->resolve(null);
  195. }
  196. },
  197. function ($reason) use (&$rejections) {
  198. $rejections[] = $reason;
  199. }
  200. )->then(
  201. function () use (&$results, &$rejections, $count) {
  202. if (count($results) !== $count) {
  203. throw new AggregateException(
  204. 'Not enough promises to fulfill count',
  205. $rejections
  206. );
  207. }
  208. ksort($results);
  209. return array_values($results);
  210. }
  211. );
  212. }
  213. /**
  214. * Like some(), with 1 as count. However, if the promise fulfills, the
  215. * fulfillment value is not an array of 1 but the value directly.
  216. *
  217. * @param mixed $promises Promises or values.
  218. *
  219. * @return PromiseInterface
  220. */
  221. public static function any($promises)
  222. {
  223. return self::some(1, $promises)->then(function ($values) {
  224. return $values[0];
  225. });
  226. }
  227. /**
  228. * Returns a promise that is fulfilled when all of the provided promises have
  229. * been fulfilled or rejected.
  230. *
  231. * The returned promise is fulfilled with an array of inspection state arrays.
  232. *
  233. * @see inspect for the inspection state array format.
  234. *
  235. * @param mixed $promises Promises or values.
  236. *
  237. * @return PromiseInterface
  238. */
  239. public static function settle($promises)
  240. {
  241. $results = [];
  242. return Each::of(
  243. $promises,
  244. function ($value, $idx) use (&$results) {
  245. $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
  246. },
  247. function ($reason, $idx) use (&$results) {
  248. $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
  249. }
  250. )->then(function () use (&$results) {
  251. ksort($results);
  252. return $results;
  253. });
  254. }
  255. }