summaryrefslogtreecommitdiff
path: root/vendor/guzzlehttp/promises/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/guzzlehttp/promises/src')
-rw-r--r--vendor/guzzlehttp/promises/src/AggregateException.php19
-rw-r--r--vendor/guzzlehttp/promises/src/CancellationException.php12
-rw-r--r--vendor/guzzlehttp/promises/src/Coroutine.php162
-rw-r--r--vendor/guzzlehttp/promises/src/Create.php79
-rw-r--r--vendor/guzzlehttp/promises/src/Each.php86
-rw-r--r--vendor/guzzlehttp/promises/src/EachPromise.php250
-rw-r--r--vendor/guzzlehttp/promises/src/FulfilledPromise.php89
-rw-r--r--vendor/guzzlehttp/promises/src/Is.php40
-rw-r--r--vendor/guzzlehttp/promises/src/Promise.php281
-rw-r--r--vendor/guzzlehttp/promises/src/PromiseInterface.php91
-rw-r--r--vendor/guzzlehttp/promises/src/PromisorInterface.php16
-rw-r--r--vendor/guzzlehttp/promises/src/RejectedPromise.php95
-rw-r--r--vendor/guzzlehttp/promises/src/RejectionException.php49
-rw-r--r--vendor/guzzlehttp/promises/src/TaskQueue.php71
-rw-r--r--vendor/guzzlehttp/promises/src/TaskQueueInterface.php24
-rw-r--r--vendor/guzzlehttp/promises/src/Utils.php259
16 files changed, 1623 insertions, 0 deletions
diff --git a/vendor/guzzlehttp/promises/src/AggregateException.php b/vendor/guzzlehttp/promises/src/AggregateException.php
new file mode 100644
index 000000000..40ffdbcf1
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/AggregateException.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * Exception thrown when too many errors occur in the some() or any() methods.
+ */
+class AggregateException extends RejectionException
+{
+ public function __construct(string $msg, array $reasons)
+ {
+ parent::__construct(
+ $reasons,
+ sprintf('%s; %d rejected promises', $msg, count($reasons))
+ );
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/CancellationException.php b/vendor/guzzlehttp/promises/src/CancellationException.php
new file mode 100644
index 000000000..ad8f51d48
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/CancellationException.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * Exception that is set as the reason for a promise that has been cancelled.
+ */
+class CancellationException extends RejectionException
+{
+}
diff --git a/vendor/guzzlehttp/promises/src/Coroutine.php b/vendor/guzzlehttp/promises/src/Coroutine.php
new file mode 100644
index 000000000..0b5b9c0a4
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Coroutine.php
@@ -0,0 +1,162 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+use Generator;
+use Throwable;
+
+/**
+ * Creates a promise that is resolved using a generator that yields values or
+ * promises (somewhat similar to C#'s async keyword).
+ *
+ * When called, the Coroutine::of method will start an instance of the generator
+ * and returns a promise that is fulfilled with its final yielded value.
+ *
+ * Control is returned back to the generator when the yielded promise settles.
+ * This can lead to less verbose code when doing lots of sequential async calls
+ * with minimal processing in between.
+ *
+ * use GuzzleHttp\Promise;
+ *
+ * function createPromise($value) {
+ * return new Promise\FulfilledPromise($value);
+ * }
+ *
+ * $promise = Promise\Coroutine::of(function () {
+ * $value = (yield createPromise('a'));
+ * try {
+ * $value = (yield createPromise($value . 'b'));
+ * } catch (\Throwable $e) {
+ * // The promise was rejected.
+ * }
+ * yield $value . 'c';
+ * });
+ *
+ * // Outputs "abc"
+ * $promise->then(function ($v) { echo $v; });
+ *
+ * @param callable $generatorFn Generator function to wrap into a promise.
+ *
+ * @return Promise
+ *
+ * @see https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
+ */
+final class Coroutine implements PromiseInterface
+{
+ /**
+ * @var PromiseInterface|null
+ */
+ private $currentPromise;
+
+ /**
+ * @var Generator
+ */
+ private $generator;
+
+ /**
+ * @var Promise
+ */
+ private $result;
+
+ public function __construct(callable $generatorFn)
+ {
+ $this->generator = $generatorFn();
+ $this->result = new Promise(function (): void {
+ while (isset($this->currentPromise)) {
+ $this->currentPromise->wait();
+ }
+ });
+ try {
+ $this->nextCoroutine($this->generator->current());
+ } catch (Throwable $throwable) {
+ $this->result->reject($throwable);
+ }
+ }
+
+ /**
+ * Create a new coroutine.
+ */
+ public static function of(callable $generatorFn): self
+ {
+ return new self($generatorFn);
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ): PromiseInterface {
+ return $this->result->then($onFulfilled, $onRejected);
+ }
+
+ public function otherwise(callable $onRejected): PromiseInterface
+ {
+ return $this->result->otherwise($onRejected);
+ }
+
+ public function wait(bool $unwrap = true)
+ {
+ return $this->result->wait($unwrap);
+ }
+
+ public function getState(): string
+ {
+ return $this->result->getState();
+ }
+
+ public function resolve($value): void
+ {
+ $this->result->resolve($value);
+ }
+
+ public function reject($reason): void
+ {
+ $this->result->reject($reason);
+ }
+
+ public function cancel(): void
+ {
+ $this->currentPromise->cancel();
+ $this->result->cancel();
+ }
+
+ private function nextCoroutine($yielded): void
+ {
+ $this->currentPromise = Create::promiseFor($yielded)
+ ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
+ }
+
+ /**
+ * @internal
+ */
+ public function _handleSuccess($value): void
+ {
+ unset($this->currentPromise);
+ try {
+ $next = $this->generator->send($value);
+ if ($this->generator->valid()) {
+ $this->nextCoroutine($next);
+ } else {
+ $this->result->resolve($value);
+ }
+ } catch (Throwable $throwable) {
+ $this->result->reject($throwable);
+ }
+ }
+
+ /**
+ * @internal
+ */
+ public function _handleFailure($reason): void
+ {
+ unset($this->currentPromise);
+ try {
+ $nextYield = $this->generator->throw(Create::exceptionFor($reason));
+ // The throw was caught, so keep iterating on the coroutine
+ $this->nextCoroutine($nextYield);
+ } catch (Throwable $throwable) {
+ $this->result->reject($throwable);
+ }
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/Create.php b/vendor/guzzlehttp/promises/src/Create.php
new file mode 100644
index 000000000..9d3fc4a1e
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Create.php
@@ -0,0 +1,79 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+final class Create
+{
+ /**
+ * Creates a promise for a value if the value is not a promise.
+ *
+ * @param mixed $value Promise or value.
+ */
+ public static function promiseFor($value): PromiseInterface
+ {
+ if ($value instanceof PromiseInterface) {
+ return $value;
+ }
+
+ // Return a Guzzle promise that shadows the given promise.
+ if (is_object($value) && method_exists($value, 'then')) {
+ $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
+ $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
+ $promise = new Promise($wfn, $cfn);
+ $value->then([$promise, 'resolve'], [$promise, 'reject']);
+
+ return $promise;
+ }
+
+ return new FulfilledPromise($value);
+ }
+
+ /**
+ * Creates a rejected promise for a reason if the reason is not a promise.
+ * If the provided reason is a promise, then it is returned as-is.
+ *
+ * @param mixed $reason Promise or reason.
+ */
+ public static function rejectionFor($reason): PromiseInterface
+ {
+ if ($reason instanceof PromiseInterface) {
+ return $reason;
+ }
+
+ return new RejectedPromise($reason);
+ }
+
+ /**
+ * Create an exception for a rejected promise value.
+ *
+ * @param mixed $reason
+ */
+ public static function exceptionFor($reason): \Throwable
+ {
+ if ($reason instanceof \Throwable) {
+ return $reason;
+ }
+
+ return new RejectionException($reason);
+ }
+
+ /**
+ * Returns an iterator for the given value.
+ *
+ * @param mixed $value
+ */
+ public static function iterFor($value): \Iterator
+ {
+ if ($value instanceof \Iterator) {
+ return $value;
+ }
+
+ if (is_array($value)) {
+ return new \ArrayIterator($value);
+ }
+
+ return new \ArrayIterator([$value]);
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/Each.php b/vendor/guzzlehttp/promises/src/Each.php
new file mode 100644
index 000000000..1a7aa0fb6
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Each.php
@@ -0,0 +1,86 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+final class Each
+{
+ /**
+ * Given an iterator that yields promises or values, returns a promise that
+ * is fulfilled with a null value when the iterator has been consumed or
+ * the aggregate promise has been fulfilled or rejected.
+ *
+ * $onFulfilled is a function that accepts the fulfilled value, iterator
+ * index, and the aggregate promise. The callback can invoke any necessary
+ * side effects and choose to resolve or reject the aggregate if needed.
+ *
+ * $onRejected is a function that accepts the rejection reason, iterator
+ * index, and the aggregate promise. The callback can invoke any necessary
+ * side effects and choose to resolve or reject the aggregate if needed.
+ *
+ * @param mixed $iterable Iterator or array to iterate over.
+ * @param callable $onFulfilled
+ * @param callable $onRejected
+ */
+ public static function of(
+ $iterable,
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ): PromiseInterface {
+ return (new EachPromise($iterable, [
+ 'fulfilled' => $onFulfilled,
+ 'rejected' => $onRejected,
+ ]))->promise();
+ }
+
+ /**
+ * Like of, but only allows a certain number of outstanding promises at any
+ * given time.
+ *
+ * $concurrency may be an integer or a function that accepts the number of
+ * pending promises and returns a numeric concurrency limit value to allow
+ * for dynamic a concurrency size.
+ *
+ * @param mixed $iterable
+ * @param int|callable $concurrency
+ * @param callable $onFulfilled
+ * @param callable $onRejected
+ */
+ public static function ofLimit(
+ $iterable,
+ $concurrency,
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ): PromiseInterface {
+ return (new EachPromise($iterable, [
+ 'fulfilled' => $onFulfilled,
+ 'rejected' => $onRejected,
+ 'concurrency' => $concurrency,
+ ]))->promise();
+ }
+
+ /**
+ * Like limit, but ensures that no promise in the given $iterable argument
+ * is rejected. If any promise is rejected, then the aggregate promise is
+ * rejected with the encountered rejection.
+ *
+ * @param mixed $iterable
+ * @param int|callable $concurrency
+ * @param callable $onFulfilled
+ */
+ public static function ofLimitAll(
+ $iterable,
+ $concurrency,
+ callable $onFulfilled = null
+ ): PromiseInterface {
+ return self::ofLimit(
+ $iterable,
+ $concurrency,
+ $onFulfilled,
+ function ($reason, $idx, PromiseInterface $aggregate): void {
+ $aggregate->reject($reason);
+ }
+ );
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/EachPromise.php b/vendor/guzzlehttp/promises/src/EachPromise.php
new file mode 100644
index 000000000..28dd9793a
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/EachPromise.php
@@ -0,0 +1,250 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * Represents a promise that iterates over many promises and invokes
+ * side-effect functions in the process.
+ *
+ * @final
+ */
+class EachPromise implements PromisorInterface
+{
+ private $pending = [];
+
+ private $nextPendingIndex = 0;
+
+ /** @var \Iterator|null */
+ private $iterable;
+
+ /** @var callable|int|null */
+ private $concurrency;
+
+ /** @var callable|null */
+ private $onFulfilled;
+
+ /** @var callable|null */
+ private $onRejected;
+
+ /** @var Promise|null */
+ private $aggregate;
+
+ /** @var bool|null */
+ private $mutex;
+
+ /**
+ * Configuration hash can include the following key value pairs:
+ *
+ * - fulfilled: (callable) Invoked when a promise fulfills. The function
+ * is invoked with three arguments: the fulfillment value, the index
+ * position from the iterable list of the promise, and the aggregate
+ * promise that manages all of the promises. The aggregate promise may
+ * be resolved from within the callback to short-circuit the promise.
+ * - rejected: (callable) Invoked when a promise is rejected. The
+ * function is invoked with three arguments: the rejection reason, the
+ * index position from the iterable list of the promise, and the
+ * aggregate promise that manages all of the promises. The aggregate
+ * promise may be resolved from within the callback to short-circuit
+ * the promise.
+ * - concurrency: (integer) Pass this configuration option to limit the
+ * allowed number of outstanding concurrently executing promises,
+ * creating a capped pool of promises. There is no limit by default.
+ *
+ * @param mixed $iterable Promises or values to iterate.
+ * @param array $config Configuration options
+ */
+ public function __construct($iterable, array $config = [])
+ {
+ $this->iterable = Create::iterFor($iterable);
+
+ if (isset($config['concurrency'])) {
+ $this->concurrency = $config['concurrency'];
+ }
+
+ if (isset($config['fulfilled'])) {
+ $this->onFulfilled = $config['fulfilled'];
+ }
+
+ if (isset($config['rejected'])) {
+ $this->onRejected = $config['rejected'];
+ }
+ }
+
+ /** @psalm-suppress InvalidNullableReturnType */
+ public function promise(): PromiseInterface
+ {
+ if ($this->aggregate) {
+ return $this->aggregate;
+ }
+
+ try {
+ $this->createPromise();
+ /** @psalm-assert Promise $this->aggregate */
+ $this->iterable->rewind();
+ $this->refillPending();
+ } catch (\Throwable $e) {
+ $this->aggregate->reject($e);
+ }
+
+ /**
+ * @psalm-suppress NullableReturnStatement
+ */
+ return $this->aggregate;
+ }
+
+ private function createPromise(): void
+ {
+ $this->mutex = false;
+ $this->aggregate = new Promise(function (): void {
+ if ($this->checkIfFinished()) {
+ return;
+ }
+ reset($this->pending);
+ // Consume a potentially fluctuating list of promises while
+ // ensuring that indexes are maintained (precluding array_shift).
+ while ($promise = current($this->pending)) {
+ next($this->pending);
+ $promise->wait();
+ if (Is::settled($this->aggregate)) {
+ return;
+ }
+ }
+ });
+
+ // Clear the references when the promise is resolved.
+ $clearFn = function (): void {
+ $this->iterable = $this->concurrency = $this->pending = null;
+ $this->onFulfilled = $this->onRejected = null;
+ $this->nextPendingIndex = 0;
+ };
+
+ $this->aggregate->then($clearFn, $clearFn);
+ }
+
+ private function refillPending(): void
+ {
+ if (!$this->concurrency) {
+ // Add all pending promises.
+ while ($this->addPending() && $this->advanceIterator()) {
+ }
+
+ return;
+ }
+
+ // Add only up to N pending promises.
+ $concurrency = is_callable($this->concurrency)
+ ? call_user_func($this->concurrency, count($this->pending))
+ : $this->concurrency;
+ $concurrency = max($concurrency - count($this->pending), 0);
+ // Concurrency may be set to 0 to disallow new promises.
+ if (!$concurrency) {
+ return;
+ }
+ // Add the first pending promise.
+ $this->addPending();
+ // Note this is special handling for concurrency=1 so that we do
+ // not advance the iterator after adding the first promise. This
+ // helps work around issues with generators that might not have the
+ // next value to yield until promise callbacks are called.
+ while (--$concurrency
+ && $this->advanceIterator()
+ && $this->addPending()) {
+ }
+ }
+
+ private function addPending(): bool
+ {
+ if (!$this->iterable || !$this->iterable->valid()) {
+ return false;
+ }
+
+ $promise = Create::promiseFor($this->iterable->current());
+ $key = $this->iterable->key();
+
+ // Iterable keys may not be unique, so we use a counter to
+ // guarantee uniqueness
+ $idx = $this->nextPendingIndex++;
+
+ $this->pending[$idx] = $promise->then(
+ function ($value) use ($idx, $key): void {
+ if ($this->onFulfilled) {
+ call_user_func(
+ $this->onFulfilled,
+ $value,
+ $key,
+ $this->aggregate
+ );
+ }
+ $this->step($idx);
+ },
+ function ($reason) use ($idx, $key): void {
+ if ($this->onRejected) {
+ call_user_func(
+ $this->onRejected,
+ $reason,
+ $key,
+ $this->aggregate
+ );
+ }
+ $this->step($idx);
+ }
+ );
+
+ return true;
+ }
+
+ private function advanceIterator(): bool
+ {
+ // Place a lock on the iterator so that we ensure to not recurse,
+ // preventing fatal generator errors.
+ if ($this->mutex) {
+ return false;
+ }
+
+ $this->mutex = true;
+
+ try {
+ $this->iterable->next();
+ $this->mutex = false;
+
+ return true;
+ } catch (\Throwable $e) {
+ $this->aggregate->reject($e);
+ $this->mutex = false;
+
+ return false;
+ }
+ }
+
+ private function step(int $idx): void
+ {
+ // If the promise was already resolved, then ignore this step.
+ if (Is::settled($this->aggregate)) {
+ return;
+ }
+
+ unset($this->pending[$idx]);
+
+ // Only refill pending promises if we are not locked, preventing the
+ // EachPromise to recursively invoke the provided iterator, which
+ // cause a fatal error: "Cannot resume an already running generator"
+ if ($this->advanceIterator() && !$this->checkIfFinished()) {
+ // Add more pending promises if possible.
+ $this->refillPending();
+ }
+ }
+
+ private function checkIfFinished(): bool
+ {
+ if (!$this->pending && !$this->iterable->valid()) {
+ // Resolve the promise if there's nothing left to do.
+ $this->aggregate->resolve(null);
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/vendor/guzzlehttp/promises/src/FulfilledPromise.php
new file mode 100644
index 000000000..ab7129659
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/FulfilledPromise.php
@@ -0,0 +1,89 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise that has been fulfilled.
+ *
+ * Thenning off of this promise will invoke the onFulfilled callback
+ * immediately and ignore other callbacks.
+ *
+ * @final
+ */
+class FulfilledPromise implements PromiseInterface
+{
+ private $value;
+
+ /**
+ * @param mixed $value
+ */
+ public function __construct($value)
+ {
+ if (is_object($value) && method_exists($value, 'then')) {
+ throw new \InvalidArgumentException(
+ 'You cannot create a FulfilledPromise with a promise.'
+ );
+ }
+
+ $this->value = $value;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ): PromiseInterface {
+ // Return itself if there is no onFulfilled function.
+ if (!$onFulfilled) {
+ return $this;
+ }
+
+ $queue = Utils::queue();
+ $p = new Promise([$queue, 'run']);
+ $value = $this->value;
+ $queue->add(static function () use ($p, $value, $onFulfilled): void {
+ if (Is::pending($p)) {
+ try {
+ $p->resolve($onFulfilled($value));
+ } catch (\Throwable $e) {
+ $p->reject($e);
+ }
+ }
+ });
+
+ return $p;
+ }
+
+ public function otherwise(callable $onRejected): PromiseInterface
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait(bool $unwrap = true)
+ {
+ return $unwrap ? $this->value : null;
+ }
+
+ public function getState(): string
+ {
+ return self::FULFILLED;
+ }
+
+ public function resolve($value): void
+ {
+ if ($value !== $this->value) {
+ throw new \LogicException('Cannot resolve a fulfilled promise');
+ }
+ }
+
+ public function reject($reason): void
+ {
+ throw new \LogicException('Cannot reject a fulfilled promise');
+ }
+
+ public function cancel(): void
+ {
+ // pass
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/Is.php b/vendor/guzzlehttp/promises/src/Is.php
new file mode 100644
index 000000000..f3f050384
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Is.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+final class Is
+{
+ /**
+ * Returns true if a promise is pending.
+ */
+ public static function pending(PromiseInterface $promise): bool
+ {
+ return $promise->getState() === PromiseInterface::PENDING;
+ }
+
+ /**
+ * Returns true if a promise is fulfilled or rejected.
+ */
+ public static function settled(PromiseInterface $promise): bool
+ {
+ return $promise->getState() !== PromiseInterface::PENDING;
+ }
+
+ /**
+ * Returns true if a promise is fulfilled.
+ */
+ public static function fulfilled(PromiseInterface $promise): bool
+ {
+ return $promise->getState() === PromiseInterface::FULFILLED;
+ }
+
+ /**
+ * Returns true if a promise is rejected.
+ */
+ public static function rejected(PromiseInterface $promise): bool
+ {
+ return $promise->getState() === PromiseInterface::REJECTED;
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/Promise.php b/vendor/guzzlehttp/promises/src/Promise.php
new file mode 100644
index 000000000..1b07bdc9a
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Promise.php
@@ -0,0 +1,281 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * Promises/A+ implementation that avoids recursion when possible.
+ *
+ * @see https://promisesaplus.com/
+ *
+ * @final
+ */
+class Promise implements PromiseInterface
+{
+ private $state = self::PENDING;
+ private $result;
+ private $cancelFn;
+ private $waitFn;
+ private $waitList;
+ private $handlers = [];
+
+ /**
+ * @param callable $waitFn Fn that when invoked resolves the promise.
+ * @param callable $cancelFn Fn that when invoked cancels the promise.
+ */
+ public function __construct(
+ callable $waitFn = null,
+ callable $cancelFn = null
+ ) {
+ $this->waitFn = $waitFn;
+ $this->cancelFn = $cancelFn;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ): PromiseInterface {
+ if ($this->state === self::PENDING) {
+ $p = new Promise(null, [$this, 'cancel']);
+ $this->handlers[] = [$p, $onFulfilled, $onRejected];
+ $p->waitList = $this->waitList;
+ $p->waitList[] = $this;
+
+ return $p;
+ }
+
+ // Return a fulfilled promise and immediately invoke any callbacks.
+ if ($this->state === self::FULFILLED) {
+ $promise = Create::promiseFor($this->result);
+
+ return $onFulfilled ? $promise->then($onFulfilled) : $promise;
+ }
+
+ // It's either cancelled or rejected, so return a rejected promise
+ // and immediately invoke any callbacks.
+ $rejection = Create::rejectionFor($this->result);
+
+ return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
+ }
+
+ public function otherwise(callable $onRejected): PromiseInterface
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait(bool $unwrap = true)
+ {
+ $this->waitIfPending();
+
+ if ($this->result instanceof PromiseInterface) {
+ return $this->result->wait($unwrap);
+ }
+ if ($unwrap) {
+ if ($this->state === self::FULFILLED) {
+ return $this->result;
+ }
+ // It's rejected so "unwrap" and throw an exception.
+ throw Create::exceptionFor($this->result);
+ }
+ }
+
+ public function getState(): string
+ {
+ return $this->state;
+ }
+
+ public function cancel(): void
+ {
+ if ($this->state !== self::PENDING) {
+ return;
+ }
+
+ $this->waitFn = $this->waitList = null;
+
+ if ($this->cancelFn) {
+ $fn = $this->cancelFn;
+ $this->cancelFn = null;
+ try {
+ $fn();
+ } catch (\Throwable $e) {
+ $this->reject($e);
+ }
+ }
+
+ // Reject the promise only if it wasn't rejected in a then callback.
+ /** @psalm-suppress RedundantCondition */
+ if ($this->state === self::PENDING) {
+ $this->reject(new CancellationException('Promise has been cancelled'));
+ }
+ }
+
+ public function resolve($value): void
+ {
+ $this->settle(self::FULFILLED, $value);
+ }
+
+ public function reject($reason): void
+ {
+ $this->settle(self::REJECTED, $reason);
+ }
+
+ private function settle(string $state, $value): void
+ {
+ if ($this->state !== self::PENDING) {
+ // Ignore calls with the same resolution.
+ if ($state === $this->state && $value === $this->result) {
+ return;
+ }
+ throw $this->state === $state
+ ? new \LogicException("The promise is already {$state}.")
+ : new \LogicException("Cannot change a {$this->state} promise to {$state}");
+ }
+
+ if ($value === $this) {
+ throw new \LogicException('Cannot fulfill or reject a promise with itself');
+ }
+
+ // Clear out the state of the promise but stash the handlers.
+ $this->state = $state;
+ $this->result = $value;
+ $handlers = $this->handlers;
+ $this->handlers = null;
+ $this->waitList = $this->waitFn = null;
+ $this->cancelFn = null;
+
+ if (!$handlers) {
+ return;
+ }
+
+ // If the value was not a settled promise or a thenable, then resolve
+ // it in the task queue using the correct ID.
+ if (!is_object($value) || !method_exists($value, 'then')) {
+ $id = $state === self::FULFILLED ? 1 : 2;
+ // It's a success, so resolve the handlers in the queue.
+ Utils::queue()->add(static function () use ($id, $value, $handlers): void {
+ foreach ($handlers as $handler) {
+ self::callHandler($id, $value, $handler);
+ }
+ });
+ } elseif ($value instanceof Promise && Is::pending($value)) {
+ // We can just merge our handlers onto the next promise.
+ $value->handlers = array_merge($value->handlers, $handlers);
+ } else {
+ // Resolve the handlers when the forwarded promise is resolved.
+ $value->then(
+ static function ($value) use ($handlers): void {
+ foreach ($handlers as $handler) {
+ self::callHandler(1, $value, $handler);
+ }
+ },
+ static function ($reason) use ($handlers): void {
+ foreach ($handlers as $handler) {
+ self::callHandler(2, $reason, $handler);
+ }
+ }
+ );
+ }
+ }
+
+ /**
+ * Call a stack of handlers using a specific callback index and value.
+ *
+ * @param int $index 1 (resolve) or 2 (reject).
+ * @param mixed $value Value to pass to the callback.
+ * @param array $handler Array of handler data (promise and callbacks).
+ */
+ private static function callHandler(int $index, $value, array $handler): void
+ {
+ /** @var PromiseInterface $promise */
+ $promise = $handler[0];
+
+ // The promise may have been cancelled or resolved before placing
+ // this thunk in the queue.
+ if (Is::settled($promise)) {
+ return;
+ }
+
+ try {
+ if (isset($handler[$index])) {
+ /*
+ * If $f throws an exception, then $handler will be in the exception
+ * stack trace. Since $handler contains a reference to the callable
+ * itself we get a circular reference. We clear the $handler
+ * here to avoid that memory leak.
+ */
+ $f = $handler[$index];
+ unset($handler);
+ $promise->resolve($f($value));
+ } elseif ($index === 1) {
+ // Forward resolution values as-is.
+ $promise->resolve($value);
+ } else {
+ // Forward rejections down the chain.
+ $promise->reject($value);
+ }
+ } catch (\Throwable $reason) {
+ $promise->reject($reason);
+ }
+ }
+
+ private function waitIfPending(): void
+ {
+ if ($this->state !== self::PENDING) {
+ return;
+ } elseif ($this->waitFn) {
+ $this->invokeWaitFn();
+ } elseif ($this->waitList) {
+ $this->invokeWaitList();
+ } else {
+ // If there's no wait function, then reject the promise.
+ $this->reject('Cannot wait on a promise that has '
+ .'no internal wait function. You must provide a wait '
+ .'function when constructing the promise to be able to '
+ .'wait on a promise.');
+ }
+
+ Utils::queue()->run();
+
+ /** @psalm-suppress RedundantCondition */
+ if ($this->state === self::PENDING) {
+ $this->reject('Invoking the wait callback did not resolve the promise');
+ }
+ }
+
+ private function invokeWaitFn(): void
+ {
+ try {
+ $wfn = $this->waitFn;
+ $this->waitFn = null;
+ $wfn(true);
+ } catch (\Throwable $reason) {
+ if ($this->state === self::PENDING) {
+ // The promise has not been resolved yet, so reject the promise
+ // with the exception.
+ $this->reject($reason);
+ } else {
+ // The promise was already resolved, so there's a problem in
+ // the application.
+ throw $reason;
+ }
+ }
+ }
+
+ private function invokeWaitList(): void
+ {
+ $waitList = $this->waitList;
+ $this->waitList = null;
+
+ foreach ($waitList as $result) {
+ do {
+ $result->waitIfPending();
+ $result = $result->result;
+ } while ($result instanceof Promise);
+
+ if ($result instanceof PromiseInterface) {
+ $result->wait(false);
+ }
+ }
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/PromiseInterface.php b/vendor/guzzlehttp/promises/src/PromiseInterface.php
new file mode 100644
index 000000000..2824802bb
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/PromiseInterface.php
@@ -0,0 +1,91 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise represents the eventual result of an asynchronous operation.
+ *
+ * The primary way of interacting with a promise is through its then method,
+ * which registers callbacks to receive either a promise’s eventual value or
+ * the reason why the promise cannot be fulfilled.
+ *
+ * @see https://promisesaplus.com/
+ */
+interface PromiseInterface
+{
+ public const PENDING = 'pending';
+ public const FULFILLED = 'fulfilled';
+ public const REJECTED = 'rejected';
+
+ /**
+ * Appends fulfillment and rejection handlers to the promise, and returns
+ * a new promise resolving to the return value of the called handler.
+ *
+ * @param callable $onFulfilled Invoked when the promise fulfills.
+ * @param callable $onRejected Invoked when the promise is rejected.
+ */
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ): PromiseInterface;
+
+ /**
+ * Appends a rejection handler callback to the promise, and returns a new
+ * promise resolving to the return value of the callback if it is called,
+ * or to its original fulfillment value if the promise is instead
+ * fulfilled.
+ *
+ * @param callable $onRejected Invoked when the promise is rejected.
+ */
+ public function otherwise(callable $onRejected): PromiseInterface;
+
+ /**
+ * Get the state of the promise ("pending", "rejected", or "fulfilled").
+ *
+ * The three states can be checked against the constants defined on
+ * PromiseInterface: PENDING, FULFILLED, and REJECTED.
+ */
+ public function getState(): string;
+
+ /**
+ * Resolve the promise with the given value.
+ *
+ * @param mixed $value
+ *
+ * @throws \RuntimeException if the promise is already resolved.
+ */
+ public function resolve($value): void;
+
+ /**
+ * Reject the promise with the given reason.
+ *
+ * @param mixed $reason
+ *
+ * @throws \RuntimeException if the promise is already resolved.
+ */
+ public function reject($reason): void;
+
+ /**
+ * Cancels the promise if possible.
+ *
+ * @see https://github.com/promises-aplus/cancellation-spec/issues/7
+ */
+ public function cancel(): void;
+
+ /**
+ * Waits until the promise completes if possible.
+ *
+ * Pass $unwrap as true to unwrap the result of the promise, either
+ * returning the resolved value or throwing the rejected exception.
+ *
+ * If the promise cannot be waited on, then the promise will be rejected.
+ *
+ * @return mixed
+ *
+ * @throws \LogicException if the promise has no wait function or if the
+ * promise does not settle after waiting.
+ */
+ public function wait(bool $unwrap = true);
+}
diff --git a/vendor/guzzlehttp/promises/src/PromisorInterface.php b/vendor/guzzlehttp/promises/src/PromisorInterface.php
new file mode 100644
index 000000000..5e8c51264
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/PromisorInterface.php
@@ -0,0 +1,16 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * Interface used with classes that return a promise.
+ */
+interface PromisorInterface
+{
+ /**
+ * Returns a promise.
+ */
+ public function promise(): PromiseInterface;
+}
diff --git a/vendor/guzzlehttp/promises/src/RejectedPromise.php b/vendor/guzzlehttp/promises/src/RejectedPromise.php
new file mode 100644
index 000000000..d947da1f5
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/RejectedPromise.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise that has been rejected.
+ *
+ * Thenning off of this promise will invoke the onRejected callback
+ * immediately and ignore other callbacks.
+ *
+ * @final
+ */
+class RejectedPromise implements PromiseInterface
+{
+ private $reason;
+
+ /**
+ * @param mixed $reason
+ */
+ public function __construct($reason)
+ {
+ if (is_object($reason) && method_exists($reason, 'then')) {
+ throw new \InvalidArgumentException(
+ 'You cannot create a RejectedPromise with a promise.'
+ );
+ }
+
+ $this->reason = $reason;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ): PromiseInterface {
+ // If there's no onRejected callback then just return self.
+ if (!$onRejected) {
+ return $this;
+ }
+
+ $queue = Utils::queue();
+ $reason = $this->reason;
+ $p = new Promise([$queue, 'run']);
+ $queue->add(static function () use ($p, $reason, $onRejected): void {
+ if (Is::pending($p)) {
+ try {
+ // Return a resolved promise if onRejected does not throw.
+ $p->resolve($onRejected($reason));
+ } catch (\Throwable $e) {
+ // onRejected threw, so return a rejected promise.
+ $p->reject($e);
+ }
+ }
+ });
+
+ return $p;
+ }
+
+ public function otherwise(callable $onRejected): PromiseInterface
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait(bool $unwrap = true)
+ {
+ if ($unwrap) {
+ throw Create::exceptionFor($this->reason);
+ }
+
+ return null;
+ }
+
+ public function getState(): string
+ {
+ return self::REJECTED;
+ }
+
+ public function resolve($value): void
+ {
+ throw new \LogicException('Cannot resolve a rejected promise');
+ }
+
+ public function reject($reason): void
+ {
+ if ($reason !== $this->reason) {
+ throw new \LogicException('Cannot reject a rejected promise');
+ }
+ }
+
+ public function cancel(): void
+ {
+ // pass
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/RejectionException.php b/vendor/guzzlehttp/promises/src/RejectionException.php
new file mode 100644
index 000000000..47dca8624
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/RejectionException.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * A special exception that is thrown when waiting on a rejected promise.
+ *
+ * The reason value is available via the getReason() method.
+ */
+class RejectionException extends \RuntimeException
+{
+ /** @var mixed Rejection reason. */
+ private $reason;
+
+ /**
+ * @param mixed $reason Rejection reason.
+ * @param string|null $description Optional description.
+ */
+ public function __construct($reason, ?string $description = null)
+ {
+ $this->reason = $reason;
+
+ $message = 'The promise was rejected';
+
+ if ($description) {
+ $message .= ' with reason: '.$description;
+ } elseif (is_string($reason)
+ || (is_object($reason) && method_exists($reason, '__toString'))
+ ) {
+ $message .= ' with reason: '.$this->reason;
+ } elseif ($reason instanceof \JsonSerializable) {
+ $message .= ' with reason: '.json_encode($this->reason, JSON_PRETTY_PRINT);
+ }
+
+ parent::__construct($message);
+ }
+
+ /**
+ * Returns the rejection reason.
+ *
+ * @return mixed
+ */
+ public function getReason()
+ {
+ return $this->reason;
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/TaskQueue.php b/vendor/guzzlehttp/promises/src/TaskQueue.php
new file mode 100644
index 000000000..503e0b2da
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/TaskQueue.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+/**
+ * A task queue that executes tasks in a FIFO order.
+ *
+ * This task queue class is used to settle promises asynchronously and
+ * maintains a constant stack size. You can use the task queue asynchronously
+ * by calling the `run()` function of the global task queue in an event loop.
+ *
+ * GuzzleHttp\Promise\Utils::queue()->run();
+ *
+ * @final
+ */
+class TaskQueue implements TaskQueueInterface
+{
+ private $enableShutdown = true;
+ private $queue = [];
+
+ public function __construct(bool $withShutdown = true)
+ {
+ if ($withShutdown) {
+ register_shutdown_function(function (): void {
+ if ($this->enableShutdown) {
+ // Only run the tasks if an E_ERROR didn't occur.
+ $err = error_get_last();
+ if (!$err || ($err['type'] ^ E_ERROR)) {
+ $this->run();
+ }
+ }
+ });
+ }
+ }
+
+ public function isEmpty(): bool
+ {
+ return !$this->queue;
+ }
+
+ public function add(callable $task): void
+ {
+ $this->queue[] = $task;
+ }
+
+ public function run(): void
+ {
+ while ($task = array_shift($this->queue)) {
+ /** @var callable $task */
+ $task();
+ }
+ }
+
+ /**
+ * The task queue will be run and exhausted by default when the process
+ * exits IFF the exit is not the result of a PHP E_ERROR error.
+ *
+ * You can disable running the automatic shutdown of the queue by calling
+ * this function. If you disable the task queue shutdown process, then you
+ * MUST either run the task queue (as a result of running your event loop
+ * or manually using the run() method) or wait on each outstanding promise.
+ *
+ * Note: This shutdown will occur before any destructors are triggered.
+ */
+ public function disableShutdown(): void
+ {
+ $this->enableShutdown = false;
+ }
+}
diff --git a/vendor/guzzlehttp/promises/src/TaskQueueInterface.php b/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
new file mode 100644
index 000000000..34c561a48
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+interface TaskQueueInterface
+{
+ /**
+ * Returns true if the queue is empty.
+ */
+ public function isEmpty(): bool;
+
+ /**
+ * Adds a task to the queue that will be executed the next time run is
+ * called.
+ */
+ public function add(callable $task): void;
+
+ /**
+ * Execute all of the pending task in the queue.
+ */
+ public function run(): void;
+}
diff --git a/vendor/guzzlehttp/promises/src/Utils.php b/vendor/guzzlehttp/promises/src/Utils.php
new file mode 100644
index 000000000..e1570d727
--- /dev/null
+++ b/vendor/guzzlehttp/promises/src/Utils.php
@@ -0,0 +1,259 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Promise;
+
+final class Utils
+{
+ /**
+ * Get the global task queue used for promise resolution.
+ *
+ * This task queue MUST be run in an event loop in order for promises to be
+ * settled asynchronously. It will be automatically run when synchronously
+ * waiting on a promise.
+ *
+ * <code>
+ * while ($eventLoop->isRunning()) {
+ * GuzzleHttp\Promise\Utils::queue()->run();
+ * }
+ * </code>
+ *
+ * @param TaskQueueInterface|null $assign Optionally specify a new queue instance.
+ */
+ public static function queue(TaskQueueInterface $assign = null): TaskQueueInterface
+ {
+ static $queue;
+
+ if ($assign) {
+ $queue = $assign;
+ } elseif (!$queue) {
+ $queue = new TaskQueue();
+ }
+
+ return $queue;
+ }
+
+ /**
+ * Adds a function to run in the task queue when it is next `run()` and
+ * returns a promise that is fulfilled or rejected with the result.
+ *
+ * @param callable $task Task function to run.
+ */
+ public static function task(callable $task): PromiseInterface
+ {
+ $queue = self::queue();
+ $promise = new Promise([$queue, 'run']);
+ $queue->add(function () use ($task, $promise): void {
+ try {
+ if (Is::pending($promise)) {
+ $promise->resolve($task());
+ }
+ } catch (\Throwable $e) {
+ $promise->reject($e);
+ }
+ });
+
+ return $promise;
+ }
+
+ /**
+ * Synchronously waits on a promise to resolve and returns an inspection
+ * state array.
+ *
+ * Returns a state associative array containing a "state" key mapping to a
+ * valid promise state. If the state of the promise is "fulfilled", the
+ * array will contain a "value" key mapping to the fulfilled value of the
+ * promise. If the promise is rejected, the array will contain a "reason"
+ * key mapping to the rejection reason of the promise.
+ *
+ * @param PromiseInterface $promise Promise or value.
+ */
+ public static function inspect(PromiseInterface $promise): array
+ {
+ try {
+ return [
+ 'state' => PromiseInterface::FULFILLED,
+ 'value' => $promise->wait(),
+ ];
+ } catch (RejectionException $e) {
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
+ } catch (\Throwable $e) {
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
+ }
+ }
+
+ /**
+ * Waits on all of the provided promises, but does not unwrap rejected
+ * promises as thrown exception.
+ *
+ * Returns an array of inspection state arrays.
+ *
+ * @see inspect for the inspection state array format.
+ *
+ * @param PromiseInterface[] $promises Traversable of promises to wait upon.
+ */
+ public static function inspectAll($promises): array
+ {
+ $results = [];
+ foreach ($promises as $key => $promise) {
+ $results[$key] = self::inspect($promise);
+ }
+
+ return $results;
+ }
+
+ /**
+ * Waits on all of the provided promises and returns the fulfilled values.
+ *
+ * Returns an array that contains the value of each promise (in the same
+ * order the promises were provided). An exception is thrown if any of the
+ * promises are rejected.
+ *
+ * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
+ *
+ * @throws \Throwable on error
+ */
+ public static function unwrap($promises): array
+ {
+ $results = [];
+ foreach ($promises as $key => $promise) {
+ $results[$key] = $promise->wait();
+ }
+
+ return $results;
+ }
+
+ /**
+ * Given an array of promises, return a promise that is fulfilled when all
+ * the items in the array are fulfilled.
+ *
+ * The promise's fulfillment value is an array with fulfillment values at
+ * respective positions to the original array. If any promise in the array
+ * rejects, the returned promise is rejected with the rejection reason.
+ *
+ * @param mixed $promises Promises or values.
+ * @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
+ */
+ public static function all($promises, bool $recursive = false): PromiseInterface
+ {
+ $results = [];
+ $promise = Each::of(
+ $promises,
+ function ($value, $idx) use (&$results): void {
+ $results[$idx] = $value;
+ },
+ function ($reason, $idx, Promise $aggregate): void {
+ $aggregate->reject($reason);
+ }
+ )->then(function () use (&$results) {
+ ksort($results);
+
+ return $results;
+ });
+
+ if (true === $recursive) {
+ $promise = $promise->then(function ($results) use ($recursive, &$promises) {
+ foreach ($promises as $promise) {
+ if (Is::pending($promise)) {
+ return self::all($promises, $recursive);
+ }
+ }
+
+ return $results;
+ });
+ }
+
+ return $promise;
+ }
+
+ /**
+ * Initiate a competitive race between multiple promises or values (values
+ * will become immediately fulfilled promises).
+ *
+ * When count amount of promises have been fulfilled, the returned promise
+ * is fulfilled with an array that contains the fulfillment values of the
+ * winners in order of resolution.
+ *
+ * This promise is rejected with a {@see AggregateException} if the number
+ * of fulfilled promises is less than the desired $count.
+ *
+ * @param int $count Total number of promises.
+ * @param mixed $promises Promises or values.
+ */
+ public static function some(int $count, $promises): PromiseInterface
+ {
+ $results = [];
+ $rejections = [];
+
+ return Each::of(
+ $promises,
+ function ($value, $idx, PromiseInterface $p) use (&$results, $count): void {
+ if (Is::settled($p)) {
+ return;
+ }
+ $results[$idx] = $value;
+ if (count($results) >= $count) {
+ $p->resolve(null);
+ }
+ },
+ function ($reason) use (&$rejections): void {
+ $rejections[] = $reason;
+ }
+ )->then(
+ function () use (&$results, &$rejections, $count) {
+ if (count($results) !== $count) {
+ throw new AggregateException(
+ 'Not enough promises to fulfill count',
+ $rejections
+ );
+ }
+ ksort($results);
+
+ return array_values($results);
+ }
+ );
+ }
+
+ /**
+ * Like some(), with 1 as count. However, if the promise fulfills, the
+ * fulfillment value is not an array of 1 but the value directly.
+ *
+ * @param mixed $promises Promises or values.
+ */
+ public static function any($promises): PromiseInterface
+ {
+ return self::some(1, $promises)->then(function ($values) {
+ return $values[0];
+ });
+ }
+
+ /**
+ * Returns a promise that is fulfilled when all of the provided promises have
+ * been fulfilled or rejected.
+ *
+ * The returned promise is fulfilled with an array of inspection state arrays.
+ *
+ * @see inspect for the inspection state array format.
+ *
+ * @param mixed $promises Promises or values.
+ */
+ public static function settle($promises): PromiseInterface
+ {
+ $results = [];
+
+ return Each::of(
+ $promises,
+ function ($value, $idx) use (&$results): void {
+ $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
+ },
+ function ($reason, $idx) use (&$results): void {
+ $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
+ }
+ )->then(function () use (&$results) {
+ ksort($results);
+
+ return $results;
+ });
+ }
+}