Search moodle.org's
Developer Documentation

See Release Notes

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