mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-10-29 12:43:25 +00:00
[Web] Update composer libraries
This commit is contained in:
1297 files changed
+71585
-24460
No files matched your search
@@ -0,0 +1,62 @@
|
||||
# Agent Guidelines
|
||||
|
||||
## Code and tooling
|
||||
|
||||
- All code must remain compatible with PHP 7.4. Run PHPStan and PHP-CS-Fixer
|
||||
only under a PHP 7.4.x runtime; never run either tool under any other PHP
|
||||
major.minor version.
|
||||
- Use fully qualified `#[\SensitiveParameter]` on concrete executable
|
||||
parameters when their established role normally carries a secret,
|
||||
credential-bearing aggregate, or confidential Guzzle-owned container, and
|
||||
the active frame can throw or invoke throwing code.
|
||||
- Repeat the attribute on every qualifying owned caller, callee, concrete trait
|
||||
method, and closure parameter. Do not add it to interfaces, abstract-only
|
||||
declarations, pure/no-realistic-throw helpers, assignment-only sites,
|
||||
arbitrary generic payloads, or completed non-recoverable derivatives.
|
||||
- For PHP 7.4 compatibility, put `#[\SensitiveParameter]` on its own line and
|
||||
the parameter on the following line, expand the complete parameter list, and
|
||||
never add a comma after the final parameter. Native trace redaction starts on
|
||||
PHP 8.2 and does not redact logs, messages, properties, wire traffic, captured
|
||||
variables, return values, or the separate backtrace `$this`/`object`.
|
||||
- Always pass an explicit character list to `trim()`, `ltrim()`, and `rtrim()`;
|
||||
never rely on the default characters.
|
||||
- Handle `preg_*` engine failures: when the result is used as data, test for
|
||||
`false` or `null` and throw a `\RuntimeException` including
|
||||
`preg_last_error_msg()`; boolean validation guards must compare strictly, such
|
||||
as `=== 1`, so an engine failure can only ever fail closed. Diagnostic
|
||||
escaping is the narrow exception: use a deterministic bytewise fallback rather
|
||||
than throwing, so it cannot obscure the original exception.
|
||||
- Anchor validation patterns to the true end of input with the `D` modifier or
|
||||
`\z`; a bare `$` accepts a trailing newline.
|
||||
- Never embed raw control bytes in exception messages and other diagnostics;
|
||||
escape or redact the offending value first.
|
||||
- Helper classes that expose only public static methods are `final` and have a
|
||||
private constructor.
|
||||
- Resist native PHP serialization when a class holds live state (streams,
|
||||
resources, handles, callbacks, credentials); plain data holders remain
|
||||
serializable.
|
||||
- To resist, `__serialize()` and `__unserialize()` both throw
|
||||
`\LogicException(static::class.' should never be serialized')` and its
|
||||
unserialized counterpart, via the `@internal` `NonSerializableTrait`.
|
||||
- In general, numeric inputs should not accept non-finite floats. In situations
|
||||
where they are accepted and we need to cast to a string, we should branch on
|
||||
`\is_finite($value)`, using `(string) $value` for the finite case and
|
||||
`\is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF')` otherwise.
|
||||
- Never call `strtolower()`, `strtoupper()`, `strcasecmp()`, `stripos()`, or
|
||||
other locale-sensitive case functions; fold ASCII case with
|
||||
`strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')`
|
||||
and its inverse instead.
|
||||
- Changes in behavior need a `CHANGELOG.md` entry in the unreleased section of
|
||||
the target branch and an `UPGRADING.md` note when the behavior differs between
|
||||
major versions.
|
||||
|
||||
## Documentation
|
||||
|
||||
- Wrap markdown prose and PHPDoc text to 80 columns using greedy wrapping. Never
|
||||
split a markdown link or an inline code span across a line break; a line that
|
||||
cannot be broken may exceed the limit. Avoid em dashes.
|
||||
- Keep PHPDoc and the corresponding `docs/` pages in sync: shared prose is
|
||||
deliberately word-for-word identical, including boilerplate copied verbatim
|
||||
between related functions, so apply the same edit to every copy. Only
|
||||
formatting and linking may differ, such as a docs link becoming a PHPDoc
|
||||
`@see` tag; the wording must never drift.
|
||||
@@ -1,11 +1,176 @@
|
||||
# CHANGELOG
|
||||
|
||||
|
||||
## 3.1.0 - 2026-10-18
|
||||
|
||||
### Added
|
||||
|
||||
- Added `State` promise state checks as a replacement for `Is`
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecated `Is` because PHP 8.6 deprecates `is` as a class name
|
||||
|
||||
|
||||
## 3.0.2 - 2026-08-24
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for PHP 8.6
|
||||
|
||||
|
||||
## 3.0.1 - 2026-08-05
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed the default `TReason` of `FulfilledPromise` and `Create::promiseFor()` to `never`
|
||||
- Changed the default `TValue` of `RejectedPromise` and `Create::rejectionFor()` to `never`
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `EachPromise` abandoning its aggregate when the pending window drains unsettled
|
||||
- Fixed `EachPromise` admitting new work after its aggregate has settled
|
||||
|
||||
|
||||
## 3.0.0 - 2026-07-20
|
||||
|
||||
### Added
|
||||
|
||||
- Added `concurrency` config support to `Utils::all()` and `Each::of()`
|
||||
- Added generic PHPDoc annotations to promise APIs and collection callbacks
|
||||
- Added recursive and `concurrency` config support to `Utils::settle()`
|
||||
- Allowed promises to be resolved without passing a value
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed `Utils::inspect()` to return actual rejection reasons
|
||||
- Changed `Utils::inspect()` to prefer the settled state over late wait function exceptions
|
||||
- Changed late rejection callbacks to follow rejected promises
|
||||
- Reject native PHP serialization of in-flight runtime objects
|
||||
- Made static helper classes non-instantiable
|
||||
- Require iterable inputs for promise collection helpers and `EachPromise`
|
||||
- Iterate `IteratorAggregate` inputs to collection helpers instead of treating them as a single value
|
||||
- Improved recursive `Utils::all()` handling of dynamically-added settled values and raw values
|
||||
|
||||
### Removed
|
||||
|
||||
- Dropped support for PHP 7.2 and 7.3
|
||||
|
||||
|
||||
## 2.5.1 - 2026-07-08
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed recursive `Utils::all()` rejecting generator inputs
|
||||
|
||||
|
||||
## 2.5.0 - 2026-06-02
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecated passing non-iterable inputs to promise collection helpers and `EachPromise`
|
||||
|
||||
|
||||
## 2.4.1 - 2026-05-20
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed cancelling settled coroutines when no current promise remains
|
||||
|
||||
|
||||
## 2.4.0 - 2026-05-20
|
||||
|
||||
### Changed
|
||||
|
||||
- Empty `EachPromise` instances now resolve when the task queue runs without `wait()`
|
||||
|
||||
|
||||
## 2.3.1 - 2026-05-19
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `Utils::inspect()` returning the internal reason array instead of the `AggregateException`
|
||||
|
||||
|
||||
## 2.3.0 - 2025-08-22
|
||||
|
||||
### Added
|
||||
|
||||
- PHP 8.5 support
|
||||
|
||||
|
||||
## 2.2.0 - 2025-03-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Revert "Allow an empty EachPromise to be resolved by running the queue"
|
||||
|
||||
|
||||
## 2.1.0 - 2025-03-27
|
||||
|
||||
### Added
|
||||
|
||||
- Allow an empty EachPromise to be resolved by running the queue
|
||||
|
||||
|
||||
## 2.0.4 - 2024-10-17
|
||||
|
||||
### Fixed
|
||||
|
||||
- Once settled, don't allow further rejection of additional promises
|
||||
|
||||
|
||||
## 2.0.3 - 2024-07-18
|
||||
|
||||
### Changed
|
||||
|
||||
- PHP 8.4 support
|
||||
|
||||
|
||||
## 2.0.2 - 2023-12-03
|
||||
|
||||
### Changed
|
||||
|
||||
- Replaced `call_user_func*` with native calls
|
||||
|
||||
|
||||
## 2.0.1 - 2023-08-03
|
||||
|
||||
### Changed
|
||||
|
||||
- PHP 8.3 support
|
||||
|
||||
|
||||
## 2.0.0 - 2023-05-21
|
||||
|
||||
### Added
|
||||
|
||||
- Added PHP 7 type hints
|
||||
|
||||
### Changed
|
||||
|
||||
- All previously non-final non-exception classes have been marked as soft-final
|
||||
|
||||
### Removed
|
||||
|
||||
- Dropped PHP < 7.2 support
|
||||
- All functions in the `GuzzleHttp\Promise` namespace
|
||||
|
||||
|
||||
## 1.5.3 - 2023-05-21
|
||||
|
||||
### Changed
|
||||
|
||||
- Removed remaining usage of deprecated functions
|
||||
|
||||
|
||||
## 1.5.2 - 2022-08-07
|
||||
|
||||
### Changed
|
||||
|
||||
- Officially support PHP 8.2
|
||||
|
||||
|
||||
## 1.5.1 - 2021-10-22
|
||||
|
||||
### Fixed
|
||||
@@ -13,6 +178,7 @@
|
||||
- Revert "Call handler when waiting on fulfilled/rejected Promise"
|
||||
- Fix pool memory leak when empty array of promises provided
|
||||
|
||||
|
||||
## 1.5.0 - 2021-10-07
|
||||
|
||||
### Changed
|
||||
@@ -24,12 +190,14 @@
|
||||
|
||||
- Fix manually settle promises generated with `Utils::task`
|
||||
|
||||
|
||||
## 1.4.1 - 2021-02-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `each_limit` skipping promises and failing
|
||||
|
||||
|
||||
## 1.4.0 - 2020-09-30
|
||||
|
||||
### Added
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
@AGENTS.md
|
||||
+56
-502
@@ -1,546 +1,100 @@
|
||||
# Guzzle Promises
|
||||
|
||||
[Promises/A+](https://promisesaplus.com/) implementation that handles promise
|
||||
chaining and resolution iteratively, allowing for "infinite" promise chaining
|
||||
while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
|
||||
for a general introduction to promises.
|
||||
`guzzlehttp/promises` is a small promise library used by Guzzle for asynchronous
|
||||
operations. It implements promise chaining, synchronous waiting, cancellation,
|
||||
and helpers for working with groups of promises.
|
||||
|
||||
- [Features](#features)
|
||||
- [Quick start](#quick-start)
|
||||
- [Synchronous wait](#synchronous-wait)
|
||||
- [Cancellation](#cancellation)
|
||||
- [API](#api)
|
||||
- [Promise](#promise)
|
||||
- [FulfilledPromise](#fulfilledpromise)
|
||||
- [RejectedPromise](#rejectedpromise)
|
||||
- [Promise interop](#promise-interop)
|
||||
- [Implementation notes](#implementation-notes)
|
||||
Most application developers use this package through
|
||||
[`guzzlehttp/guzzle`](https://github.com/guzzle/guzzle/blob/8.0/README.md) by
|
||||
calling methods such as `requestAsync()`. Install this package directly when you
|
||||
need promise composition without the full HTTP client.
|
||||
|
||||
## Installation
|
||||
|
||||
## Features
|
||||
```bash
|
||||
composer require guzzlehttp/promises
|
||||
```
|
||||
|
||||
- [Promises/A+](https://promisesaplus.com/) implementation.
|
||||
- Promise resolution and chaining is handled iteratively, allowing for
|
||||
"infinite" promise chaining.
|
||||
- Promises have a synchronous `wait` method.
|
||||
- Promises can be cancelled.
|
||||
- Works with any object that has a `then` function.
|
||||
- C# style async/await coroutine promises using
|
||||
`GuzzleHttp\Promise\Coroutine::of()`.
|
||||
## Version Guidance
|
||||
|
||||
| Version | Status | PHP Version |
|
||||
|---------|--------------|--------------|
|
||||
| 3.1 | Latest | >=7.4,<8.7 |
|
||||
| 2.6 | Maintenance | >=7.2.5,<8.7 |
|
||||
| 1.5 | End of Life | >=5.5,<8.3 |
|
||||
|
||||
## Quick Start
|
||||
|
||||
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.
|
||||
|
||||
### Callbacks
|
||||
|
||||
Callbacks are registered with the `then` method by providing an optional
|
||||
`$onFulfilled` followed by an optional `$onRejected` function.
|
||||
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new Promise();
|
||||
|
||||
$promise->then(
|
||||
// $onFulfilled
|
||||
function ($value) {
|
||||
echo 'The promise was fulfilled.';
|
||||
echo 'Fulfilled: ' . $value;
|
||||
},
|
||||
// $onRejected
|
||||
function ($reason) {
|
||||
echo 'The promise was rejected.';
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
*Resolving* a promise means that you either fulfill a promise with a *value* or
|
||||
reject a promise with a *reason*. Resolving a promise triggers callbacks
|
||||
registered with the promise's `then` method. These callbacks are triggered
|
||||
only once and in the order in which they were added.
|
||||
|
||||
### Resolving a Promise
|
||||
|
||||
Promises are fulfilled using the `resolve($value)` method. Resolving a promise
|
||||
with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
|
||||
all of the onFulfilled callbacks (resolving a promise with a rejected promise
|
||||
will reject the promise and trigger the `$onRejected` callbacks).
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise
|
||||
->then(function ($value) {
|
||||
// Return a value and don't break the chain
|
||||
return "Hello, " . $value;
|
||||
})
|
||||
// This then is executed after the first then and receives the value
|
||||
// returned from the first then.
|
||||
->then(function ($value) {
|
||||
echo $value;
|
||||
});
|
||||
|
||||
// Resolving the promise triggers the $onFulfilled callbacks and outputs
|
||||
// "Hello, reader."
|
||||
$promise->resolve('reader.');
|
||||
```
|
||||
|
||||
### Promise Forwarding
|
||||
|
||||
Promises can be chained one after the other. Each then in the chain is a new
|
||||
promise. The return value of a promise is what's forwarded to the next
|
||||
promise in the chain. Returning a promise in a `then` callback will cause the
|
||||
subsequent promises in the chain to only be fulfilled when the returned promise
|
||||
has been fulfilled. The next promise in the chain will be invoked with the
|
||||
resolved value of the promise.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise();
|
||||
$nextPromise = new Promise();
|
||||
|
||||
$promise
|
||||
->then(function ($value) use ($nextPromise) {
|
||||
echo $value;
|
||||
return $nextPromise;
|
||||
})
|
||||
->then(function ($value) {
|
||||
echo $value;
|
||||
});
|
||||
|
||||
// Triggers the first callback and outputs "A"
|
||||
$promise->resolve('A');
|
||||
// Triggers the second callback and outputs "B"
|
||||
$nextPromise->resolve('B');
|
||||
```
|
||||
|
||||
### Promise Rejection
|
||||
|
||||
When a promise is rejected, the `$onRejected` callbacks are invoked with the
|
||||
rejection reason.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise->then(null, function ($reason) {
|
||||
echo $reason;
|
||||
});
|
||||
|
||||
$promise->reject('Error!');
|
||||
// Outputs "Error!"
|
||||
```
|
||||
|
||||
### Rejection Forwarding
|
||||
|
||||
If an exception is thrown in an `$onRejected` callback, subsequent
|
||||
`$onRejected` callbacks are invoked with the thrown exception as the reason.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise->then(null, function ($reason) {
|
||||
throw new Exception($reason);
|
||||
})->then(null, function ($reason) {
|
||||
assert($reason->getMessage() === 'Error!');
|
||||
});
|
||||
|
||||
$promise->reject('Error!');
|
||||
```
|
||||
|
||||
You can also forward a rejection down the promise chain by returning a
|
||||
`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
|
||||
`$onRejected` callback.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise->then(null, function ($reason) {
|
||||
return new RejectedPromise($reason);
|
||||
})->then(null, function ($reason) {
|
||||
assert($reason === 'Error!');
|
||||
});
|
||||
|
||||
$promise->reject('Error!');
|
||||
```
|
||||
|
||||
If an exception is not thrown in a `$onRejected` callback and the callback
|
||||
does not return a rejected promise, downstream `$onFulfilled` callbacks are
|
||||
invoked using the value returned from the `$onRejected` callback.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise
|
||||
->then(null, function ($reason) {
|
||||
return "It's ok";
|
||||
})
|
||||
->then(function ($value) {
|
||||
assert($value === "It's ok");
|
||||
});
|
||||
|
||||
$promise->reject('Error!');
|
||||
```
|
||||
|
||||
|
||||
## Synchronous Wait
|
||||
|
||||
You can synchronously force promises to complete using a promise's `wait`
|
||||
method. When creating a promise, you can provide a wait function that is used
|
||||
to synchronously force a promise to complete. When a wait function is invoked
|
||||
it is expected to deliver a value to the promise or reject the promise. If the
|
||||
wait function does not deliver a value, then an exception is thrown. The wait
|
||||
function provided to a promise constructor is invoked when the `wait` function
|
||||
of the promise is called.
|
||||
|
||||
```php
|
||||
$promise = new Promise(function () use (&$promise) {
|
||||
$promise->resolve('foo');
|
||||
});
|
||||
|
||||
// Calling wait will return the value of the promise.
|
||||
echo $promise->wait(); // outputs "foo"
|
||||
```
|
||||
|
||||
If an exception is encountered while invoking the wait function of a promise,
|
||||
the promise is rejected with the exception and the exception is thrown.
|
||||
|
||||
```php
|
||||
$promise = new Promise(function () use (&$promise) {
|
||||
throw new Exception('foo');
|
||||
});
|
||||
|
||||
$promise->wait(); // throws the exception.
|
||||
```
|
||||
|
||||
Calling `wait` on a promise that has been fulfilled will not trigger the wait
|
||||
function. It will simply return the previously resolved value.
|
||||
|
||||
```php
|
||||
$promise = new Promise(function () { die('this is not called!'); });
|
||||
$promise->resolve('foo');
|
||||
echo $promise->wait(); // outputs "foo"
|
||||
```
|
||||
|
||||
Calling `wait` on a promise that has been rejected will throw an exception. If
|
||||
the rejection reason is an instance of `\Exception` the reason is thrown.
|
||||
Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
|
||||
can be obtained by calling the `getReason` method of the exception.
|
||||
|
||||
```php
|
||||
$promise = new Promise();
|
||||
$promise->reject('foo');
|
||||
$promise->wait();
|
||||
```
|
||||
|
||||
> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
|
||||
|
||||
### Unwrapping a Promise
|
||||
|
||||
When synchronously waiting on a promise, you are joining the state of the
|
||||
promise into the current state of execution (i.e., return the value of the
|
||||
promise if it was fulfilled or throw an exception if it was rejected). This is
|
||||
called "unwrapping" the promise. Waiting on a promise will by default unwrap
|
||||
the promise state.
|
||||
|
||||
You can force a promise to resolve and *not* unwrap the state of the promise
|
||||
by passing `false` to the first argument of the `wait` function:
|
||||
|
||||
```php
|
||||
$promise = new Promise();
|
||||
$promise->reject('foo');
|
||||
// This will not throw an exception. It simply ensures the promise has
|
||||
// been resolved.
|
||||
$promise->wait(false);
|
||||
```
|
||||
|
||||
When unwrapping a promise, the resolved value of the promise will be waited
|
||||
upon until the unwrapped value is not a promise. This means that if you resolve
|
||||
promise A with a promise B and unwrap promise A, the value returned by the
|
||||
wait function will be the value delivered to promise B.
|
||||
|
||||
**Note**: when you do not unwrap the promise, no value is returned.
|
||||
|
||||
|
||||
## Cancellation
|
||||
|
||||
You can cancel a promise that has not yet been fulfilled using the `cancel()`
|
||||
method of a promise. When creating a promise you can provide an optional
|
||||
cancel function that when invoked cancels the action of computing a resolution
|
||||
of the promise.
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### Promise
|
||||
|
||||
When creating a promise object, you can provide an optional `$waitFn` and
|
||||
`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
|
||||
expected to resolve the promise. `$cancelFn` is a function with no arguments
|
||||
that is expected to cancel the computation of a promise. It is invoked when the
|
||||
`cancel()` method of a promise is called.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise(
|
||||
function () use (&$promise) {
|
||||
$promise->resolve('waited');
|
||||
},
|
||||
function () {
|
||||
// do something that will cancel the promise computation (e.g., close
|
||||
// a socket, cancel a database query, etc...)
|
||||
echo 'Rejected: ' . $reason;
|
||||
}
|
||||
);
|
||||
|
||||
assert('waited' === $promise->wait());
|
||||
$promise->resolve('done');
|
||||
Utils::queue()->run();
|
||||
```
|
||||
|
||||
A promise has the following methods:
|
||||
|
||||
- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
|
||||
|
||||
Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.
|
||||
|
||||
- `otherwise(callable $onRejected) : 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.
|
||||
|
||||
- `wait($unwrap = true) : mixed`
|
||||
|
||||
Synchronously waits on the promise to complete.
|
||||
|
||||
`$unwrap` controls whether or not the value of the promise is returned for a
|
||||
fulfilled promise or if an exception is thrown if the promise is rejected.
|
||||
This is set to `true` by default.
|
||||
|
||||
- `cancel()`
|
||||
|
||||
Attempts to cancel the promise if possible. The promise being cancelled and
|
||||
the parent most ancestor that has not yet been resolved will also be
|
||||
cancelled. Any promises waiting on the cancelled promise to resolve will also
|
||||
be cancelled.
|
||||
|
||||
- `getState() : string`
|
||||
|
||||
Returns the state of the promise. One of `pending`, `fulfilled`, or
|
||||
`rejected`.
|
||||
|
||||
- `resolve($value)`
|
||||
|
||||
Fulfills the promise with the given `$value`.
|
||||
|
||||
- `reject($reason)`
|
||||
|
||||
Rejects the promise with the given `$reason`.
|
||||
|
||||
|
||||
### FulfilledPromise
|
||||
|
||||
A fulfilled promise can be created to represent a promise that has been
|
||||
fulfilled.
|
||||
You can wait for a promise to complete synchronously:
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\FulfilledPromise;
|
||||
|
||||
$promise = new FulfilledPromise('value');
|
||||
|
||||
// Fulfilled callbacks are immediately invoked.
|
||||
$promise->then(function ($value) {
|
||||
echo $value;
|
||||
});
|
||||
$value = $promise->wait();
|
||||
```
|
||||
|
||||
A promise's `getState()` describes how it was settled, not the eventual
|
||||
outcome: a promise that was resolved with another promise, directly or by
|
||||
returning one from a `then()` handler, reports `fulfilled` while the inner
|
||||
promise may still be pending, and `wait()` can still throw if the inner
|
||||
promise rejects. Poll the state only on promises settled with plain values,
|
||||
or call `wait()` first (see
|
||||
[guzzle/promises#101](https://github.com/guzzle/promises/issues/101)).
|
||||
|
||||
### RejectedPromise
|
||||
|
||||
A rejected promise can be created to represent a promise that has been
|
||||
rejected.
|
||||
When using Guzzle HTTP requests, asynchronous methods return
|
||||
`GuzzleHttp\Promise\PromiseInterface` instances:
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
|
||||
$promise = new RejectedPromise('Error');
|
||||
|
||||
// Rejected callbacks are immediately invoked.
|
||||
$promise->then(null, function ($reason) {
|
||||
echo $reason;
|
||||
});
|
||||
$promise = $client->requestAsync('GET', 'https://example.com');
|
||||
$response = $promise->wait();
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
## Promise Interoperability
|
||||
|
||||
This library works with foreign promises that have a `then` method. This means
|
||||
you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
|
||||
for example. When a foreign promise is returned inside of a then method
|
||||
callback, promise resolution will occur recursively.
|
||||
|
||||
```php
|
||||
// Create a React promise
|
||||
$deferred = new React\Promise\Deferred();
|
||||
$reactPromise = $deferred->promise();
|
||||
|
||||
// Create a Guzzle promise that is fulfilled with a React promise.
|
||||
$guzzlePromise = new GuzzleHttp\Promise\Promise();
|
||||
$guzzlePromise->then(function ($value) use ($reactPromise) {
|
||||
// Do something something with the value...
|
||||
// Return the React promise
|
||||
return $reactPromise;
|
||||
});
|
||||
```
|
||||
|
||||
Please note that wait and cancel chaining is no longer possible when forwarding
|
||||
a foreign promise. You will need to wrap a third-party promise with a Guzzle
|
||||
promise in order to utilize wait and cancel functions with foreign promises.
|
||||
|
||||
|
||||
### Event Loop Integration
|
||||
|
||||
In order to keep the stack size constant, Guzzle promises are resolved
|
||||
asynchronously using a task queue. When waiting on promises synchronously, the
|
||||
task queue will be automatically run to ensure that the blocking promise and
|
||||
any forwarded promises are resolved. When using promises asynchronously in an
|
||||
event loop, you will need to run the task queue on each tick of the loop. If
|
||||
you do not run the task queue, then promises will not be resolved.
|
||||
|
||||
You can run the task queue using the `run()` method of the global task queue
|
||||
instance.
|
||||
|
||||
```php
|
||||
// Get the global task queue
|
||||
$queue = GuzzleHttp\Promise\Utils::queue();
|
||||
$queue->run();
|
||||
```
|
||||
|
||||
For example, you could use Guzzle promises with React using a periodic timer:
|
||||
|
||||
```php
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
$loop->addPeriodicTimer(0, [$queue, 'run']);
|
||||
```
|
||||
|
||||
*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
|
||||
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Promise Resolution and Chaining is Handled Iteratively
|
||||
|
||||
By shuffling pending handlers from one owner to another, promises are
|
||||
resolved iteratively, allowing for "infinite" then chaining.
|
||||
|
||||
```php
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$parent = new Promise();
|
||||
$p = $parent;
|
||||
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$p = $p->then(function ($v) {
|
||||
// The stack size remains constant (a good thing)
|
||||
echo xdebug_get_stack_depth() . ', ';
|
||||
return $v + 1;
|
||||
});
|
||||
}
|
||||
|
||||
$parent->resolve(0);
|
||||
var_dump($p->wait()); // int(1000)
|
||||
|
||||
```
|
||||
|
||||
When a promise is fulfilled or rejected with a non-promise value, the promise
|
||||
then takes ownership of the handlers of each child promise and delivers values
|
||||
down the chain without using recursion.
|
||||
|
||||
When a promise is resolved with another promise, the original promise transfers
|
||||
all of its pending handlers to the new promise. When the new promise is
|
||||
eventually resolved, all of the pending handlers are delivered the forwarded
|
||||
value.
|
||||
|
||||
### A Promise is the Deferred
|
||||
|
||||
Some promise libraries implement promises using a deferred object to represent
|
||||
a computation and a promise object to represent the delivery of the result of
|
||||
the computation. This is a nice separation of computation and delivery because
|
||||
consumers of the promise cannot modify the value that will be eventually
|
||||
delivered.
|
||||
|
||||
One side effect of being able to implement promise resolution and chaining
|
||||
iteratively is that you need to be able for one promise to reach into the state
|
||||
of another promise to shuffle around ownership of handlers. In order to achieve
|
||||
this without making the handlers of a promise publicly mutable, a promise is
|
||||
also the deferred value, allowing promises of the same parent class to reach
|
||||
into and modify the private properties of promises of the same type. While this
|
||||
does allow consumers of the value to modify the resolution or rejection of the
|
||||
deferred, it is a small price to pay for keeping the stack size constant.
|
||||
|
||||
```php
|
||||
$promise = new Promise();
|
||||
$promise->then(function ($value) { echo $value; });
|
||||
// The promise is the deferred value, so you can deliver a value to it.
|
||||
$promise->resolve('foo');
|
||||
// prints "foo"
|
||||
```
|
||||
|
||||
|
||||
## Upgrading from Function API
|
||||
|
||||
A static API was first introduced in 1.4.0, in order to mitigate problems with
|
||||
functions conflicting between global and local copies of the package. The
|
||||
function API will be removed in 2.0.0. A migration table has been provided here
|
||||
for your convenience:
|
||||
|
||||
| Original Function | Replacement Method |
|
||||
|----------------|----------------|
|
||||
| `queue` | `Utils::queue` |
|
||||
| `task` | `Utils::task` |
|
||||
| `promise_for` | `Create::promiseFor` |
|
||||
| `rejection_for` | `Create::rejectionFor` |
|
||||
| `exception_for` | `Create::exceptionFor` |
|
||||
| `iter_for` | `Create::iterFor` |
|
||||
| `inspect` | `Utils::inspect` |
|
||||
| `inspect_all` | `Utils::inspectAll` |
|
||||
| `unwrap` | `Utils::unwrap` |
|
||||
| `all` | `Utils::all` |
|
||||
| `some` | `Utils::some` |
|
||||
| `any` | `Utils::any` |
|
||||
| `settle` | `Utils::settle` |
|
||||
| `each` | `Each::of` |
|
||||
| `each_limit` | `Each::ofLimit` |
|
||||
| `each_limit_all` | `Each::ofLimitAll` |
|
||||
| `!is_fulfilled` | `Is::pending` |
|
||||
| `is_fulfilled` | `Is::fulfilled` |
|
||||
| `is_rejected` | `Is::rejected` |
|
||||
| `is_settled` | `Is::settled` |
|
||||
| `coroutine` | `Coroutine::of` |
|
||||
|
||||
- [Promise Quick Start](docs/promise-quick-start.md)
|
||||
- [Promise API](docs/promise-api.md)
|
||||
- [Promise Interoperability](docs/promise-interoperability.md)
|
||||
- [Implementation Notes](docs/implementation-notes.md)
|
||||
- [Upgrade Guide](UPGRADING.md)
|
||||
- [Changelog](CHANGELOG.md)
|
||||
|
||||
## Security
|
||||
|
||||
If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/promises/security/policy) for more information.
|
||||
|
||||
If you discover a security vulnerability within this package, please send an
|
||||
email to security@tidelift.com. All security vulnerabilities will be promptly
|
||||
addressed by the security maintainers listed in the
|
||||
[Security Policy](https://github.com/guzzle/promises/security/policy). Please do
|
||||
not disclose security-related issues publicly until a fix has been announced.
|
||||
|
||||
## License
|
||||
|
||||
Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
|
||||
|
||||
Guzzle is made available under the MIT License (MIT). Please see
|
||||
[License File](LICENSE) for more information.
|
||||
|
||||
## For Enterprise
|
||||
|
||||
Available as part of the Tidelift Subscription
|
||||
|
||||
The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-promises?utm_source=packagist-guzzlehttp-promises&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
||||
The maintainers of Guzzle and thousands of other packages are working with
|
||||
Tidelift to deliver commercial support and maintenance for the open source
|
||||
dependencies you use to build your applications. Save time, reduce risk, and
|
||||
improve code health, while paying the maintainers of the exact dependencies you
|
||||
use.
|
||||
[Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-promises?utm_source=packagist-guzzlehttp-promises&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
||||
@@ -0,0 +1,279 @@
|
||||
Guzzle Promises Upgrade Guide
|
||||
=============================
|
||||
|
||||
2.x to 3.0
|
||||
----------
|
||||
|
||||
Guzzle Promises 3.0 is a major release that raises the minimum PHP version,
|
||||
updates promise and collection helper signatures, adds generic PHPDoc types for
|
||||
static analyzers, tightens collection helper inputs, improves recursive
|
||||
collection behavior, and clarifies rejection inspection and late rejection
|
||||
callback behavior.
|
||||
|
||||
#### PHP Version and Dependencies
|
||||
|
||||
Guzzle Promises 3.0 requires PHP `^7.4 || ^8.0`. Guzzle Promises 2.x supported
|
||||
PHP `^7.2.5 || ^8.0`.
|
||||
|
||||
If your application still supports PHP 7.2 or 7.3, continue using Guzzle
|
||||
Promises 2.x until your minimum PHP version is raised.
|
||||
|
||||
Guzzle Promises 3.0 has no runtime package dependencies beyond PHP. It removes
|
||||
the 2.x runtime dependency on `symfony/deprecation-contracts`; require that
|
||||
package directly if your application uses it.
|
||||
|
||||
#### Optional Promise Resolution Values
|
||||
|
||||
`PromiseInterface::resolve()` now accepts an optional value. Calling `resolve()`
|
||||
without an argument fulfills the promise with `null`.
|
||||
|
||||
Custom implementations of `PromiseInterface`, and subclasses that override
|
||||
`resolve()` on `Promise`, `FulfilledPromise`, or `RejectedPromise`, must update
|
||||
their method signature from `resolve($value): void` to
|
||||
`resolve($value = null): void`.
|
||||
|
||||
#### Generic PHPDoc Types
|
||||
|
||||
`PromiseInterface`, `PromisorInterface`, and the built-in promise classes now
|
||||
include generic PHPDoc annotations for static analysis tools. The first template
|
||||
type represents the fulfillment value and the second represents the rejection
|
||||
reason. This is a static-analysis-only change and does not alter runtime
|
||||
behavior, but projects with stricter static analysis may see new or different
|
||||
diagnostics:
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
|
||||
/** @var PromiseInterface<string, \Throwable> */
|
||||
$promise = $factory->createPromise();
|
||||
|
||||
$value = $promise->wait();
|
||||
```
|
||||
|
||||
Code that uses unparameterized promise types continues to work and is treated as
|
||||
`PromiseInterface<mixed, mixed>`. If your project implements promise interfaces,
|
||||
extends promise classes, or has stricter static analysis, you may need to update
|
||||
your PHPDoc annotations to include the generic value and reason types. The
|
||||
expanded PHPDoc also preserves promise-chain fulfillment and rejection types
|
||||
more precisely through `then()` and `otherwise()`, and documents collection
|
||||
callbacks with value or reason, key, and aggregate-promise arguments so
|
||||
callbacks may declare only the arguments they use.
|
||||
|
||||
#### Collection Helper Inputs
|
||||
|
||||
Promise collection helpers now require iterable inputs. Passing a single promise
|
||||
or scalar value directly now throws a `TypeError`.
|
||||
|
||||
Wrap single promises or values in an array before passing them to
|
||||
`Create::iterFor()`, `Each::of()`, `Each::ofLimit()`, `Each::ofLimitAll()`,
|
||||
`EachPromise`, or the `Utils` collection helpers.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Each;
|
||||
|
||||
// 2.x
|
||||
$promise = Each::ofLimit($singlePromise, 2);
|
||||
|
||||
// 3.0
|
||||
$promise = Each::ofLimit([$singlePromise], 2);
|
||||
```
|
||||
|
||||
`IteratorAggregate` inputs are now iterated via `getIterator()`. In 2.x an
|
||||
aggregate was treated as a single value and produced one result; in 3.0 its
|
||||
entries are consumed individually.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$aggregate = new \ArrayObject([$promiseA, $promiseB]);
|
||||
|
||||
// 2.x: one result — the ArrayObject itself
|
||||
// 3.0: two results — the fulfillment values of $promiseA and $promiseB
|
||||
$promise = Utils::all($aggregate);
|
||||
```
|
||||
|
||||
#### Collection Helper Signatures
|
||||
|
||||
`Utils::all()`, `Utils::settle()`, and `Each::of()` now accept trailing optional
|
||||
arguments. Direct calls using the 2.x argument lists continue to work, and the
|
||||
affected helper classes are final so subclass signatures do not need to change.
|
||||
Code that mirrors or reflects exact helper signatures may need to be updated.
|
||||
|
||||
Pass the recursive flag before the config array when using `Utils::all()` or
|
||||
`Utils::settle()`:
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = Utils::all($promises, false, ['concurrency' => 5]);
|
||||
$promise = Utils::settle($promises, false, ['concurrency' => 5]);
|
||||
```
|
||||
|
||||
Only `concurrency` is honored by these helper config arrays. Callback config
|
||||
keys such as `fulfilled` and `rejected` are ignored; pass callbacks to
|
||||
`Each::of()` directly or use `EachPromise`.
|
||||
|
||||
#### Recursive Collection Helpers
|
||||
|
||||
Existing `Utils::all($promises, true)` calls may return different results in
|
||||
3.0. Recursive mode now detects dynamically-added settled promises and raw
|
||||
values. In 2.x, recursive mode only checked for pending promises.
|
||||
|
||||
If you previously worked around the lack of recursive `Utils::settle()` support,
|
||||
you can replace that workaround with the new `$recursive` argument:
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = Utils::settle($promises, true);
|
||||
```
|
||||
|
||||
When `$recursive` is true, collection helpers continue taking passes over the
|
||||
collection until no new entries are found and no visible promises remain
|
||||
pending. This is intended for rewindable mutable collections such as
|
||||
`ArrayIterator`. Generators are safe to pass, but they cannot be traversed
|
||||
again once consumed, so recursive mode degrades to a single pass; use a
|
||||
rewindable mutable collection when recursion needs to observe added values.
|
||||
|
||||
#### Promise Inspection
|
||||
|
||||
`Utils::inspect()` and `Utils::inspectAll()` now return the actual rejection
|
||||
reason delivered to rejection callbacks. They no longer unwrap
|
||||
`RejectionException` instances to their inner reason.
|
||||
|
||||
For example, a promise rejected with a `RejectionException` now inspects with
|
||||
that exception as the reason:
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
use GuzzleHttp\Promise\RejectionException;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$reason = new RejectionException('reason');
|
||||
$result = Utils::inspect(new RejectedPromise($reason));
|
||||
|
||||
assert($result['reason'] === $reason);
|
||||
```
|
||||
|
||||
Cancelled promises now inspect with a `CancellationException` reason. If you
|
||||
need the string reason from a `RejectionException` or subclass, call
|
||||
`getReason()` on the exception.
|
||||
|
||||
`Utils::inspect()` still reports wait-function failures as rejection reasons
|
||||
when the wait function does not settle the promise. If the wait function
|
||||
settles the promise and then throws, `inspect()` reports the settled
|
||||
fulfillment or rejection state; direct `Promise::wait()` calls continue to
|
||||
throw that late exception.
|
||||
|
||||
#### Late Rejection Callbacks
|
||||
|
||||
Rejection callbacks registered after a promise was resolved with a rejected
|
||||
promise are now invoked with the nested rejection reason.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise->resolve(new RejectedPromise('reason'));
|
||||
|
||||
$promise->then(null, function ($reason): void {
|
||||
assert($reason === 'reason');
|
||||
});
|
||||
|
||||
Utils::queue()->run();
|
||||
```
|
||||
|
||||
#### Non-instantiable Helper Classes
|
||||
|
||||
Static helper classes such as `Create`, `Each`, `Is`, and `Utils` now have
|
||||
private constructors. Replace any accidental instantiation with static method
|
||||
calls.
|
||||
|
||||
#### Native PHP Serialization of Runtime Objects
|
||||
|
||||
`Promise`, `TaskQueue`, `EachPromise`, and `Coroutine` no longer support native
|
||||
PHP `serialize()` or `unserialize()`. Persist application values instead of
|
||||
promise runtime state.
|
||||
|
||||
1.x to 2.0
|
||||
----------
|
||||
|
||||
Guzzle Promises 2.0 is a major release that removes deprecated APIs, raises the
|
||||
minimum PHP version, and adds PHP 7 parameter and return types. Applications that
|
||||
only use the object-oriented API should usually need small changes. Applications
|
||||
that call helper functions, implement package interfaces, extend package classes,
|
||||
or pass invalid argument types need closer review.
|
||||
|
||||
#### PHP Version and Dependencies
|
||||
|
||||
Guzzle Promises 2.0 requires PHP `^7.2.5 || ^8.0`. Guzzle Promises 1.x
|
||||
supported PHP `>=5.5`.
|
||||
|
||||
#### PHP 7 Type Hints and Return Types
|
||||
|
||||
Type hints and return types were added wherever possible. Please make sure:
|
||||
|
||||
- You pass values of the documented type when calling methods and functions.
|
||||
- Classes that implement `PromiseInterface`, `PromisorInterface`, or
|
||||
`TaskQueueInterface` update method signatures to remain compatible.
|
||||
- Classes that extend Guzzle Promises classes update any overridden method
|
||||
signatures to remain compatible.
|
||||
- Code that expected package-specific exceptions for invalid argument types may
|
||||
now receive PHP `TypeError` exceptions instead.
|
||||
|
||||
#### Soft-Final Classes
|
||||
|
||||
All previously non-final non-exception classes are now final or annotated with
|
||||
`@final`. If your code extends one of these classes, replace inheritance with
|
||||
composition or implement the relevant interface directly.
|
||||
|
||||
#### Removed Function API
|
||||
|
||||
The static API was introduced in 1.4.0 to mitigate problems with functions
|
||||
conflicting between global and local copies of the package. The function API was
|
||||
removed in 2.0.0, along with the Composer `files` autoload entry that loaded
|
||||
`src/functions_include.php`.
|
||||
|
||||
Replace namespaced function calls with the corresponding static methods in the
|
||||
`GuzzleHttp\Promise` namespace:
|
||||
|
||||
```php
|
||||
// Before:
|
||||
use function GuzzleHttp\Promise\promise_for;
|
||||
|
||||
$promise = promise_for('value');
|
||||
|
||||
// After:
|
||||
use GuzzleHttp\Promise\Create;
|
||||
|
||||
$promise = Create::promiseFor('value');
|
||||
```
|
||||
|
||||
| Original Function | Replacement Method |
|
||||
|-------------------|--------------------|
|
||||
| `queue` | `Utils::queue` |
|
||||
| `task` | `Utils::task` |
|
||||
| `promise_for` | `Create::promiseFor` |
|
||||
| `rejection_for` | `Create::rejectionFor` |
|
||||
| `exception_for` | `Create::exceptionFor` |
|
||||
| `iter_for` | `Create::iterFor` |
|
||||
| `inspect` | `Utils::inspect` |
|
||||
| `inspect_all` | `Utils::inspectAll` |
|
||||
| `unwrap` | `Utils::unwrap` |
|
||||
| `all` | `Utils::all` |
|
||||
| `some` | `Utils::some` |
|
||||
| `any` | `Utils::any` |
|
||||
| `settle` | `Utils::settle` |
|
||||
| `each` | `Each::of` |
|
||||
| `each_limit` | `Each::ofLimit` |
|
||||
| `each_limit_all` | `Each::ofLimitAll` |
|
||||
| `!is_fulfilled` | `Is::pending` |
|
||||
| `is_fulfilled` | `Is::fulfilled` |
|
||||
| `is_rejected` | `Is::rejected` |
|
||||
| `is_settled` | `Is::settled` |
|
||||
| `coroutine` | `Coroutine::of` |
|
||||
|
||||
For the full 2.0 diff, see
|
||||
https://github.com/guzzle/promises/compare/1.5.3...2.0.0.
|
||||
+17
-14
@@ -1,8 +1,10 @@
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"description": "Guzzle promises library",
|
||||
"keywords": ["promise"],
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
@@ -26,33 +28,34 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5"
|
||||
"php": "^7.4 || ^8.0",
|
||||
"symfony/deprecation-contracts": "^2.5 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^4.4 || ^5.1"
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"phpunit/phpunit": "^9.6.34"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
},
|
||||
"files": ["src/functions_include.php"]
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/simple-phpunit",
|
||||
"test-ci": "vendor/bin/simple-phpunit --coverage-text"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.5-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"bamarni/composer-bin-plugin": true
|
||||
},
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
# Implementation Notes
|
||||
|
||||
This document describes implementation details that explain observable behavior
|
||||
in `guzzlehttp/promises`, especially queue-based callback execution, iterative
|
||||
resolution, and why `Promise` also acts as the deferred value. Application code
|
||||
usually only needs the [Promise Quick Start](promise-quick-start.md) and
|
||||
[Promise API](promise-api.md).
|
||||
|
||||
## Iterative Resolution and Chaining
|
||||
|
||||
Promises are resolved iteratively by moving pending handlers between promises.
|
||||
This keeps stack size constant even for very long `then()` chains.
|
||||
|
||||
```php
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$parent = new Promise();
|
||||
$p = $parent;
|
||||
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$p = $p->then(function ($v) {
|
||||
// The stack size remains constant.
|
||||
echo xdebug_get_stack_depth() . ', ';
|
||||
return $v + 1;
|
||||
});
|
||||
}
|
||||
|
||||
$parent->resolve(0);
|
||||
var_dump($p->wait()); // int(1000)
|
||||
|
||||
```
|
||||
|
||||
When a promise is fulfilled or rejected with a non-promise value, the promise
|
||||
takes ownership of each child promise's handlers and delivers values down the
|
||||
chain without recursion.
|
||||
|
||||
When a promise is resolved with another promise, the original promise transfers
|
||||
all of its pending handlers to the new promise. When the new promise is
|
||||
eventually resolved, all pending handlers receive the forwarded
|
||||
value.
|
||||
|
||||
## A Promise Is the Deferred
|
||||
|
||||
Some promise libraries implement promises using a deferred object to represent
|
||||
a computation and a promise object to represent the delivery of the result of
|
||||
the computation. That separation prevents consumers from modifying the value
|
||||
that will eventually be delivered.
|
||||
|
||||
Iterative resolution requires one promise to move handlers from another promise.
|
||||
To do that without making handlers publicly mutable, `Promise` is also the
|
||||
deferred value. Promises of the same class can modify each other's private
|
||||
state, including handler ownership. This means a consumer that receives a
|
||||
`Promise` can also resolve or reject it, but it keeps chaining efficient and
|
||||
stack safe.
|
||||
|
||||
```php
|
||||
$promise = new Promise();
|
||||
$promise->then(function ($value) { echo $value; });
|
||||
// The promise is the deferred value, so you can deliver a value to it.
|
||||
$promise->resolve('foo');
|
||||
GuzzleHttp\Promise\Utils::queue()->run();
|
||||
// Prints "foo"
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [Quick Start](promise-quick-start.md)
|
||||
- [Promise API](promise-api.md)
|
||||
- [Promise Interoperability](promise-interoperability.md)
|
||||
@@ -0,0 +1,177 @@
|
||||
# Promise API
|
||||
|
||||
This reference summarizes the public API provided by `guzzlehttp/promises`.
|
||||
Promise APIs are documented for static analysis as
|
||||
`PromiseInterface<TValue, TReason>`. `TValue` is the fulfillment value type and
|
||||
`TReason` is the rejection reason type. This typing is PHPDoc-only and does not
|
||||
change runtime behavior.
|
||||
|
||||
Callbacks registered with `then()` are queued. They are invoked when the global
|
||||
task queue runs, when a returned promise is waited on, or when the queue is
|
||||
drained by the default shutdown handler.
|
||||
|
||||
## PromiseInterface and Promise
|
||||
|
||||
`PromiseInterface` defines the common promise contract and the states
|
||||
`pending`, `fulfilled`, and `rejected`.
|
||||
|
||||
When creating a `Promise`, you can provide an optional `$waitFn` and
|
||||
`$cancelFn`. `$waitFn` receives a boolean argument and is expected to resolve or
|
||||
reject the promise. `$cancelFn` receives no arguments and is invoked when
|
||||
`cancel()` is called.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise(
|
||||
function (bool $recursive) use (&$promise) {
|
||||
$promise->resolve('waited');
|
||||
},
|
||||
function () {
|
||||
// Cancel the underlying operation, such as closing a socket.
|
||||
}
|
||||
);
|
||||
|
||||
assert('waited' === $promise->wait());
|
||||
```
|
||||
|
||||
A promise has the following methods:
|
||||
|
||||
- `then(?callable $onFulfilled = null, ?callable $onRejected = null) : PromiseInterface` appends fulfillment and rejection handlers and returns a new promise resolving to the return value of the called handler. If a handler is omitted, the original fulfillment value or rejection reason is forwarded.
|
||||
- `otherwise(callable $onRejected) : PromiseInterface` appends a rejection handler and returns a new promise resolving to the callback result if called, or to the original fulfillment value if the promise is fulfilled.
|
||||
- `wait(bool $unwrap = true) : mixed` synchronously waits on the promise. When `$unwrap` is `true`, fulfilled values are returned and rejected reasons are thrown. When `$unwrap` is `false`, the promise is settled without returning or throwing its result.
|
||||
- `cancel() : void` attempts to cancel the promise and dependent promises.
|
||||
- `getState() : string` returns `pending`, `fulfilled`, or `rejected`.
|
||||
- `resolve($value = null) : void` fulfills the promise with `$value`, or with `null` if no value is given.
|
||||
- `reject($reason) : void` rejects the promise with `$reason`.
|
||||
|
||||
## Settled Promises
|
||||
|
||||
`FulfilledPromise` represents an already fulfilled promise. Fulfillment
|
||||
callbacks are still queued and run when the task queue runs or the returned
|
||||
promise is waited on.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\FulfilledPromise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new FulfilledPromise('value');
|
||||
$promise->then(function ($value) {
|
||||
echo $value;
|
||||
});
|
||||
|
||||
Utils::queue()->run();
|
||||
```
|
||||
|
||||
`RejectedPromise` represents an already rejected promise. Rejection callbacks
|
||||
are also queued and run when the queue is drained or the returned promise is
|
||||
waited on.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new RejectedPromise('Error');
|
||||
$promise->then(null, function ($reason) {
|
||||
echo $reason;
|
||||
});
|
||||
|
||||
Utils::queue()->run();
|
||||
```
|
||||
|
||||
## Utils
|
||||
|
||||
`GuzzleHttp\Promise\Utils` provides helpers for inspecting, aggregating, and
|
||||
queuing promise work.
|
||||
|
||||
- `Utils::queue(?TaskQueueInterface $assign = null) : TaskQueueInterface` returns the global task queue, or assigns a replacement queue when `$assign` is provided.
|
||||
- `Utils::task(callable $task) : PromiseInterface` adds a task to the global queue and returns a promise that is fulfilled or rejected with the task result.
|
||||
- `Utils::inspect(PromiseInterface $promise) : array` waits for one promise to settle and returns an inspection array with `state` and either `value` or `reason`.
|
||||
- `Utils::inspectAll(iterable $promises) : array` inspects each promise and returns inspection arrays keyed like the input iterable.
|
||||
- `Utils::unwrap(iterable $promises) : array` waits on all promises and returns fulfilled values, throwing if any promise rejects.
|
||||
- `Utils::all(iterable $promises, bool $recursive = false, array $config = []) : PromiseInterface` returns a promise fulfilled with all values, or rejected when any input rejects.
|
||||
- `Utils::settle(iterable $promises, bool $recursive = false, array $config = []) : PromiseInterface` returns a promise fulfilled with inspection arrays after all inputs settle.
|
||||
- `Utils::some(int $count, iterable $promises) : PromiseInterface` fulfills with the values of the first `$count` promises to fulfill, in the order they appear in the input, or rejects with `AggregateException` if too few fulfill.
|
||||
- `Utils::any(iterable $promises) : PromiseInterface` fulfills with the first fulfilled value, or rejects with `AggregateException` if none fulfill.
|
||||
|
||||
`Utils::all()` and `Utils::settle()` accept `['concurrency' => 5]` or
|
||||
`['concurrency' => callable]` for lazy iterables. This limits how many items are
|
||||
pulled from the iterable at one time; it does not throttle promises that have
|
||||
already been created or started.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = Utils::all($promises, false, ['concurrency' => 5]);
|
||||
```
|
||||
|
||||
## Create
|
||||
|
||||
`GuzzleHttp\Promise\Create` provides factories used by the promise
|
||||
implementation and by callers that need to normalize values.
|
||||
|
||||
- `Create::promiseFor($value) : PromiseInterface` returns `$value` when it is already a Guzzle promise, wraps foreign thenables in a Guzzle promise, or returns a fulfilled promise for plain values.
|
||||
- `Create::rejectionFor($reason) : PromiseInterface` returns `$reason` when it is already a promise, or returns a rejected promise for plain reasons.
|
||||
- `Create::exceptionFor($reason) : Throwable` returns throwable reasons as-is, or wraps non-throwable reasons in `RejectionException`.
|
||||
- `Create::iterFor(iterable $value) : Iterator` returns an iterator for arrays, iterators, iterator aggregates, and traversables.
|
||||
|
||||
## State
|
||||
|
||||
`GuzzleHttp\Promise\State` provides readable state checks:
|
||||
|
||||
- `State::isPending(PromiseInterface $promise) : bool`
|
||||
- `State::isSettled(PromiseInterface $promise) : bool`
|
||||
- `State::isFulfilled(PromiseInterface $promise) : bool`
|
||||
- `State::isRejected(PromiseInterface $promise) : bool`
|
||||
|
||||
`GuzzleHttp\Promise\Is` is deprecated because PHP 8.6 deprecates `is` as a class
|
||||
name, and will be removed in Guzzle Promises 4.0. Replace `Is::pending()`,
|
||||
`Is::settled()`, `Is::fulfilled()`, and `Is::rejected()` with the matching
|
||||
`State` methods.
|
||||
|
||||
## Each and EachPromise
|
||||
|
||||
`Each::of()` consumes an iterable of promises or values and invokes callbacks as
|
||||
items settle. Fulfillment callbacks receive the fulfilled value, iterable key,
|
||||
and aggregate promise. Rejection callbacks receive the rejection reason,
|
||||
iterable key, and aggregate promise. Callback return values are ignored.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Each;
|
||||
|
||||
$promise = Each::of($promises, $onFulfilled, $onRejected, ['concurrency' => 5]);
|
||||
```
|
||||
|
||||
- `Each::of(iterable $iterable, ?callable $onFulfilled = null, ?callable $onRejected = null, array $config = []) : PromiseInterface` consumes the iterable and optionally limits lazy iteration with `concurrency`.
|
||||
- `Each::ofLimit(iterable $iterable, $concurrency, ?callable $onFulfilled = null, ?callable $onRejected = null) : PromiseInterface` is a convenience wrapper for `Each::of()` with a concurrency limit.
|
||||
- `Each::ofLimitAll(iterable $iterable, $concurrency, ?callable $onFulfilled = null) : PromiseInterface` is like `ofLimit()`, but rejects the aggregate promise on the first rejection.
|
||||
- `EachPromise` is the configurable class behind `Each`; use it directly when you need `fulfilled`, `rejected`, and `concurrency` keys in one configuration array.
|
||||
|
||||
The concurrency options limit lazy promise creation. For HTTP request
|
||||
concurrency, use `GuzzleHttp\Pool` from `guzzlehttp/guzzle`.
|
||||
|
||||
## Coroutine
|
||||
|
||||
`Coroutine::of(callable $generatorFn) : Coroutine` creates a promise resolved by
|
||||
a generator that yields values or promises. The generator resumes with each
|
||||
fulfilled value, and rejections are thrown into the generator.
|
||||
|
||||
## Task Queue
|
||||
|
||||
`TaskQueueInterface` exposes `isEmpty()`, `add(callable $task)`, and `run()`.
|
||||
`TaskQueue` executes queued tasks in FIFO order. The default queue runs at
|
||||
process shutdown unless disabled, and `wait()` drains the queue while resolving
|
||||
the promise being waited on.
|
||||
|
||||
## Exceptions
|
||||
|
||||
- `RejectionException` is thrown by `wait()` when a rejected promise has a non-throwable reason. The original reason is available through `getReason()`.
|
||||
- `AggregateException` extends `RejectionException` and is used by `Utils::some()` and `Utils::any()` when too few promises fulfill.
|
||||
- `CancellationException` extends `RejectionException` and is used as the rejection reason for cancelled promises.
|
||||
|
||||
## Related
|
||||
|
||||
- [Quick Start](promise-quick-start.md)
|
||||
- [Promise Interoperability](promise-interoperability.md)
|
||||
- [Implementation Notes](implementation-notes.md)
|
||||
- [Upgrade Guide](../UPGRADING.md)
|
||||
@@ -0,0 +1,78 @@
|
||||
# Promise Interoperability
|
||||
|
||||
This guide explains how Guzzle promises interact with foreign promise
|
||||
implementations. A foreign promise is any object with a `then` method, such as a
|
||||
[React promise](https://github.com/reactphp/promise). When a foreign promise is
|
||||
returned from a `then` callback, Guzzle forwards resolution to that promise.
|
||||
|
||||
## Foreign Promises
|
||||
|
||||
Capture the promise returned from `then()`. That chained promise is the Guzzle
|
||||
promise that follows the foreign promise's eventual result.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$deferred = new React\Promise\Deferred();
|
||||
$reactPromise = $deferred->promise();
|
||||
|
||||
$guzzlePromise = new Promise();
|
||||
$chained = $guzzlePromise->then(function ($value) use ($reactPromise) {
|
||||
// Use the Guzzle value, then continue with the React promise.
|
||||
return $reactPromise;
|
||||
});
|
||||
|
||||
$chained->then(function ($value) {
|
||||
echo $value;
|
||||
});
|
||||
|
||||
$guzzlePromise->resolve('start');
|
||||
Utils::queue()->run();
|
||||
|
||||
$deferred->resolve('done');
|
||||
Utils::queue()->run();
|
||||
```
|
||||
|
||||
Forwarding a foreign promise does not make Guzzle able to synchronously wait on
|
||||
or cancel the foreign operation. The chained Guzzle promise settles only after
|
||||
the foreign implementation invokes the callbacks registered through `then()`.
|
||||
If the foreign promise has no compatible `wait()` or `cancel()` behavior, Guzzle
|
||||
cannot invent that behavior.
|
||||
|
||||
Use `Create::promiseFor($foreignPromise)` to shadow a foreign thenable as a
|
||||
Guzzle promise. If the foreign object exposes `wait()` or `cancel()` methods,
|
||||
the wrapper uses them. Otherwise, use the foreign implementation's event loop or
|
||||
completion mechanism and drain Guzzle's task queue when callbacks need to run.
|
||||
|
||||
## Event Loop Integration
|
||||
|
||||
Guzzle promises use a task queue to keep stack size constant and to run promise
|
||||
callbacks asynchronously. When waiting on promises synchronously, the task queue
|
||||
is automatically run while resolving the blocking promise and forwarded Guzzle
|
||||
promises.
|
||||
|
||||
When using promises asynchronously in an event loop, run the task queue on loop
|
||||
ticks. If you do not run the task queue, Guzzle promise callbacks may remain
|
||||
queued.
|
||||
|
||||
```php
|
||||
$queue = GuzzleHttp\Promise\Utils::queue();
|
||||
$queue->run();
|
||||
```
|
||||
|
||||
For example, you could use Guzzle promises with React using a short periodic
|
||||
timer. Avoid zero-interval timers because they may keep the loop busy even when
|
||||
there is no promise work to run.
|
||||
|
||||
```php
|
||||
$queue = GuzzleHttp\Promise\Utils::queue();
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
$loop->addPeriodicTimer(0.01, [$queue, 'run']);
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [Quick Start](promise-quick-start.md)
|
||||
- [Promise API](promise-api.md)
|
||||
- [Implementation Notes](implementation-notes.md)
|
||||
@@ -0,0 +1,319 @@
|
||||
# Promise Quick Start
|
||||
|
||||
This guide covers the common promise operations needed when using
|
||||
`guzzlehttp/promises` directly: registering callbacks, resolving or rejecting
|
||||
promises, waiting synchronously, composing chains, and using generator-based
|
||||
flows. For the full public surface, see the [Promise API](promise-api.md).
|
||||
|
||||
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 the eventual value or the reason why the
|
||||
promise cannot be fulfilled.
|
||||
|
||||
Guzzle promise callbacks are queued. They run when the task queue is drained,
|
||||
for example by `Utils::queue()->run()`, by waiting on a returned promise, or by
|
||||
the default shutdown handler at the end of the PHP process. Examples that show
|
||||
callback output drain the queue explicitly.
|
||||
|
||||
## Callbacks
|
||||
|
||||
Callbacks are registered with the `then` method by providing an optional
|
||||
`$onFulfilled` followed by an optional `$onRejected` function.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise->then(
|
||||
// $onFulfilled
|
||||
function ($value) {
|
||||
echo 'The promise was fulfilled.';
|
||||
},
|
||||
// $onRejected
|
||||
function ($reason) {
|
||||
echo 'The promise was rejected.';
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
*Resolving* a promise means that you either fulfill a promise with a *value* or
|
||||
reject a promise with a *reason*. Callbacks registered with `then` are invoked
|
||||
only once and in the order in which they were added when the queue is drained.
|
||||
|
||||
## Resolving a Promise
|
||||
|
||||
Promises are fulfilled using the `resolve($value = null)` method. Calling
|
||||
`resolve()` without an argument fulfills the promise with `null`. Resolving a
|
||||
promise with any value other than a `GuzzleHttp\Promise\RejectedPromise` queues
|
||||
the `$onFulfilled` callbacks. Resolving with a rejected promise rejects the
|
||||
promise and queues the `$onRejected` callbacks.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise
|
||||
->then(function ($value) {
|
||||
return 'Hello, ' . $value;
|
||||
})
|
||||
->then(function ($value) {
|
||||
echo $value;
|
||||
});
|
||||
|
||||
$promise->resolve('reader.');
|
||||
Utils::queue()->run();
|
||||
// Outputs "Hello, reader."
|
||||
```
|
||||
|
||||
## Promise Forwarding
|
||||
|
||||
Promises can be chained one after the other. Each `then` call returns a new
|
||||
promise. The return value of a callback is forwarded to the next promise in the
|
||||
chain. Returning a promise from a callback makes the next promise wait for that
|
||||
returned promise to settle.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new Promise();
|
||||
$nextPromise = new Promise();
|
||||
|
||||
$promise
|
||||
->then(function ($value) use ($nextPromise) {
|
||||
echo $value;
|
||||
|
||||
return $nextPromise;
|
||||
})
|
||||
->then(function ($value) {
|
||||
echo $value;
|
||||
});
|
||||
|
||||
$promise->resolve('A');
|
||||
Utils::queue()->run();
|
||||
// Outputs "A"
|
||||
|
||||
$nextPromise->resolve('B');
|
||||
Utils::queue()->run();
|
||||
// Outputs "B"
|
||||
```
|
||||
|
||||
## Promise Rejection
|
||||
|
||||
When a promise is rejected, the `$onRejected` callbacks are invoked with the
|
||||
rejection reason when the queue is drained.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise->then(null, function ($reason) {
|
||||
echo $reason;
|
||||
});
|
||||
|
||||
$promise->reject('Error!');
|
||||
Utils::queue()->run();
|
||||
// Outputs "Error!"
|
||||
```
|
||||
|
||||
## Rejection Forwarding
|
||||
|
||||
If an exception is thrown in an `$onRejected` callback, subsequent
|
||||
`$onRejected` callbacks receive the thrown exception as the reason.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise->then(null, function ($reason) {
|
||||
throw new Exception($reason);
|
||||
})->then(null, function ($reason) {
|
||||
assert($reason->getMessage() === 'Error!');
|
||||
});
|
||||
|
||||
$promise->reject('Error!');
|
||||
Utils::queue()->run();
|
||||
```
|
||||
|
||||
You can also forward a rejection down the promise chain by returning a
|
||||
`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
|
||||
`$onRejected` callback.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\RejectedPromise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise->then(null, function ($reason) {
|
||||
return new RejectedPromise($reason);
|
||||
})->then(null, function ($reason) {
|
||||
assert($reason === 'Error!');
|
||||
});
|
||||
|
||||
$promise->reject('Error!');
|
||||
Utils::queue()->run();
|
||||
```
|
||||
|
||||
If an exception is not thrown in an `$onRejected` callback and the callback
|
||||
does not return a rejected promise, downstream `$onFulfilled` callbacks are
|
||||
invoked using the value returned from the `$onRejected` callback.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
use GuzzleHttp\Promise\Utils;
|
||||
|
||||
$promise = new Promise();
|
||||
$promise
|
||||
->then(null, function ($reason) {
|
||||
return "It's ok";
|
||||
})
|
||||
->then(function ($value) {
|
||||
assert($value === "It's ok");
|
||||
});
|
||||
|
||||
$promise->reject('Error!');
|
||||
Utils::queue()->run();
|
||||
```
|
||||
|
||||
## Synchronous Wait
|
||||
|
||||
You can synchronously force promises to complete using a promise's `wait`
|
||||
method. When creating a promise, you can provide a wait function that is used
|
||||
to synchronously complete the promise. The wait function receives a boolean
|
||||
argument and is expected to resolve or reject the promise. If the wait function
|
||||
does not settle the promise, an exception is thrown.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise(function (bool $recursive) use (&$promise) {
|
||||
$promise->resolve('foo');
|
||||
});
|
||||
|
||||
echo $promise->wait();
|
||||
// Outputs "foo"
|
||||
```
|
||||
|
||||
If a throwable is encountered while invoking the wait function of a promise,
|
||||
the promise is rejected with the throwable and the throwable is thrown.
|
||||
|
||||
```php
|
||||
$promise = new Promise(function (bool $recursive) use (&$promise) {
|
||||
throw new Exception('foo');
|
||||
});
|
||||
|
||||
$promise->wait(); // Throws the exception.
|
||||
```
|
||||
|
||||
Calling `wait` on a promise that has been fulfilled will not trigger the wait
|
||||
function. It will simply return the previously resolved value.
|
||||
|
||||
```php
|
||||
$promise = new Promise(function (bool $recursive) { die('this is not called!'); });
|
||||
$promise->resolve('foo');
|
||||
|
||||
echo $promise->wait();
|
||||
// Outputs "foo"
|
||||
```
|
||||
|
||||
Calling `wait` on a promise that has been rejected will throw. If the rejection
|
||||
reason is an instance of `\Throwable`, the reason is thrown. Otherwise, a
|
||||
`GuzzleHttp\Promise\RejectionException` is thrown and the reason can be
|
||||
obtained by calling `getReason()` on the exception.
|
||||
|
||||
```php
|
||||
$promise = new Promise();
|
||||
$promise->reject('foo');
|
||||
$promise->wait();
|
||||
```
|
||||
|
||||
> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with reason: foo'
|
||||
|
||||
## Unwrapping a Promise
|
||||
|
||||
When synchronously waiting on a promise, you are joining the state of the
|
||||
promise into the current execution: the fulfilled value is returned, or the
|
||||
rejection reason is thrown. This is called "unwrapping" the promise. Waiting on
|
||||
a promise unwraps by default.
|
||||
|
||||
You can force a promise to resolve and *not* unwrap its state by passing
|
||||
`false` to `wait()`:
|
||||
|
||||
```php
|
||||
$promise = new Promise();
|
||||
$promise->reject('foo');
|
||||
|
||||
// This does not throw. It only ensures the promise has been resolved.
|
||||
$promise->wait(false);
|
||||
```
|
||||
|
||||
When unwrapping a promise, the resolved value of the promise will be waited on
|
||||
until the unwrapped value is not a promise. This means that if promise A is
|
||||
resolved with promise B, unwrapping promise A returns the value delivered to
|
||||
promise B.
|
||||
|
||||
When you do not unwrap the promise, no value is returned.
|
||||
|
||||
## Inspecting a Promise
|
||||
|
||||
`Utils::inspect($promise)` waits for a promise to settle and returns an array
|
||||
describing its final state. For rejected promises, the `reason` entry is the
|
||||
actual rejection reason delivered to rejection callbacks.
|
||||
|
||||
This means `RejectionException` and subclasses are not unwrapped by
|
||||
`inspect()`. For example, cancelled promises inspect with a
|
||||
`CancellationException` reason.
|
||||
|
||||
## Generator-Based Async
|
||||
|
||||
`Coroutine::of()` creates a promise from a generator that yields values or
|
||||
promises. The generator resumes each time the yielded value settles, which can
|
||||
make sequential asynchronous flows easier to read.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Coroutine;
|
||||
use GuzzleHttp\Promise\FulfilledPromise;
|
||||
|
||||
$promise = Coroutine::of(function () {
|
||||
$first = yield new FulfilledPromise('A');
|
||||
$second = yield new FulfilledPromise($first . 'B');
|
||||
|
||||
yield $second . 'C';
|
||||
});
|
||||
|
||||
echo $promise->wait();
|
||||
// Outputs "ABC"
|
||||
```
|
||||
|
||||
If a yielded promise rejects, the rejection is thrown into the generator. Catch
|
||||
it inside the generator to recover, or let it reject the coroutine promise.
|
||||
|
||||
## Cancellation
|
||||
|
||||
You can cancel a promise that has not yet been fulfilled using `cancel()`. When
|
||||
creating a promise, you can provide an optional cancel function that cancels the
|
||||
underlying operation, such as closing a socket or aborting a query.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Promise\Promise;
|
||||
|
||||
$promise = new Promise(null, function () {
|
||||
// Cancel the underlying operation.
|
||||
});
|
||||
|
||||
$promise->cancel();
|
||||
```
|
||||
|
||||
Cancellation rejects the promise with a `CancellationException` unless the
|
||||
cancel function settles the promise first.
|
||||
|
||||
## Related
|
||||
|
||||
- [Promise API](promise-api.md)
|
||||
- [Promise Interoperability](promise-interoperability.md)
|
||||
- [Implementation Notes](implementation-notes.md)
|
||||
- [Upgrade Guide](../UPGRADING.md)
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
@@ -7,7 +9,7 @@ namespace GuzzleHttp\Promise;
|
||||
*/
|
||||
class AggregateException extends RejectionException
|
||||
{
|
||||
public function __construct($msg, array $reasons)
|
||||
public function __construct(string $msg, array $reasons)
|
||||
{
|
||||
parent::__construct(
|
||||
$reasons,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
|
||||
+67
-41
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
use Exception;
|
||||
use Generator;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Creates a promise that is resolved using a generator that yields values or
|
||||
@@ -27,7 +27,7 @@ use Throwable;
|
||||
* $value = (yield createPromise('a'));
|
||||
* try {
|
||||
* $value = (yield createPromise($value . 'b'));
|
||||
* } catch (\Exception $e) {
|
||||
* } catch (\Throwable $e) {
|
||||
* // The promise was rejected.
|
||||
* }
|
||||
* yield $value . 'c';
|
||||
@@ -36,42 +36,46 @@ use Throwable;
|
||||
* // Outputs "abc"
|
||||
* $promise->then(function ($v) { echo $v; });
|
||||
*
|
||||
* @param callable $generatorFn Generator function to wrap into a promise.
|
||||
* @template TValue = mixed
|
||||
* @template TReason = mixed
|
||||
*
|
||||
* @return Promise
|
||||
* @implements PromiseInterface<TValue, TReason>
|
||||
*
|
||||
* @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
|
||||
* @see https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
|
||||
*/
|
||||
final class Coroutine implements PromiseInterface
|
||||
{
|
||||
/**
|
||||
* @var PromiseInterface|null
|
||||
*/
|
||||
private $currentPromise;
|
||||
use NonSerializableTrait;
|
||||
|
||||
/**
|
||||
* @var Generator
|
||||
* @var PromiseInterface<mixed, mixed>|null
|
||||
*/
|
||||
private $generator;
|
||||
private ?PromiseInterface $currentPromise = null;
|
||||
|
||||
/**
|
||||
* @var Promise
|
||||
* @var Generator<mixed, mixed, mixed, mixed>
|
||||
*/
|
||||
private $result;
|
||||
private Generator $generator;
|
||||
|
||||
/**
|
||||
* @var Promise<TValue, TReason>
|
||||
*/
|
||||
private PromiseInterface $result;
|
||||
|
||||
/**
|
||||
* @param callable(): Generator<mixed, mixed, mixed, mixed> $generatorFn
|
||||
*/
|
||||
public function __construct(callable $generatorFn)
|
||||
{
|
||||
$this->generator = $generatorFn();
|
||||
$this->result = new Promise(function () {
|
||||
$this->result = new Promise(function (): void {
|
||||
while (isset($this->currentPromise)) {
|
||||
$this->currentPromise->wait();
|
||||
}
|
||||
});
|
||||
try {
|
||||
$this->nextCoroutine($this->generator->current());
|
||||
} catch (\Exception $exception) {
|
||||
$this->result->reject($exception);
|
||||
} catch (Throwable $throwable) {
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->result->reject($throwable);
|
||||
}
|
||||
}
|
||||
@@ -79,52 +83,76 @@ final class Coroutine implements PromiseInterface
|
||||
/**
|
||||
* Create a new coroutine.
|
||||
*
|
||||
* @return self
|
||||
* @param callable(): Generator<mixed, mixed, mixed, mixed> $generatorFn
|
||||
*
|
||||
* @return self<mixed, mixed>
|
||||
*/
|
||||
public static function of(callable $generatorFn)
|
||||
public static function of(callable $generatorFn): self
|
||||
{
|
||||
return new self($generatorFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TFulfilledValue = never
|
||||
* @template TFulfilledReason = never
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @param (callable(TValue): (TFulfilledValue|PromiseInterface<TFulfilledValue, TFulfilledReason>))|null $onFulfilled Invoked when the promise fulfills.
|
||||
* @param (callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>))|null $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return PromiseInterface<($onFulfilled is null ? TValue : TFulfilledValue)|($onRejected is null ? never : TRejectedValue), ($onFulfilled is null ? never : TFulfilledReason|\Throwable)|($onRejected is null ? TReason : TRejectedReason|\Throwable)>
|
||||
*/
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
) {
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
): PromiseInterface {
|
||||
return $this->result->then($onFulfilled, $onRejected);
|
||||
}
|
||||
|
||||
public function otherwise(callable $onRejected)
|
||||
/**
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @param callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>) $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return PromiseInterface<TValue|TRejectedValue, TRejectedReason|\Throwable>
|
||||
*/
|
||||
public function otherwise(callable $onRejected): PromiseInterface
|
||||
{
|
||||
return $this->result->otherwise($onRejected);
|
||||
}
|
||||
|
||||
public function wait($unwrap = true)
|
||||
public function wait(bool $unwrap = true)
|
||||
{
|
||||
return $this->result->wait($unwrap);
|
||||
}
|
||||
|
||||
public function getState()
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->result->getState();
|
||||
}
|
||||
|
||||
public function resolve($value)
|
||||
public function resolve($value = null): void
|
||||
{
|
||||
$this->result->resolve($value);
|
||||
}
|
||||
|
||||
public function reject($reason)
|
||||
public function reject($reason): void
|
||||
{
|
||||
$this->result->reject($reason);
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
public function cancel(): void
|
||||
{
|
||||
$this->currentPromise->cancel();
|
||||
if (isset($this->currentPromise)) {
|
||||
$this->currentPromise->cancel();
|
||||
}
|
||||
|
||||
$this->result->cancel();
|
||||
}
|
||||
|
||||
private function nextCoroutine($yielded)
|
||||
private function nextCoroutine($yielded): void
|
||||
{
|
||||
$this->currentPromise = Create::promiseFor($yielded)
|
||||
->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
|
||||
@@ -133,9 +161,10 @@ final class Coroutine implements PromiseInterface
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function _handleSuccess($value)
|
||||
public function _handleSuccess($value): void
|
||||
{
|
||||
unset($this->currentPromise);
|
||||
$this->currentPromise = null;
|
||||
|
||||
try {
|
||||
$next = $this->generator->send($value);
|
||||
if ($this->generator->valid()) {
|
||||
@@ -143,9 +172,7 @@ final class Coroutine implements PromiseInterface
|
||||
} else {
|
||||
$this->result->resolve($value);
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
$this->result->reject($exception);
|
||||
} catch (Throwable $throwable) {
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->result->reject($throwable);
|
||||
}
|
||||
}
|
||||
@@ -153,16 +180,15 @@ final class Coroutine implements PromiseInterface
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function _handleFailure($reason)
|
||||
public function _handleFailure($reason): void
|
||||
{
|
||||
unset($this->currentPromise);
|
||||
$this->currentPromise = null;
|
||||
|
||||
try {
|
||||
$nextYield = $this->generator->throw(Create::exceptionFor($reason));
|
||||
// The throw was caught, so keep iterating on the coroutine
|
||||
$this->nextCoroutine($nextYield);
|
||||
} catch (Exception $exception) {
|
||||
$this->result->reject($exception);
|
||||
} catch (Throwable $throwable) {
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->result->reject($throwable);
|
||||
}
|
||||
}
|
||||
|
||||
+44
-19
@@ -1,17 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
final class Create
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a promise for a value if the value is not a promise.
|
||||
* Returns `$value` when it is already a Guzzle promise, wraps foreign
|
||||
* thenables in a Guzzle promise, or returns a fulfilled promise for plain
|
||||
* values.
|
||||
*
|
||||
* @param mixed $value Promise or value.
|
||||
* @template TValue
|
||||
* @template TPromise of PromiseInterface<mixed, mixed> = PromiseInterface<mixed, mixed>
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param TValue|TPromise $value Promise or value.
|
||||
*
|
||||
* @return ($value is PromiseInterface ? TPromise : FulfilledPromise<TValue, never>)
|
||||
*/
|
||||
public static function promiseFor($value)
|
||||
public static function promiseFor($value): PromiseInterface
|
||||
{
|
||||
if ($value instanceof PromiseInterface) {
|
||||
return $value;
|
||||
@@ -23,6 +34,7 @@ final class Create
|
||||
$cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
|
||||
$promise = new Promise($wfn, $cfn);
|
||||
$value->then([$promise, 'resolve'], [$promise, 'reject']);
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
@@ -30,14 +42,18 @@ final class Create
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Returns `$reason` when it is already a promise, or returns a rejected
|
||||
* promise for plain reasons.
|
||||
*
|
||||
* @param mixed $reason Promise or reason.
|
||||
* @template TReason
|
||||
* @template TValue = never
|
||||
* @template TPromise of PromiseInterface<mixed, mixed> = PromiseInterface<mixed, mixed>
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param TReason|TPromise $reason Promise or reason.
|
||||
*
|
||||
* @return ($reason is PromiseInterface ? TPromise : RejectedPromise<TValue, TReason>)
|
||||
*/
|
||||
public static function rejectionFor($reason)
|
||||
public static function rejectionFor($reason): PromiseInterface
|
||||
{
|
||||
if ($reason instanceof PromiseInterface) {
|
||||
return $reason;
|
||||
@@ -47,15 +63,16 @@ final class Create
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exception for a rejected promise value.
|
||||
* Returns throwable reasons as-is, or wraps non-throwable reasons in
|
||||
* `RejectionException`.
|
||||
*
|
||||
* @param mixed $reason
|
||||
* @template TReason
|
||||
*
|
||||
* @return \Exception|\Throwable
|
||||
* @param TReason $reason
|
||||
*/
|
||||
public static function exceptionFor($reason)
|
||||
public static function exceptionFor($reason): \Throwable
|
||||
{
|
||||
if ($reason instanceof \Exception || $reason instanceof \Throwable) {
|
||||
if ($reason instanceof \Throwable) {
|
||||
return $reason;
|
||||
}
|
||||
|
||||
@@ -63,13 +80,17 @@ final class Create
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator for the given value.
|
||||
* Returns an iterator for arrays, iterators, iterator aggregates, and
|
||||
* traversables.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
*
|
||||
* @return \Iterator
|
||||
* @param iterable<TKey, TValue> $value
|
||||
*
|
||||
* @return \Iterator<TKey, TValue>
|
||||
*/
|
||||
public static function iterFor($value)
|
||||
public static function iterFor(iterable $value): \Iterator
|
||||
{
|
||||
if ($value instanceof \Iterator) {
|
||||
return $value;
|
||||
@@ -79,6 +100,10 @@ final class Create
|
||||
return new \ArrayIterator($value);
|
||||
}
|
||||
|
||||
return new \ArrayIterator([$value]);
|
||||
if ($value instanceof \IteratorAggregate) {
|
||||
return self::iterFor($value->getIterator());
|
||||
}
|
||||
|
||||
return new \IteratorIterator($value);
|
||||
}
|
||||
}
|
||||
+71
-43
@@ -1,37 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
final class Each
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* $onFulfilled is a function that accepts the fulfilled value, iterable
|
||||
* key, 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
|
||||
* $onRejected is a function that accepts the rejection reason, iterable
|
||||
* key, 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
|
||||
* The config array accepts a concurrency option matching {@see ofLimit}.
|
||||
* Other config keys are ignored by this wrapper.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @param iterable<TKey, TValue|PromiseInterface<TValue, TReason>> $iterable Iterator or array to iterate over.
|
||||
* @param (callable(TValue, TKey, PromiseInterface<mixed, mixed>): mixed)|null $onFulfilled
|
||||
* @param (callable(TReason, TKey, PromiseInterface<mixed, mixed>): mixed)|null $onRejected
|
||||
* @param array{concurrency?: int|(callable(int): int)} $config Configuration options.
|
||||
*
|
||||
* @return PromiseInterface<mixed, mixed>
|
||||
*/
|
||||
public static function of(
|
||||
$iterable,
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
) {
|
||||
return (new EachPromise($iterable, [
|
||||
'fulfilled' => $onFulfilled,
|
||||
'rejected' => $onRejected
|
||||
]))->promise();
|
||||
iterable $iterable,
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null,
|
||||
array $config = []
|
||||
): PromiseInterface {
|
||||
$eachConfig = [];
|
||||
|
||||
if (null !== $onFulfilled) {
|
||||
$eachConfig['fulfilled'] = $onFulfilled;
|
||||
}
|
||||
|
||||
if (null !== $onRejected) {
|
||||
$eachConfig['rejected'] = $onRejected;
|
||||
}
|
||||
|
||||
if (isset($config['concurrency'])) {
|
||||
$eachConfig['concurrency'] = $config['concurrency'];
|
||||
}
|
||||
|
||||
return (new EachPromise($iterable, $eachConfig))->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,49 +66,51 @@ final class Each
|
||||
*
|
||||
* $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.
|
||||
* for a dynamic concurrency size.
|
||||
*
|
||||
* @param mixed $iterable
|
||||
* @param int|callable $concurrency
|
||||
* @param callable $onFulfilled
|
||||
* @param callable $onRejected
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param iterable<TKey, TValue|PromiseInterface<TValue, TReason>> $iterable
|
||||
* @param int|(callable(int): int) $concurrency
|
||||
* @param (callable(TValue, TKey, PromiseInterface<mixed, mixed>): mixed)|null $onFulfilled
|
||||
* @param (callable(TReason, TKey, PromiseInterface<mixed, mixed>): mixed)|null $onRejected
|
||||
*
|
||||
* @return PromiseInterface<mixed, mixed>
|
||||
*/
|
||||
public static function ofLimit(
|
||||
$iterable,
|
||||
iterable $iterable,
|
||||
$concurrency,
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
) {
|
||||
return (new EachPromise($iterable, [
|
||||
'fulfilled' => $onFulfilled,
|
||||
'rejected' => $onRejected,
|
||||
'concurrency' => $concurrency
|
||||
]))->promise();
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
): PromiseInterface {
|
||||
return self::of($iterable, $onFulfilled, $onRejected, ['concurrency' => $concurrency]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Like ofLimit, but rejects the aggregate promise on the first rejection.
|
||||
*
|
||||
* @param mixed $iterable
|
||||
* @param int|callable $concurrency
|
||||
* @param callable $onFulfilled
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param iterable<TKey, TValue|PromiseInterface<TValue, TReason>> $iterable
|
||||
* @param int|(callable(int): int) $concurrency
|
||||
* @param (callable(TValue, TKey, PromiseInterface<mixed, mixed>): mixed)|null $onFulfilled
|
||||
*
|
||||
* @return PromiseInterface<mixed, mixed>
|
||||
*/
|
||||
public static function ofLimitAll(
|
||||
$iterable,
|
||||
iterable $iterable,
|
||||
$concurrency,
|
||||
callable $onFulfilled = null
|
||||
) {
|
||||
return each_limit(
|
||||
?callable $onFulfilled = null
|
||||
): PromiseInterface {
|
||||
return self::ofLimit(
|
||||
$iterable,
|
||||
$concurrency,
|
||||
$onFulfilled,
|
||||
function ($reason, $idx, PromiseInterface $aggregate) {
|
||||
function ($reason, $idx, PromiseInterface $aggregate): void {
|
||||
$aggregate->reject($reason);
|
||||
}
|
||||
);
|
||||
|
||||
+104
-56
@@ -1,34 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* Represents a promise that iterates over many promises and invokes
|
||||
* side-effect functions in the process.
|
||||
*
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @implements PromisorInterface<mixed, mixed>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class EachPromise implements PromisorInterface
|
||||
{
|
||||
private $pending = [];
|
||||
use NonSerializableTrait;
|
||||
|
||||
private $nextPendingIndex = 0;
|
||||
/** @var array<int, PromiseInterface<mixed, mixed>>|null */
|
||||
private ?array $pending = [];
|
||||
|
||||
/** @var \Iterator|null */
|
||||
private $iterable;
|
||||
private int $nextPendingIndex = 0;
|
||||
|
||||
/** @var callable|int|null */
|
||||
/** @var \Iterator<TKey, TValue|PromiseInterface<TValue, TReason>>|null */
|
||||
private ?\Iterator $iterable;
|
||||
|
||||
/** @var (callable(int): int)|int|null */
|
||||
private $concurrency;
|
||||
|
||||
/** @var callable|null */
|
||||
/** @var (callable(TValue, TKey, PromiseInterface<mixed, mixed>): mixed)|null */
|
||||
private $onFulfilled;
|
||||
|
||||
/** @var callable|null */
|
||||
/** @var (callable(TReason, TKey, PromiseInterface<mixed, mixed>): mixed)|null */
|
||||
private $onRejected;
|
||||
|
||||
/** @var Promise|null */
|
||||
private $aggregate;
|
||||
/** @var Promise<mixed, mixed>|null */
|
||||
private ?Promise $aggregate = null;
|
||||
|
||||
/** @var bool|null */
|
||||
private $mutex;
|
||||
private ?bool $mutex = null;
|
||||
|
||||
private bool $stepWhileLocked = false;
|
||||
|
||||
/**
|
||||
* Configuration hash can include the following key value pairs:
|
||||
@@ -48,10 +62,14 @@ class EachPromise implements PromisorInterface
|
||||
* 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
|
||||
* @param iterable<TKey, TValue|PromiseInterface<TValue, TReason>> $iterable Promises or values to iterate.
|
||||
* @param array{
|
||||
* fulfilled?: callable(TValue, TKey, PromiseInterface<mixed, mixed>): mixed,
|
||||
* rejected?: callable(TReason, TKey, PromiseInterface<mixed, mixed>): mixed,
|
||||
* concurrency?: int|(callable(int): int)
|
||||
* } $config Configuration options
|
||||
*/
|
||||
public function __construct($iterable, array $config = [])
|
||||
public function __construct(iterable $iterable, array $config = [])
|
||||
{
|
||||
$this->iterable = Create::iterFor($iterable);
|
||||
|
||||
@@ -68,8 +86,10 @@ class EachPromise implements PromisorInterface
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress InvalidNullableReturnType */
|
||||
public function promise()
|
||||
/**
|
||||
* @return PromiseInterface<mixed, mixed>
|
||||
*/
|
||||
public function promise(): PromiseInterface
|
||||
{
|
||||
if ($this->aggregate) {
|
||||
return $this->aggregate;
|
||||
@@ -77,43 +97,56 @@ class EachPromise implements PromisorInterface
|
||||
|
||||
try {
|
||||
$this->createPromise();
|
||||
/** @psalm-assert Promise $this->aggregate */
|
||||
$this->iterable->rewind();
|
||||
$this->refillPending();
|
||||
if (!$this->pending) {
|
||||
Utils::queue()->add(function (): void {
|
||||
if (!$this->aggregate || State::isSettled($this->aggregate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->checkIfFinished();
|
||||
} catch (\Throwable $e) {
|
||||
$this->aggregate->reject($e);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->aggregate->reject($e);
|
||||
} catch (\Exception $e) {
|
||||
$this->aggregate->reject($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress NullableReturnStatement
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
return $this->aggregate;
|
||||
}
|
||||
|
||||
private function createPromise()
|
||||
private function createPromise(): void
|
||||
{
|
||||
$this->mutex = false;
|
||||
$this->aggregate = new Promise(function () {
|
||||
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)) {
|
||||
$this->aggregate = new Promise(function (): void {
|
||||
while (true) {
|
||||
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 (State::isSettled($this->aggregate)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Refill and re-sweep; give up only when nothing remains.
|
||||
$this->refillPending();
|
||||
if (State::isSettled($this->aggregate) || !$this->pending) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Clear the references when the promise is resolved.
|
||||
$clearFn = function () {
|
||||
$clearFn = function (): void {
|
||||
$this->iterable = $this->concurrency = $this->pending = null;
|
||||
$this->onFulfilled = $this->onRejected = null;
|
||||
$this->nextPendingIndex = 0;
|
||||
@@ -122,18 +155,24 @@ class EachPromise implements PromisorInterface
|
||||
$this->aggregate->then($clearFn, $clearFn);
|
||||
}
|
||||
|
||||
private function refillPending()
|
||||
private function refillPending(): void
|
||||
{
|
||||
if (!$this->concurrency) {
|
||||
// Add all pending promises.
|
||||
while ($this->addPending() && $this->advanceIterator());
|
||||
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)(count($this->pending))
|
||||
: $this->concurrency;
|
||||
// The callable can settle the aggregate; admit nothing more.
|
||||
if (State::isSettled($this->aggregate)) {
|
||||
return;
|
||||
}
|
||||
$concurrency = max($concurrency - count($this->pending), 0);
|
||||
// Concurrency may be set to 0 to disallow new promises.
|
||||
if (!$concurrency) {
|
||||
@@ -147,10 +186,11 @@ class EachPromise implements PromisorInterface
|
||||
// next value to yield until promise callbacks are called.
|
||||
while (--$concurrency
|
||||
&& $this->advanceIterator()
|
||||
&& $this->addPending());
|
||||
&& $this->addPending()) {
|
||||
}
|
||||
}
|
||||
|
||||
private function addPending()
|
||||
private function addPending(): bool
|
||||
{
|
||||
if (!$this->iterable || !$this->iterable->valid()) {
|
||||
return false;
|
||||
@@ -164,10 +204,9 @@ class EachPromise implements PromisorInterface
|
||||
$idx = $this->nextPendingIndex++;
|
||||
|
||||
$this->pending[$idx] = $promise->then(
|
||||
function ($value) use ($idx, $key) {
|
||||
function ($value) use ($idx, $key): void {
|
||||
if ($this->onFulfilled) {
|
||||
call_user_func(
|
||||
$this->onFulfilled,
|
||||
($this->onFulfilled)(
|
||||
$value,
|
||||
$key,
|
||||
$this->aggregate
|
||||
@@ -175,10 +214,9 @@ class EachPromise implements PromisorInterface
|
||||
}
|
||||
$this->step($idx);
|
||||
},
|
||||
function ($reason) use ($idx, $key) {
|
||||
function ($reason) use ($idx, $key): void {
|
||||
if ($this->onRejected) {
|
||||
call_user_func(
|
||||
$this->onRejected,
|
||||
($this->onRejected)(
|
||||
$reason,
|
||||
$key,
|
||||
$this->aggregate
|
||||
@@ -191,11 +229,13 @@ class EachPromise implements PromisorInterface
|
||||
return true;
|
||||
}
|
||||
|
||||
private function advanceIterator()
|
||||
private function advanceIterator(): bool
|
||||
{
|
||||
// Place a lock on the iterator so that we ensure to not recurse,
|
||||
// preventing fatal generator errors.
|
||||
if ($this->mutex) {
|
||||
$this->stepWhileLocked = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -204,22 +244,28 @@ class EachPromise implements PromisorInterface
|
||||
try {
|
||||
$this->iterable->next();
|
||||
$this->mutex = false;
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
$this->aggregate->reject($e);
|
||||
$this->mutex = false;
|
||||
return false;
|
||||
} catch (\Exception $e) {
|
||||
$this->aggregate->reject($e);
|
||||
$this->mutex = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run the completion check that locked steps skipped.
|
||||
if ($this->stepWhileLocked) {
|
||||
$this->stepWhileLocked = false;
|
||||
if (!State::isSettled($this->aggregate)) {
|
||||
$this->checkIfFinished();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function step($idx)
|
||||
private function step(int $idx): void
|
||||
{
|
||||
// If the promise was already resolved, then ignore this step.
|
||||
if (Is::settled($this->aggregate)) {
|
||||
if (State::isSettled($this->aggregate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -234,11 +280,13 @@ class EachPromise implements PromisorInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function checkIfFinished()
|
||||
/** @phpstan-impure */
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
@@ -7,11 +9,22 @@ namespace GuzzleHttp\Promise;
|
||||
*
|
||||
* Thenning off of this promise will invoke the onFulfilled callback
|
||||
* immediately and ignore other callbacks.
|
||||
*
|
||||
* @template TValue = mixed
|
||||
* @template TReason = never
|
||||
*
|
||||
* @implements PromiseInterface<TValue, TReason>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class FulfilledPromise implements PromiseInterface
|
||||
{
|
||||
/** @var TValue */
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param TValue $value
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
if (is_object($value) && method_exists($value, 'then')) {
|
||||
@@ -23,10 +36,21 @@ class FulfilledPromise implements PromiseInterface
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TFulfilledValue = never
|
||||
* @template TFulfilledReason = never
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @param (callable(TValue): (TFulfilledValue|PromiseInterface<TFulfilledValue, TFulfilledReason>))|null $onFulfilled Invoked when the promise fulfills.
|
||||
* @param (callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>))|null $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return ($onFulfilled is null ? self<TValue, TReason> : PromiseInterface<TFulfilledValue, TFulfilledReason|\Throwable>)
|
||||
*/
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
) {
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
): PromiseInterface {
|
||||
// Return itself if there is no onFulfilled function.
|
||||
if (!$onFulfilled) {
|
||||
return $this;
|
||||
@@ -35,14 +59,12 @@ class FulfilledPromise implements PromiseInterface
|
||||
$queue = Utils::queue();
|
||||
$p = new Promise([$queue, 'run']);
|
||||
$value = $this->value;
|
||||
$queue->add(static function () use ($p, $value, $onFulfilled) {
|
||||
if (Is::pending($p)) {
|
||||
$queue->add(static function () use ($p, $value, $onFulfilled): void {
|
||||
if (State::isPending($p)) {
|
||||
try {
|
||||
$p->resolve($onFulfilled($value));
|
||||
} catch (\Throwable $e) {
|
||||
$p->reject($e);
|
||||
} catch (\Exception $e) {
|
||||
$p->reject($e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -50,34 +72,39 @@ class FulfilledPromise implements PromiseInterface
|
||||
return $p;
|
||||
}
|
||||
|
||||
public function otherwise(callable $onRejected)
|
||||
/**
|
||||
* @param callable(TReason): mixed $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return self<TValue, TReason>
|
||||
*/
|
||||
public function otherwise(callable $onRejected): PromiseInterface
|
||||
{
|
||||
return $this->then(null, $onRejected);
|
||||
}
|
||||
|
||||
public function wait($unwrap = true, $defaultDelivery = null)
|
||||
public function wait(bool $unwrap = true)
|
||||
{
|
||||
return $unwrap ? $this->value : null;
|
||||
}
|
||||
|
||||
public function getState()
|
||||
public function getState(): string
|
||||
{
|
||||
return self::FULFILLED;
|
||||
}
|
||||
|
||||
public function resolve($value)
|
||||
public function resolve($value = null): void
|
||||
{
|
||||
if ($value !== $this->value) {
|
||||
throw new \LogicException("Cannot resolve a fulfilled promise");
|
||||
throw new \LogicException('Cannot resolve a fulfilled promise');
|
||||
}
|
||||
}
|
||||
|
||||
public function reject($reason)
|
||||
public function reject($reason): void
|
||||
{
|
||||
throw new \LogicException("Cannot reject a fulfilled promise");
|
||||
throw new \LogicException('Cannot reject a fulfilled promise');
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
public function cancel(): void
|
||||
{
|
||||
// pass
|
||||
}
|
||||
|
||||
+57
-12
@@ -1,46 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* @deprecated Is will be removed in guzzlehttp/promises:4.0. Use State instead.
|
||||
*/
|
||||
final class Is
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is pending.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated Is::pending() will be removed in guzzlehttp/promises:4.0. Use
|
||||
* State::isPending() instead.
|
||||
*/
|
||||
public static function pending(PromiseInterface $promise)
|
||||
public static function pending(PromiseInterface $promise): bool
|
||||
{
|
||||
return $promise->getState() === PromiseInterface::PENDING;
|
||||
\trigger_deprecation(
|
||||
'guzzlehttp/promises',
|
||||
'3.1',
|
||||
'%s() is deprecated and will be removed in 4.0. Use %s::isPending() instead.',
|
||||
__METHOD__,
|
||||
State::class
|
||||
);
|
||||
|
||||
return State::isPending($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is fulfilled or rejected.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated Is::settled() will be removed in guzzlehttp/promises:4.0. Use
|
||||
* State::isSettled() instead.
|
||||
*/
|
||||
public static function settled(PromiseInterface $promise)
|
||||
public static function settled(PromiseInterface $promise): bool
|
||||
{
|
||||
return $promise->getState() !== PromiseInterface::PENDING;
|
||||
\trigger_deprecation(
|
||||
'guzzlehttp/promises',
|
||||
'3.1',
|
||||
'%s() is deprecated and will be removed in 4.0. Use %s::isSettled() instead.',
|
||||
__METHOD__,
|
||||
State::class
|
||||
);
|
||||
|
||||
return State::isSettled($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is fulfilled.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated Is::fulfilled() will be removed in guzzlehttp/promises:4.0.
|
||||
* Use State::isFulfilled() instead.
|
||||
*/
|
||||
public static function fulfilled(PromiseInterface $promise)
|
||||
public static function fulfilled(PromiseInterface $promise): bool
|
||||
{
|
||||
return $promise->getState() === PromiseInterface::FULFILLED;
|
||||
\trigger_deprecation(
|
||||
'guzzlehttp/promises',
|
||||
'3.1',
|
||||
'%s() is deprecated and will be removed in 4.0. Use %s::isFulfilled() instead.',
|
||||
__METHOD__,
|
||||
State::class
|
||||
);
|
||||
|
||||
return State::isFulfilled($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is rejected.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated Is::rejected() will be removed in guzzlehttp/promises:4.0.
|
||||
* Use State::isRejected() instead.
|
||||
*/
|
||||
public static function rejected(PromiseInterface $promise)
|
||||
public static function rejected(PromiseInterface $promise): bool
|
||||
{
|
||||
return $promise->getState() === PromiseInterface::REJECTED;
|
||||
\trigger_deprecation(
|
||||
'guzzlehttp/promises',
|
||||
'3.1',
|
||||
'%s() is deprecated and will be removed in 4.0. Use %s::isRejected() instead.',
|
||||
__METHOD__,
|
||||
State::class
|
||||
);
|
||||
|
||||
return State::isRejected($promise);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait NonSerializableTrait
|
||||
{
|
||||
public function __serialize(): array
|
||||
{
|
||||
throw new \LogicException(static::class.' should never be serialized');
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
throw new \LogicException(static::class.' should never be unserialized');
|
||||
}
|
||||
}
|
||||
+83
-38
@@ -1,63 +1,110 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* Promises/A+ implementation that avoids recursion when possible.
|
||||
*
|
||||
* @link https://promisesaplus.com/
|
||||
* @template TValue = mixed
|
||||
* @template TReason = mixed
|
||||
*
|
||||
* @implements PromiseInterface<TValue, TReason>
|
||||
*
|
||||
* @see https://promisesaplus.com/
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class Promise implements PromiseInterface
|
||||
{
|
||||
private $state = self::PENDING;
|
||||
use NonSerializableTrait;
|
||||
|
||||
/** @var self::PENDING|self::FULFILLED|self::REJECTED */
|
||||
private string $state = self::PENDING;
|
||||
|
||||
/** @var TValue|TReason|PromiseInterface<TValue, TReason>|null */
|
||||
private $result;
|
||||
|
||||
/** @var (callable(): void)|null */
|
||||
private $cancelFn;
|
||||
|
||||
/** @var (callable(bool): void)|null */
|
||||
private $waitFn;
|
||||
private $waitList;
|
||||
private $handlers = [];
|
||||
|
||||
/** @var list<Promise<mixed, mixed>>|null */
|
||||
private ?array $waitList = null;
|
||||
|
||||
/** @var list<array{0: PromiseInterface<mixed, mixed>, 1: callable|null, 2: callable|null}>|null */
|
||||
private ?array $handlers = [];
|
||||
|
||||
/**
|
||||
* @param callable $waitFn Fn that when invoked resolves the promise.
|
||||
* @param callable $cancelFn Fn that when invoked cancels the promise.
|
||||
* @param (callable(bool): void)|null $waitFn Fn that when invoked resolves the promise.
|
||||
* @param (callable(): void)|null $cancelFn Fn that when invoked cancels the promise.
|
||||
*/
|
||||
public function __construct(
|
||||
callable $waitFn = null,
|
||||
callable $cancelFn = null
|
||||
?callable $waitFn = null,
|
||||
?callable $cancelFn = null
|
||||
) {
|
||||
$this->waitFn = $waitFn;
|
||||
$this->cancelFn = $cancelFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TFulfilledValue = never
|
||||
* @template TFulfilledReason = never
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @param (callable(TValue): (TFulfilledValue|PromiseInterface<TFulfilledValue, TFulfilledReason>))|null $onFulfilled Invoked when the promise fulfills.
|
||||
* @param (callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>))|null $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return PromiseInterface<($onFulfilled is null ? TValue : TFulfilledValue)|($onRejected is null ? never : TRejectedValue), ($onFulfilled is null ? never : TFulfilledReason|\Throwable)|($onRejected is null ? TReason : TRejectedReason|\Throwable)>
|
||||
*/
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
) {
|
||||
?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;
|
||||
|
||||
return $promise->then($onFulfilled, $onRejected);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
/** @var PromiseInterface<($onFulfilled is null ? TValue : TFulfilledValue)|($onRejected is null ? never : TRejectedValue), ($onFulfilled is null ? never : TFulfilledReason|\Throwable)|($onRejected is null ? TReason : TRejectedReason|\Throwable)> $promise */
|
||||
$promise = $onRejected ? $rejection->then(null, $onRejected) : $rejection;
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
public function otherwise(callable $onRejected)
|
||||
/**
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @param callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>) $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return PromiseInterface<TValue|TRejectedValue, TRejectedReason|\Throwable>
|
||||
*/
|
||||
public function otherwise(callable $onRejected): PromiseInterface
|
||||
{
|
||||
return $this->then(null, $onRejected);
|
||||
}
|
||||
|
||||
public function wait($unwrap = true)
|
||||
public function wait(bool $unwrap = true)
|
||||
{
|
||||
$this->waitIfPending();
|
||||
|
||||
@@ -71,14 +118,16 @@ class Promise implements PromiseInterface
|
||||
// It's rejected so "unwrap" and throw an exception.
|
||||
throw Create::exceptionFor($this->result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getState()
|
||||
public function getState(): string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
public function cancel(): void
|
||||
{
|
||||
if ($this->state !== self::PENDING) {
|
||||
return;
|
||||
@@ -93,8 +142,6 @@ class Promise implements PromiseInterface
|
||||
$fn();
|
||||
} catch (\Throwable $e) {
|
||||
$this->reject($e);
|
||||
} catch (\Exception $e) {
|
||||
$this->reject($e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,17 +152,17 @@ class Promise implements PromiseInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function resolve($value)
|
||||
public function resolve($value = null): void
|
||||
{
|
||||
$this->settle(self::FULFILLED, $value);
|
||||
}
|
||||
|
||||
public function reject($reason)
|
||||
public function reject($reason): void
|
||||
{
|
||||
$this->settle(self::REJECTED, $reason);
|
||||
}
|
||||
|
||||
private function settle($state, $value)
|
||||
private function settle(string $state, $value): void
|
||||
{
|
||||
if ($this->state !== self::PENDING) {
|
||||
// Ignore calls with the same resolution.
|
||||
@@ -148,23 +195,23 @@ class Promise implements PromiseInterface
|
||||
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) {
|
||||
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)) {
|
||||
} elseif ($value instanceof Promise && State::isPending($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) {
|
||||
static function ($value) use ($handlers): void {
|
||||
foreach ($handlers as $handler) {
|
||||
self::callHandler(1, $value, $handler);
|
||||
}
|
||||
},
|
||||
static function ($reason) use ($handlers) {
|
||||
static function ($reason) use ($handlers): void {
|
||||
foreach ($handlers as $handler) {
|
||||
self::callHandler(2, $reason, $handler);
|
||||
}
|
||||
@@ -180,14 +227,14 @@ class Promise implements PromiseInterface
|
||||
* @param mixed $value Value to pass to the callback.
|
||||
* @param array $handler Array of handler data (promise and callbacks).
|
||||
*/
|
||||
private static function callHandler($index, $value, array $handler)
|
||||
private static function callHandler(int $index, $value, array $handler): void
|
||||
{
|
||||
/** @var PromiseInterface $promise */
|
||||
/** @var PromiseInterface<mixed, mixed> $promise */
|
||||
$promise = $handler[0];
|
||||
|
||||
// The promise may have been cancelled or resolved before placing
|
||||
// this thunk in the queue.
|
||||
if (Is::settled($promise)) {
|
||||
if (State::isSettled($promise)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -211,12 +258,10 @@ class Promise implements PromiseInterface
|
||||
}
|
||||
} catch (\Throwable $reason) {
|
||||
$promise->reject($reason);
|
||||
} catch (\Exception $reason) {
|
||||
$promise->reject($reason);
|
||||
}
|
||||
}
|
||||
|
||||
private function waitIfPending()
|
||||
private function waitIfPending(): void
|
||||
{
|
||||
if ($this->state !== self::PENDING) {
|
||||
return;
|
||||
@@ -227,9 +272,9 @@ class Promise implements PromiseInterface
|
||||
} 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.');
|
||||
.'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();
|
||||
@@ -240,13 +285,13 @@ class Promise implements PromiseInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function invokeWaitFn()
|
||||
private function invokeWaitFn(): void
|
||||
{
|
||||
try {
|
||||
$wfn = $this->waitFn;
|
||||
$this->waitFn = null;
|
||||
$wfn(true);
|
||||
} catch (\Exception $reason) {
|
||||
} catch (\Throwable $reason) {
|
||||
if ($this->state === self::PENDING) {
|
||||
// The promise has not been resolved yet, so reject the promise
|
||||
// with the exception.
|
||||
@@ -259,7 +304,7 @@ class Promise implements PromiseInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function invokeWaitList()
|
||||
private function invokeWaitList(): void
|
||||
{
|
||||
$waitList = $this->waitList;
|
||||
$this->waitList = null;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
@@ -9,27 +11,35 @@ namespace GuzzleHttp\Promise;
|
||||
* which registers callbacks to receive either a promise’s eventual value or
|
||||
* the reason why the promise cannot be fulfilled.
|
||||
*
|
||||
* @link https://promisesaplus.com/
|
||||
* @template TValue = mixed
|
||||
* @template TReason = mixed
|
||||
*
|
||||
* @see https://promisesaplus.com/
|
||||
*/
|
||||
interface PromiseInterface
|
||||
{
|
||||
const PENDING = 'pending';
|
||||
const FULFILLED = 'fulfilled';
|
||||
const REJECTED = 'rejected';
|
||||
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.
|
||||
* @template TFulfilledValue = never
|
||||
* @template TFulfilledReason = never
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param (callable(TValue): (TFulfilledValue|PromiseInterface<TFulfilledValue, TFulfilledReason>))|null $onFulfilled Invoked when the promise fulfills.
|
||||
* @param (callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>))|null $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return PromiseInterface<($onFulfilled is null ? TValue : TFulfilledValue)|($onRejected is null ? never : TRejectedValue), ($onFulfilled is null ? never : TFulfilledReason|\Throwable)|($onRejected is null ? TReason : TRejectedReason|\Throwable)>
|
||||
*/
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
);
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
): PromiseInterface;
|
||||
|
||||
/**
|
||||
* Appends a rejection handler callback to the promise, and returns a new
|
||||
@@ -37,11 +47,14 @@ interface PromiseInterface
|
||||
* or to its original fulfillment value if the promise is instead
|
||||
* fulfilled.
|
||||
*
|
||||
* @param callable $onRejected Invoked when the promise is rejected.
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>) $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return PromiseInterface<TValue|TRejectedValue, TRejectedReason|\Throwable>
|
||||
*/
|
||||
public function otherwise(callable $onRejected);
|
||||
public function otherwise(callable $onRejected): PromiseInterface;
|
||||
|
||||
/**
|
||||
* Get the state of the promise ("pending", "rejected", or "fulfilled").
|
||||
@@ -49,34 +62,45 @@ interface PromiseInterface
|
||||
* The three states can be checked against the constants defined on
|
||||
* PromiseInterface: PENDING, FULFILLED, and REJECTED.
|
||||
*
|
||||
* @return string
|
||||
* The state describes how this promise was settled, not the eventual
|
||||
* outcome: a promise that was resolved with another promise, directly or
|
||||
* by returning one from a then() handler, reports FULFILLED while the
|
||||
* inner promise may still be pending, and wait() can still throw if the
|
||||
* inner promise rejects. Poll the state only on promises settled with
|
||||
* plain values, or call wait() first.
|
||||
*
|
||||
* @return self::PENDING|self::FULFILLED|self::REJECTED
|
||||
*
|
||||
* @see https://github.com/guzzle/promises/issues/101
|
||||
*/
|
||||
public function getState();
|
||||
public function getState(): string;
|
||||
|
||||
/**
|
||||
* Resolve the promise with the given value.
|
||||
* Resolve the promise with the given value, or with null if no value is given.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param TValue|PromiseInterface<TValue, TReason>|null $value
|
||||
*
|
||||
* @throws \RuntimeException if the promise is already resolved.
|
||||
* @throws \LogicException if the promise is already settled with a
|
||||
* conflicting resolution.
|
||||
*/
|
||||
public function resolve($value);
|
||||
public function resolve($value = null): void;
|
||||
|
||||
/**
|
||||
* Reject the promise with the given reason.
|
||||
*
|
||||
* @param mixed $reason
|
||||
* @param TReason $reason
|
||||
*
|
||||
* @throws \RuntimeException if the promise is already resolved.
|
||||
* @throws \LogicException if the promise is already settled with a
|
||||
* conflicting resolution.
|
||||
*/
|
||||
public function reject($reason);
|
||||
public function reject($reason): void;
|
||||
|
||||
/**
|
||||
* Cancels the promise if possible.
|
||||
*
|
||||
* @link https://github.com/promises-aplus/cancellation-spec/issues/7
|
||||
* @see https://github.com/promises-aplus/cancellation-spec/issues/7
|
||||
*/
|
||||
public function cancel();
|
||||
public function cancel(): void;
|
||||
|
||||
/**
|
||||
* Waits until the promise completes if possible.
|
||||
@@ -86,12 +110,10 @@ interface PromiseInterface
|
||||
*
|
||||
* If the promise cannot be waited on, then the promise will be rejected.
|
||||
*
|
||||
* @param bool $unwrap
|
||||
*
|
||||
* @return mixed
|
||||
* @return ($unwrap is true ? TValue : null)
|
||||
*
|
||||
* @throws \LogicException if the promise has no wait function or if the
|
||||
* promise does not settle after waiting.
|
||||
*/
|
||||
public function wait($unwrap = true);
|
||||
public function wait(bool $unwrap = true);
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* Interface used with classes that return a promise.
|
||||
*
|
||||
* @template TValue = mixed
|
||||
* @template TReason = mixed
|
||||
*/
|
||||
interface PromisorInterface
|
||||
{
|
||||
/**
|
||||
* Returns a promise.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @return PromiseInterface<TValue, TReason>
|
||||
*/
|
||||
public function promise();
|
||||
public function promise(): PromiseInterface;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
@@ -7,11 +9,22 @@ namespace GuzzleHttp\Promise;
|
||||
*
|
||||
* Thenning off of this promise will invoke the onRejected callback
|
||||
* immediately and ignore other callbacks.
|
||||
*
|
||||
* @template TValue = never
|
||||
* @template TReason = mixed
|
||||
*
|
||||
* @implements PromiseInterface<TValue, TReason>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class RejectedPromise implements PromiseInterface
|
||||
{
|
||||
/** @var TReason */
|
||||
private $reason;
|
||||
|
||||
/**
|
||||
* @param TReason $reason
|
||||
*/
|
||||
public function __construct($reason)
|
||||
{
|
||||
if (is_object($reason) && method_exists($reason, 'then')) {
|
||||
@@ -23,10 +36,21 @@ class RejectedPromise implements PromiseInterface
|
||||
$this->reason = $reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TFulfilledValue = never
|
||||
* @template TFulfilledReason = never
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @param (callable(TValue): (TFulfilledValue|PromiseInterface<TFulfilledValue, TFulfilledReason>))|null $onFulfilled Invoked when the promise fulfills.
|
||||
* @param (callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>))|null $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return ($onRejected is null ? self<TValue, TReason> : PromiseInterface<TRejectedValue, TRejectedReason|\Throwable>)
|
||||
*/
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
) {
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
): PromiseInterface {
|
||||
// If there's no onRejected callback then just return self.
|
||||
if (!$onRejected) {
|
||||
return $this;
|
||||
@@ -35,17 +59,14 @@ class RejectedPromise implements PromiseInterface
|
||||
$queue = Utils::queue();
|
||||
$reason = $this->reason;
|
||||
$p = new Promise([$queue, 'run']);
|
||||
$queue->add(static function () use ($p, $reason, $onRejected) {
|
||||
if (Is::pending($p)) {
|
||||
$queue->add(static function () use ($p, $reason, $onRejected): void {
|
||||
if (State::isPending($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);
|
||||
} catch (\Exception $e) {
|
||||
// onRejected threw, so return a rejected promise.
|
||||
$p->reject($e);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -53,12 +74,20 @@ class RejectedPromise implements PromiseInterface
|
||||
return $p;
|
||||
}
|
||||
|
||||
public function otherwise(callable $onRejected)
|
||||
/**
|
||||
* @template TRejectedValue = never
|
||||
* @template TRejectedReason = never
|
||||
*
|
||||
* @param callable(TReason): (TRejectedValue|PromiseInterface<TRejectedValue, TRejectedReason>) $onRejected Invoked when the promise is rejected.
|
||||
*
|
||||
* @return PromiseInterface<TRejectedValue, TRejectedReason|\Throwable>
|
||||
*/
|
||||
public function otherwise(callable $onRejected): PromiseInterface
|
||||
{
|
||||
return $this->then(null, $onRejected);
|
||||
}
|
||||
|
||||
public function wait($unwrap = true, $defaultDelivery = null)
|
||||
public function wait(bool $unwrap = true)
|
||||
{
|
||||
if ($unwrap) {
|
||||
throw Create::exceptionFor($this->reason);
|
||||
@@ -67,24 +96,24 @@ class RejectedPromise implements PromiseInterface
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getState()
|
||||
public function getState(): string
|
||||
{
|
||||
return self::REJECTED;
|
||||
}
|
||||
|
||||
public function resolve($value)
|
||||
public function resolve($value = null): void
|
||||
{
|
||||
throw new \LogicException("Cannot resolve a rejected promise");
|
||||
throw new \LogicException('Cannot resolve a rejected promise');
|
||||
}
|
||||
|
||||
public function reject($reason)
|
||||
public function reject($reason): void
|
||||
{
|
||||
if ($reason !== $this->reason) {
|
||||
throw new \LogicException("Cannot reject a rejected promise");
|
||||
throw new \LogicException('Cannot reject a rejected promise');
|
||||
}
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
public function cancel(): void
|
||||
{
|
||||
// pass
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
@@ -13,24 +15,23 @@ class RejectionException extends \RuntimeException
|
||||
private $reason;
|
||||
|
||||
/**
|
||||
* @param mixed $reason Rejection reason.
|
||||
* @param string $description Optional description
|
||||
* @param mixed $reason Rejection reason.
|
||||
* @param string|null $description Optional description.
|
||||
*/
|
||||
public function __construct($reason, $description = null)
|
||||
public function __construct($reason, ?string $description = null)
|
||||
{
|
||||
$this->reason = $reason;
|
||||
|
||||
$message = 'The promise was rejected';
|
||||
|
||||
if ($description) {
|
||||
$message .= ' with reason: ' . $description;
|
||||
$message .= ' with reason: '.$description;
|
||||
} elseif (is_string($reason)
|
||||
|| (is_object($reason) && method_exists($reason, '__toString'))
|
||||
) {
|
||||
$message .= ' with reason: ' . $this->reason;
|
||||
$message .= ' with reason: '.$this->reason;
|
||||
} elseif ($reason instanceof \JsonSerializable) {
|
||||
$message .= ' with reason: '
|
||||
. json_encode($this->reason, JSON_PRETTY_PRINT);
|
||||
$message .= ' with reason: '.json_encode($this->reason, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
parent::__construct($message);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
final class State
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is pending.
|
||||
*/
|
||||
public static function isPending(PromiseInterface $promise): bool
|
||||
{
|
||||
return $promise->getState() === PromiseInterface::PENDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is fulfilled or rejected.
|
||||
*/
|
||||
public static function isSettled(PromiseInterface $promise): bool
|
||||
{
|
||||
return $promise->getState() !== PromiseInterface::PENDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is fulfilled.
|
||||
*/
|
||||
public static function isFulfilled(PromiseInterface $promise): bool
|
||||
{
|
||||
return $promise->getState() === PromiseInterface::FULFILLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is rejected.
|
||||
*/
|
||||
public static function isRejected(PromiseInterface $promise): bool
|
||||
{
|
||||
return $promise->getState() === PromiseInterface::REJECTED;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
@@ -10,16 +12,21 @@ namespace GuzzleHttp\Promise;
|
||||
* 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 = [];
|
||||
use NonSerializableTrait;
|
||||
|
||||
public function __construct($withShutdown = true)
|
||||
private bool $enableShutdown = true;
|
||||
/** @var list<callable(): void> */
|
||||
private array $queue = [];
|
||||
|
||||
public function __construct(bool $withShutdown = true)
|
||||
{
|
||||
if ($withShutdown) {
|
||||
register_shutdown_function(function () {
|
||||
register_shutdown_function(function (): void {
|
||||
if ($this->enableShutdown) {
|
||||
// Only run the tasks if an E_ERROR didn't occur.
|
||||
$err = error_get_last();
|
||||
@@ -31,20 +38,23 @@ class TaskQueue implements TaskQueueInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return !$this->queue;
|
||||
}
|
||||
|
||||
public function add(callable $task)
|
||||
/**
|
||||
* @param callable(): void $task
|
||||
*/
|
||||
public function add(callable $task): void
|
||||
{
|
||||
$this->queue[] = $task;
|
||||
}
|
||||
|
||||
public function run()
|
||||
public function run(): void
|
||||
{
|
||||
while ($task = array_shift($this->queue)) {
|
||||
/** @var callable $task */
|
||||
/** @var callable(): void $task */
|
||||
$task();
|
||||
}
|
||||
}
|
||||
@@ -60,7 +70,7 @@ class TaskQueue implements TaskQueueInterface
|
||||
*
|
||||
* Note: This shutdown will occur before any destructors are triggered.
|
||||
*/
|
||||
public function disableShutdown()
|
||||
public function disableShutdown(): void
|
||||
{
|
||||
$this->enableShutdown = false;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
interface TaskQueueInterface
|
||||
{
|
||||
/**
|
||||
* Returns true if the queue is empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty();
|
||||
public function isEmpty(): bool;
|
||||
|
||||
/**
|
||||
* Adds a task to the queue that will be executed the next time run is
|
||||
* called.
|
||||
*
|
||||
* @param callable(): void $task
|
||||
*/
|
||||
public function add(callable $task);
|
||||
public function add(callable $task): void;
|
||||
|
||||
/**
|
||||
* Execute all of the pending task in the queue.
|
||||
*/
|
||||
public function run();
|
||||
public function run(): void;
|
||||
}
|
||||
+186
-72
@@ -1,9 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
final class Utils
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the global task queue used for promise resolution.
|
||||
*
|
||||
@@ -17,11 +23,9 @@ final class Utils
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @param TaskQueueInterface $assign Optionally specify a new queue instance.
|
||||
*
|
||||
* @return TaskQueueInterface
|
||||
* @param TaskQueueInterface|null $assign Optionally specify a new queue instance.
|
||||
*/
|
||||
public static function queue(TaskQueueInterface $assign = null)
|
||||
public static function queue(?TaskQueueInterface $assign = null): TaskQueueInterface
|
||||
{
|
||||
static $queue;
|
||||
|
||||
@@ -35,26 +39,26 @@ final class Utils
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Adds a task to the global queue and returns a promise that is fulfilled
|
||||
* or rejected with the task result.
|
||||
*
|
||||
* @param callable $task Task function to run.
|
||||
* @template TValue
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param callable(): TValue $task Task function to run.
|
||||
*
|
||||
* @return PromiseInterface<TValue, \Throwable>
|
||||
*/
|
||||
public static function task(callable $task)
|
||||
public static function task(callable $task): PromiseInterface
|
||||
{
|
||||
$queue = self::queue();
|
||||
$promise = new Promise([$queue, 'run']);
|
||||
$queue->add(function () use ($task, $promise) {
|
||||
$queue->add(function () use ($task, $promise): void {
|
||||
try {
|
||||
if (Is::pending($promise)) {
|
||||
if (State::isPending($promise)) {
|
||||
$promise->resolve($task());
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$promise->reject($e);
|
||||
} catch (\Exception $e) {
|
||||
$promise->reject($e);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -62,7 +66,7 @@ final class Utils
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously waits on a promise to resolve and returns an inspection
|
||||
* Synchronously waits on a promise to settle and returns an inspection
|
||||
* state array.
|
||||
*
|
||||
* Returns a state associative array containing a "state" key mapping to a
|
||||
@@ -71,43 +75,78 @@ final class Utils
|
||||
* 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.
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @return array
|
||||
* @param PromiseInterface<TValue, TReason> $promise Promise to inspect.
|
||||
*
|
||||
* @return array{state: PromiseInterface::FULFILLED, value: TValue}|array{state: PromiseInterface::REJECTED, reason: TReason|\Throwable}|array{state: PromiseInterface::PENDING}
|
||||
*/
|
||||
public static function inspect(PromiseInterface $promise)
|
||||
public static function inspect(PromiseInterface $promise): array
|
||||
{
|
||||
$result = null;
|
||||
$getResult = static function () use (&$result): ?array {
|
||||
return $result;
|
||||
};
|
||||
|
||||
$inspection = $promise->then(
|
||||
static function ($value) use (&$result): void {
|
||||
$result = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
|
||||
},
|
||||
static function ($reason) use (&$result): void {
|
||||
$result = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
return [
|
||||
'state' => PromiseInterface::FULFILLED,
|
||||
'value' => $promise->wait()
|
||||
];
|
||||
} catch (RejectionException $e) {
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
|
||||
$inspection->wait(false);
|
||||
} catch (\Throwable $e) {
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
|
||||
} catch (\Exception $e) {
|
||||
$settled = $getResult();
|
||||
if (null !== $settled) {
|
||||
return $settled;
|
||||
}
|
||||
|
||||
if (State::isSettled($promise)) {
|
||||
try {
|
||||
self::queue()->run();
|
||||
} catch (\Throwable $queueError) {
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $queueError];
|
||||
}
|
||||
|
||||
$settled = $getResult();
|
||||
if (null !== $settled) {
|
||||
return $settled;
|
||||
}
|
||||
}
|
||||
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
|
||||
}
|
||||
|
||||
return $getResult() ?? ['state' => $promise->getState()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits on all of the provided promises, but does not unwrap rejected
|
||||
* promises as thrown exception.
|
||||
* promises as a thrown exception.
|
||||
*
|
||||
* Returns an array of inspection state arrays.
|
||||
* Returns an array of inspection state arrays keyed like the input
|
||||
* iterable.
|
||||
*
|
||||
* @see inspect for the inspection state array format.
|
||||
*
|
||||
* @param PromiseInterface[] $promises Traversable of promises to wait upon.
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @return array
|
||||
* @param iterable<TKey, PromiseInterface<TValue, TReason>> $promises Traversable of promises to wait upon.
|
||||
*
|
||||
* @return array<TKey, array{state: PromiseInterface::FULFILLED, value: TValue}|array{state: PromiseInterface::REJECTED, reason: TReason|\Throwable}|array{state: PromiseInterface::PENDING}>
|
||||
*/
|
||||
public static function inspectAll($promises)
|
||||
public static function inspectAll(iterable $promises): array
|
||||
{
|
||||
$results = [];
|
||||
foreach ($promises as $key => $promise) {
|
||||
$results[$key] = inspect($promise);
|
||||
$results[$key] = self::inspect($promise);
|
||||
}
|
||||
|
||||
return $results;
|
||||
@@ -120,14 +159,17 @@ final class Utils
|
||||
* 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.
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @return array
|
||||
* @param iterable<TKey, PromiseInterface<TValue, TReason>> $promises Iterable of PromiseInterface objects to wait on.
|
||||
*
|
||||
* @throws \Exception on error
|
||||
* @throws \Throwable on error in PHP >=7
|
||||
* @return array<TKey, TValue>
|
||||
*
|
||||
* @throws \Throwable on error
|
||||
*/
|
||||
public static function unwrap($promises)
|
||||
public static function unwrap(iterable $promises): array
|
||||
{
|
||||
$results = [];
|
||||
foreach ($promises as $key => $promise) {
|
||||
@@ -145,34 +187,45 @@ final class Utils
|
||||
* 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.
|
||||
* The config array accepts a concurrency option for lazy iterables. Other
|
||||
* config keys are ignored by this wrapper.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @param iterable<TKey, TValue|PromiseInterface<TValue, TReason>> $promises Promises or values.
|
||||
* @param bool $recursive If true, resolves newly-added entries until no unprocessed entries or pending promises remain.
|
||||
* @param array{concurrency?: int|(callable(int): int)} $config Configuration options.
|
||||
*
|
||||
* @return PromiseInterface<array<TKey, TValue>, TReason|\Throwable>
|
||||
*/
|
||||
public static function all($promises, $recursive = false)
|
||||
public static function all(iterable $promises, bool $recursive = false, array $config = []): PromiseInterface
|
||||
{
|
||||
$results = [];
|
||||
$promise = Each::of(
|
||||
$promises,
|
||||
function ($value, $idx) use (&$results) {
|
||||
function ($value, $idx) use (&$results): void {
|
||||
$results[$idx] = $value;
|
||||
},
|
||||
function ($reason, $idx, Promise $aggregate) {
|
||||
$aggregate->reject($reason);
|
||||
}
|
||||
function ($reason, $idx, PromiseInterface $aggregate): void {
|
||||
if (State::isPending($aggregate)) {
|
||||
$aggregate->reject($reason);
|
||||
}
|
||||
},
|
||||
$config
|
||||
)->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);
|
||||
}
|
||||
$promise = $promise->then(function ($results) use (&$promises, $config) {
|
||||
if (self::shouldRecurse($promises, $results)) {
|
||||
return self::all($promises, true, $config);
|
||||
}
|
||||
|
||||
return $results;
|
||||
});
|
||||
}
|
||||
@@ -186,25 +239,28 @@ final class Utils
|
||||
*
|
||||
* 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.
|
||||
* winners, in the order they appear in the input.
|
||||
*
|
||||
* 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.
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param int $count Total number of promises.
|
||||
* @param iterable<TValue|PromiseInterface<TValue, TReason>> $promises Promises or values.
|
||||
*
|
||||
* @return PromiseInterface<list<TValue>, \Throwable>
|
||||
*/
|
||||
public static function some($count, $promises)
|
||||
public static function some(int $count, iterable $promises): PromiseInterface
|
||||
{
|
||||
$results = [];
|
||||
$rejections = [];
|
||||
|
||||
return Each::of(
|
||||
$promise = Each::of(
|
||||
$promises,
|
||||
function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
|
||||
if (Is::settled($p)) {
|
||||
function ($value, $idx, PromiseInterface $p) use (&$results, $count): void {
|
||||
if (State::isSettled($p)) {
|
||||
return;
|
||||
}
|
||||
$results[$idx] = $value;
|
||||
@@ -212,7 +268,7 @@ final class Utils
|
||||
$p->resolve(null);
|
||||
}
|
||||
},
|
||||
function ($reason) use (&$rejections) {
|
||||
function ($reason) use (&$rejections): void {
|
||||
$rejections[] = $reason;
|
||||
}
|
||||
)->then(
|
||||
@@ -224,53 +280,111 @@ final class Utils
|
||||
);
|
||||
}
|
||||
ksort($results);
|
||||
|
||||
return array_values($results);
|
||||
}
|
||||
);
|
||||
|
||||
/** @var PromiseInterface<list<TValue>, \Throwable> $promise */
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param iterable<TValue|PromiseInterface<TValue, TReason>> $promises Promises or values.
|
||||
*
|
||||
* @return PromiseInterface<TValue, \Throwable>
|
||||
*/
|
||||
public static function any($promises)
|
||||
public static function any(iterable $promises): PromiseInterface
|
||||
{
|
||||
return self::some(1, $promises)->then(function ($values) {
|
||||
return self::some(1, $promises)->then(function (array $values) {
|
||||
return $values[0];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that is fulfilled when all of the provided promises have
|
||||
* been fulfilled or rejected.
|
||||
* 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.
|
||||
* The returned promise is fulfilled with an array of inspection state
|
||||
* arrays.
|
||||
*
|
||||
* The config array accepts a concurrency option for lazy iterables. Other
|
||||
* config keys are ignored by this wrapper.
|
||||
*
|
||||
* @see inspect for the inspection state array format.
|
||||
*
|
||||
* @param mixed $promises Promises or values.
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @param iterable<TKey, TValue|PromiseInterface<TValue, TReason>> $promises Promises or values.
|
||||
* @param bool $recursive If true, settles newly-added entries until no unprocessed entries or pending promises remain.
|
||||
* @param array{concurrency?: int|(callable(int): int)} $config Configuration options.
|
||||
*
|
||||
* @return PromiseInterface<array<TKey, array{state: PromiseInterface::FULFILLED, value: TValue}|array{state: PromiseInterface::REJECTED, reason: TReason|\Throwable}>, \Throwable>
|
||||
*/
|
||||
public static function settle($promises)
|
||||
public static function settle(iterable $promises, bool $recursive = false, array $config = []): PromiseInterface
|
||||
{
|
||||
$results = [];
|
||||
|
||||
return Each::of(
|
||||
$promise = Each::of(
|
||||
$promises,
|
||||
function ($value, $idx) use (&$results) {
|
||||
function ($value, $idx) use (&$results): void {
|
||||
$results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
|
||||
},
|
||||
function ($reason, $idx) use (&$results) {
|
||||
function ($reason, $idx) use (&$results): void {
|
||||
$results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
|
||||
}
|
||||
},
|
||||
$config
|
||||
)->then(function () use (&$results) {
|
||||
ksort($results);
|
||||
|
||||
return $results;
|
||||
});
|
||||
|
||||
if (true === $recursive) {
|
||||
$promise = $promise->then(function ($results) use (&$promises, $config) {
|
||||
if (self::shouldRecurse($promises, $results)) {
|
||||
return self::settle($promises, true, $config);
|
||||
}
|
||||
|
||||
return $results;
|
||||
});
|
||||
}
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TKey of array-key
|
||||
*
|
||||
* @param iterable<TKey, mixed> $promises Promises or values.
|
||||
* @param array<TKey, mixed> $results Results already collected for a pass.
|
||||
*/
|
||||
private static function shouldRecurse(iterable $promises, array $results): bool
|
||||
{
|
||||
// A consumed generator cannot be traversed again, so a recursive
|
||||
// pass has nothing further to observe.
|
||||
if ($promises instanceof \Generator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($promises as $key => $promise) {
|
||||
if (!array_key_exists($key, $results)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($promise instanceof PromiseInterface && State::isPending($promise)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,363 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* 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\queue()->run();
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @param TaskQueueInterface $assign Optionally specify a new queue instance.
|
||||
*
|
||||
* @return TaskQueueInterface
|
||||
*
|
||||
* @deprecated queue will be removed in guzzlehttp/promises:2.0. Use Utils::queue instead.
|
||||
*/
|
||||
function queue(TaskQueueInterface $assign = null)
|
||||
{
|
||||
return Utils::queue($assign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated task will be removed in guzzlehttp/promises:2.0. Use Utils::task instead.
|
||||
*/
|
||||
function task(callable $task)
|
||||
{
|
||||
return Utils::task($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a promise for a value if the value is not a promise.
|
||||
*
|
||||
* @param mixed $value Promise or value.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead.
|
||||
*/
|
||||
function promise_for($value)
|
||||
{
|
||||
return Create::promiseFor($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.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.
|
||||
*/
|
||||
function rejection_for($reason)
|
||||
{
|
||||
return Create::rejectionFor($reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exception for a rejected promise value.
|
||||
*
|
||||
* @param mixed $reason
|
||||
*
|
||||
* @return \Exception|\Throwable
|
||||
*
|
||||
* @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead.
|
||||
*/
|
||||
function exception_for($reason)
|
||||
{
|
||||
return Create::exceptionFor($reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator for the given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return \Iterator
|
||||
*
|
||||
* @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead.
|
||||
*/
|
||||
function iter_for($value)
|
||||
{
|
||||
return Create::iterFor($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead.
|
||||
*/
|
||||
function inspect(PromiseInterface $promise)
|
||||
{
|
||||
return Utils::inspect($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead.
|
||||
*/
|
||||
function inspect_all($promises)
|
||||
{
|
||||
return Utils::inspectAll($promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception on error
|
||||
* @throws \Throwable on error in PHP >=7
|
||||
*
|
||||
* @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead.
|
||||
*/
|
||||
function unwrap($promises)
|
||||
{
|
||||
return Utils::unwrap($promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead.
|
||||
*/
|
||||
function all($promises, $recursive = false)
|
||||
{
|
||||
return Utils::all($promises, $recursive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead.
|
||||
*/
|
||||
function some($count, $promises)
|
||||
{
|
||||
return Utils::some($count, $promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead.
|
||||
*/
|
||||
function any($promises)
|
||||
{
|
||||
return Utils::any($promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead.
|
||||
*/
|
||||
function settle($promises)
|
||||
{
|
||||
return Utils::settle($promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead.
|
||||
*/
|
||||
function each(
|
||||
$iterable,
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
) {
|
||||
return Each::of($iterable, $onFulfilled, $onRejected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like each, 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
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead.
|
||||
*/
|
||||
function each_limit(
|
||||
$iterable,
|
||||
$concurrency,
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null
|
||||
) {
|
||||
return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like each_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
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead.
|
||||
*/
|
||||
function each_limit_all(
|
||||
$iterable,
|
||||
$concurrency,
|
||||
callable $onFulfilled = null
|
||||
) {
|
||||
return Each::ofLimitAll($iterable, $concurrency, $onFulfilled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is fulfilled.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead.
|
||||
*/
|
||||
function is_fulfilled(PromiseInterface $promise)
|
||||
{
|
||||
return Is::fulfilled($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is rejected.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead.
|
||||
*/
|
||||
function is_rejected(PromiseInterface $promise)
|
||||
{
|
||||
return Is::rejected($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is fulfilled or rejected.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead.
|
||||
*/
|
||||
function is_settled(PromiseInterface $promise)
|
||||
{
|
||||
return Is::settled($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new coroutine.
|
||||
*
|
||||
* @see Coroutine
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*
|
||||
* @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead.
|
||||
*/
|
||||
function coroutine(callable $generatorFn)
|
||||
{
|
||||
return Coroutine::of($generatorFn);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Don't redefine the functions if included multiple times.
|
||||
if (!function_exists('GuzzleHttp\Promise\promise_for')) {
|
||||
require __DIR__ . '/functions.php';
|
||||
}
|
||||
Reference in new issue
Block a user