v2.1.7
This commit is contained in:
parent
c2ead8e434
commit
77d0cea926
995 changed files with 28142 additions and 8603 deletions
45
vendor/guzzlehttp/promises/CHANGELOG.md
vendored
45
vendor/guzzlehttp/promises/CHANGELOG.md
vendored
|
|
@ -1,6 +1,51 @@
|
|||
# CHANGELOG
|
||||
|
||||
|
||||
## 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
|
||||
|
|
|
|||
535
vendor/guzzlehttp/promises/README.md
vendored
535
vendor/guzzlehttp/promises/README.md
vendored
|
|
@ -1,536 +1,93 @@
|
|||
# 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.
|
||||
|
||||
- [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)
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- [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()`.
|
||||
`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.
|
||||
|
||||
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
|
||||
|
||||
```shell
|
||||
```bash
|
||||
composer require guzzlehttp/promises
|
||||
```
|
||||
|
||||
|
||||
## Version Guidance
|
||||
|
||||
| Version | Status | PHP Version |
|
||||
|---------|---------------------|--------------|
|
||||
| 1.x | Security fixes only | >=5.5,<8.3 |
|
||||
| 2.x | Latest | >=7.2.5,<8.6 |
|
||||
|
||||
| Version | Status | PHP Version |
|
||||
|---------|--------------|--------------|
|
||||
| 3.0 | Latest | >=7.4,<8.6 |
|
||||
| 2.5 | Maintenance | >=7.2.5,<8.6 |
|
||||
| 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 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 () 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. 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 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 = null, ?callable $onRejected = null) : PromiseInterface`
|
||||
|
||||
Appends fulfillment and rejection handlers to the promise, 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 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();
|
||||
```
|
||||
|
||||
|
||||
### 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 short periodic
|
||||
timer. Avoid zero-interval timers because they may keep the loop busy even when
|
||||
there is no promise work to run.
|
||||
|
||||
```php
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
$loop->addPeriodicTimer(0.01, [$queue, 'run']);
|
||||
```
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
See [UPGRADING.md](UPGRADING.md) for package upgrade notes.
|
||||
|
||||
- [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. 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.
|
||||
|
||||
## 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)
|
||||
|
|
|
|||
196
vendor/guzzlehttp/promises/UPGRADING.md
vendored
196
vendor/guzzlehttp/promises/UPGRADING.md
vendored
|
|
@ -1,6 +1,202 @@
|
|||
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
|
||||
----------
|
||||
|
||||
|
|
|
|||
64
vendor/guzzlehttp/promises/src/Coroutine.php
vendored
64
vendor/guzzlehttp/promises/src/Coroutine.php
vendored
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||
namespace GuzzleHttp\Promise;
|
||||
|
||||
use Generator;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Creates a promise that is resolved using a generator that yields values or
|
||||
|
|
@ -37,29 +36,35 @@ 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>
|
||||
*
|
||||
* @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();
|
||||
|
|
@ -70,19 +75,34 @@ final class Coroutine implements PromiseInterface
|
|||
});
|
||||
try {
|
||||
$this->nextCoroutine($this->generator->current());
|
||||
} catch (Throwable $throwable) {
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->result->reject($throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new coroutine.
|
||||
*
|
||||
* @param callable(): Generator<mixed, mixed, mixed, mixed> $generatorFn
|
||||
*
|
||||
* @return self<mixed, mixed>
|
||||
*/
|
||||
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
|
||||
|
|
@ -90,6 +110,14 @@ final class Coroutine implements PromiseInterface
|
|||
return $this->result->then($onFulfilled, $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);
|
||||
|
|
@ -105,7 +133,7 @@ final class Coroutine implements PromiseInterface
|
|||
return $this->result->getState();
|
||||
}
|
||||
|
||||
public function resolve($value): void
|
||||
public function resolve($value = null): void
|
||||
{
|
||||
$this->result->resolve($value);
|
||||
}
|
||||
|
|
@ -135,7 +163,8 @@ final class Coroutine implements PromiseInterface
|
|||
*/
|
||||
public function _handleSuccess($value): void
|
||||
{
|
||||
unset($this->currentPromise);
|
||||
$this->currentPromise = null;
|
||||
|
||||
try {
|
||||
$next = $this->generator->send($value);
|
||||
if ($this->generator->valid()) {
|
||||
|
|
@ -143,7 +172,7 @@ final class Coroutine implements PromiseInterface
|
|||
} else {
|
||||
$this->result->resolve($value);
|
||||
}
|
||||
} catch (Throwable $throwable) {
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->result->reject($throwable);
|
||||
}
|
||||
}
|
||||
|
|
@ -153,12 +182,13 @@ final class Coroutine implements PromiseInterface
|
|||
*/
|
||||
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 (Throwable $throwable) {
|
||||
} catch (\Throwable $throwable) {
|
||||
$this->result->reject($throwable);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
vendor/guzzlehttp/promises/src/Create.php
vendored
58
vendor/guzzlehttp/promises/src/Create.php
vendored
|
|
@ -6,10 +6,21 @@ 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>
|
||||
*
|
||||
* @param TValue|TPromise $value Promise or value.
|
||||
*
|
||||
* @return ($value is PromiseInterface ? TPromise : FulfilledPromise<TValue, never>)
|
||||
*/
|
||||
public static function promiseFor($value): PromiseInterface
|
||||
{
|
||||
|
|
@ -31,10 +42,16 @@ 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>
|
||||
*
|
||||
* @param TReason|TPromise $reason Promise or reason.
|
||||
*
|
||||
* @return ($reason is PromiseInterface ? TPromise : RejectedPromise<TValue, TReason>)
|
||||
*/
|
||||
public static function rejectionFor($reason): PromiseInterface
|
||||
{
|
||||
|
|
@ -46,9 +63,12 @@ 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
|
||||
*
|
||||
* @param TReason $reason
|
||||
*/
|
||||
public static function exceptionFor($reason): \Throwable
|
||||
{
|
||||
|
|
@ -60,11 +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
|
||||
*
|
||||
* @param iterable<TKey, TValue> $value
|
||||
*
|
||||
* @return \Iterator<TKey, TValue>
|
||||
*/
|
||||
public static function iterFor($value): \Iterator
|
||||
public static function iterFor(iterable $value): \Iterator
|
||||
{
|
||||
if ($value instanceof \Iterator) {
|
||||
return $value;
|
||||
|
|
@ -74,16 +100,10 @@ final class Create
|
|||
return new \ArrayIterator($value);
|
||||
}
|
||||
|
||||
if (!is_iterable($value)) {
|
||||
\trigger_deprecation(
|
||||
'guzzlehttp/promises',
|
||||
'2.5',
|
||||
'Passing a non-iterable to %s::%s() is deprecated; guzzlehttp/promises 3.0 will require an iterable.',
|
||||
__CLASS__,
|
||||
__FUNCTION__
|
||||
);
|
||||
if ($value instanceof \IteratorAggregate) {
|
||||
return self::iterFor($value->getIterator());
|
||||
}
|
||||
|
||||
return new \ArrayIterator([$value]);
|
||||
return new \IteratorIterator($value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
110
vendor/guzzlehttp/promises/src/Each.php
vendored
110
vendor/guzzlehttp/promises/src/Each.php
vendored
|
|
@ -6,32 +6,58 @@ 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.
|
||||
* The config array accepts a concurrency option matching {@see ofLimit}.
|
||||
* Other config keys are ignored by this wrapper.
|
||||
*
|
||||
* @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,
|
||||
iterable $iterable,
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
?callable $onRejected = null,
|
||||
array $config = []
|
||||
): PromiseInterface {
|
||||
$iterable = self::prepareIterable($iterable, __FUNCTION__);
|
||||
$eachConfig = [];
|
||||
|
||||
return (new EachPromise($iterable, [
|
||||
'fulfilled' => $onFulfilled,
|
||||
'rejected' => $onRejected,
|
||||
]))->promise();
|
||||
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,41 +66,46 @@ 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
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @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
|
||||
): PromiseInterface {
|
||||
$iterable = self::prepareIterable($iterable, __FUNCTION__);
|
||||
|
||||
return (new EachPromise($iterable, [
|
||||
'fulfilled' => $onFulfilled,
|
||||
'rejected' => $onRejected,
|
||||
'concurrency' => $concurrency,
|
||||
]))->promise();
|
||||
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
|
||||
* @template TKey of array-key
|
||||
* @template TValue
|
||||
* @template TReason
|
||||
*
|
||||
* @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
|
||||
): PromiseInterface {
|
||||
$iterable = self::prepareIterable($iterable, __FUNCTION__);
|
||||
|
||||
return self::ofLimit(
|
||||
$iterable,
|
||||
$concurrency,
|
||||
|
|
@ -84,21 +115,4 @@ final class Each
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static function prepareIterable($iterable, string $method): iterable
|
||||
{
|
||||
if (is_iterable($iterable)) {
|
||||
return $iterable;
|
||||
}
|
||||
|
||||
\trigger_deprecation(
|
||||
'guzzlehttp/promises',
|
||||
'2.5',
|
||||
'Passing a non-iterable to %s::%s() is deprecated; guzzlehttp/promises 3.0 will require an iterable.',
|
||||
self::class,
|
||||
$method
|
||||
);
|
||||
|
||||
return [$iterable];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
108
vendor/guzzlehttp/promises/src/EachPromise.php
vendored
108
vendor/guzzlehttp/promises/src/EachPromise.php
vendored
|
|
@ -8,31 +8,41 @@ 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:
|
||||
|
|
@ -52,23 +62,15 @@ 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 = [])
|
||||
{
|
||||
if (!is_iterable($iterable)) {
|
||||
\trigger_deprecation(
|
||||
'guzzlehttp/promises',
|
||||
'2.5',
|
||||
'Passing a non-iterable to %s::%s() is deprecated; guzzlehttp/promises 3.0 will require an iterable.',
|
||||
__CLASS__,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
$iterable = [$iterable];
|
||||
}
|
||||
|
||||
$this->iterable = Create::iterFor($iterable);
|
||||
|
||||
if (isset($config['concurrency'])) {
|
||||
|
|
@ -84,7 +86,9 @@ class EachPromise implements PromisorInterface
|
|||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress InvalidNullableReturnType */
|
||||
/**
|
||||
* @return PromiseInterface<mixed, mixed>
|
||||
*/
|
||||
public function promise(): PromiseInterface
|
||||
{
|
||||
if ($this->aggregate) {
|
||||
|
|
@ -93,7 +97,6 @@ class EachPromise implements PromisorInterface
|
|||
|
||||
try {
|
||||
$this->createPromise();
|
||||
/** @psalm-assert Promise $this->aggregate */
|
||||
$this->iterable->rewind();
|
||||
$this->refillPending();
|
||||
if (!$this->pending) {
|
||||
|
|
@ -113,9 +116,6 @@ class EachPromise implements PromisorInterface
|
|||
$this->aggregate->reject($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress NullableReturnStatement
|
||||
*/
|
||||
return $this->aggregate;
|
||||
}
|
||||
|
||||
|
|
@ -123,16 +123,23 @@ class EachPromise implements PromisorInterface
|
|||
{
|
||||
$this->mutex = false;
|
||||
$this->aggregate = new Promise(function (): void {
|
||||
if ($this->checkIfFinished()) {
|
||||
return;
|
||||
}
|
||||
reset($this->pending);
|
||||
// Consume a potentially fluctuating list of promises while
|
||||
// ensuring that indexes are maintained (precluding array_shift).
|
||||
while ($promise = current($this->pending)) {
|
||||
next($this->pending);
|
||||
$promise->wait();
|
||||
if (Is::settled($this->aggregate)) {
|
||||
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 (Is::settled($this->aggregate)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Refill and re-sweep; give up only when nothing remains.
|
||||
$this->refillPending();
|
||||
if (Is::settled($this->aggregate) || !$this->pending) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -162,6 +169,10 @@ class EachPromise implements PromisorInterface
|
|||
$concurrency = is_callable($this->concurrency)
|
||||
? ($this->concurrency)(count($this->pending))
|
||||
: $this->concurrency;
|
||||
// The callable can settle the aggregate; admit nothing more.
|
||||
if (Is::settled($this->aggregate)) {
|
||||
return;
|
||||
}
|
||||
$concurrency = max($concurrency - count($this->pending), 0);
|
||||
// Concurrency may be set to 0 to disallow new promises.
|
||||
if (!$concurrency) {
|
||||
|
|
@ -223,6 +234,8 @@ class EachPromise implements PromisorInterface
|
|||
// 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;
|
||||
}
|
||||
|
||||
|
|
@ -231,14 +244,22 @@ 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;
|
||||
}
|
||||
|
||||
// Run the completion check that locked steps skipped.
|
||||
if ($this->stepWhileLocked) {
|
||||
$this->stepWhileLocked = false;
|
||||
if (!Is::settled($this->aggregate)) {
|
||||
$this->checkIfFinished();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function step(int $idx): void
|
||||
|
|
@ -259,6 +280,7 @@ class EachPromise implements PromisorInterface
|
|||
}
|
||||
}
|
||||
|
||||
/** @phpstan-impure */
|
||||
private function checkIfFinished(): bool
|
||||
{
|
||||
if (!$this->pending && !$this->iterable->valid()) {
|
||||
|
|
|
|||
|
|
@ -10,14 +10,20 @@ 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 mixed $value
|
||||
* @param TValue $value
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
|
|
@ -30,6 +36,17 @@ 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
|
||||
|
|
@ -55,6 +72,11 @@ class FulfilledPromise implements PromiseInterface
|
|||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
|
@ -70,7 +92,7 @@ class FulfilledPromise implements PromiseInterface
|
|||
return self::FULFILLED;
|
||||
}
|
||||
|
||||
public function resolve($value): void
|
||||
public function resolve($value = null): void
|
||||
{
|
||||
if ($value !== $this->value) {
|
||||
throw new \LogicException('Cannot resolve a fulfilled promise');
|
||||
|
|
|
|||
4
vendor/guzzlehttp/promises/src/Is.php
vendored
4
vendor/guzzlehttp/promises/src/Is.php
vendored
|
|
@ -6,6 +6,10 @@ namespace GuzzleHttp\Promise;
|
|||
|
||||
final class Is
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a promise is pending.
|
||||
*/
|
||||
|
|
|
|||
21
vendor/guzzlehttp/promises/src/NonSerializableTrait.php
vendored
Normal file
21
vendor/guzzlehttp/promises/src/NonSerializableTrait.php
vendored
Normal file
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
60
vendor/guzzlehttp/promises/src/Promise.php
vendored
60
vendor/guzzlehttp/promises/src/Promise.php
vendored
|
|
@ -7,22 +7,40 @@ namespace GuzzleHttp\Promise;
|
|||
/**
|
||||
* Promises/A+ implementation that avoids recursion when possible.
|
||||
*
|
||||
* @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,
|
||||
|
|
@ -32,6 +50,17 @@ class Promise implements PromiseInterface
|
|||
$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
|
||||
|
|
@ -49,16 +78,27 @@ class Promise implements PromiseInterface
|
|||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
|
@ -78,6 +118,8 @@ class Promise implements PromiseInterface
|
|||
// It's rejected so "unwrap" and throw an exception.
|
||||
throw Create::exceptionFor($this->result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getState(): string
|
||||
|
|
@ -110,7 +152,7 @@ class Promise implements PromiseInterface
|
|||
}
|
||||
}
|
||||
|
||||
public function resolve($value): void
|
||||
public function resolve($value = null): void
|
||||
{
|
||||
$this->settle(self::FULFILLED, $value);
|
||||
}
|
||||
|
|
@ -187,7 +229,7 @@ class Promise implements PromiseInterface
|
|||
*/
|
||||
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
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ namespace GuzzleHttp\Promise;
|
|||
* which registers callbacks to receive either a promise’s eventual value or
|
||||
* the reason why the promise cannot be fulfilled.
|
||||
*
|
||||
* @template TValue = mixed
|
||||
* @template TReason = mixed
|
||||
*
|
||||
* @see https://promisesaplus.com/
|
||||
*/
|
||||
interface PromiseInterface
|
||||
|
|
@ -23,8 +26,15 @@ interface PromiseInterface
|
|||
* 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
|
||||
*
|
||||
* @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,
|
||||
|
|
@ -37,7 +47,12 @@ 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
|
||||
*
|
||||
* @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;
|
||||
|
||||
|
|
@ -46,22 +61,24 @@ interface PromiseInterface
|
|||
*
|
||||
* The three states can be checked against the constants defined on
|
||||
* PromiseInterface: PENDING, FULFILLED, and REJECTED.
|
||||
*
|
||||
* @return self::PENDING|self::FULFILLED|self::REJECTED
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public function resolve($value): void;
|
||||
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.
|
||||
*/
|
||||
|
|
@ -82,7 +99,7 @@ interface PromiseInterface
|
|||
*
|
||||
* If the promise cannot be waited on, then the promise will be rejected.
|
||||
*
|
||||
* @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.
|
||||
|
|
|
|||
|
|
@ -6,11 +6,16 @@ namespace GuzzleHttp\Promise;
|
|||
|
||||
/**
|
||||
* Interface used with classes that return a promise.
|
||||
*
|
||||
* @template TValue = mixed
|
||||
* @template TReason = mixed
|
||||
*/
|
||||
interface PromisorInterface
|
||||
{
|
||||
/**
|
||||
* Returns a promise.
|
||||
*
|
||||
* @return PromiseInterface<TValue, TReason>
|
||||
*/
|
||||
public function promise(): PromiseInterface;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,20 @@ 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 mixed $reason
|
||||
* @param TReason $reason
|
||||
*/
|
||||
public function __construct($reason)
|
||||
{
|
||||
|
|
@ -30,6 +36,17 @@ 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
|
||||
|
|
@ -57,6 +74,14 @@ class RejectedPromise implements PromiseInterface
|
|||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
|
@ -76,7 +101,7 @@ class RejectedPromise implements PromiseInterface
|
|||
return self::REJECTED;
|
||||
}
|
||||
|
||||
public function resolve($value): void
|
||||
public function resolve($value = null): void
|
||||
{
|
||||
throw new \LogicException('Cannot resolve a rejected promise');
|
||||
}
|
||||
|
|
|
|||
12
vendor/guzzlehttp/promises/src/TaskQueue.php
vendored
12
vendor/guzzlehttp/promises/src/TaskQueue.php
vendored
|
|
@ -17,8 +17,11 @@ namespace GuzzleHttp\Promise;
|
|||
*/
|
||||
class TaskQueue implements TaskQueueInterface
|
||||
{
|
||||
private $enableShutdown = true;
|
||||
private $queue = [];
|
||||
use NonSerializableTrait;
|
||||
|
||||
private bool $enableShutdown = true;
|
||||
/** @var list<callable(): void> */
|
||||
private array $queue = [];
|
||||
|
||||
public function __construct(bool $withShutdown = true)
|
||||
{
|
||||
|
|
@ -40,6 +43,9 @@ class TaskQueue implements TaskQueueInterface
|
|||
return !$this->queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(): void $task
|
||||
*/
|
||||
public function add(callable $task): void
|
||||
{
|
||||
$this->queue[] = $task;
|
||||
|
|
@ -48,7 +54,7 @@ class TaskQueue implements TaskQueueInterface
|
|||
public function run(): void
|
||||
{
|
||||
while ($task = array_shift($this->queue)) {
|
||||
/** @var callable $task */
|
||||
/** @var callable(): void $task */
|
||||
$task();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ interface TaskQueueInterface
|
|||
/**
|
||||
* 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): void;
|
||||
|
||||
|
|
|
|||
233
vendor/guzzlehttp/promises/src/Utils.php
vendored
233
vendor/guzzlehttp/promises/src/Utils.php
vendored
|
|
@ -6,6 +6,10 @@ namespace GuzzleHttp\Promise;
|
|||
|
||||
final class Utils
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the global task queue used for promise resolution.
|
||||
*
|
||||
|
|
@ -35,10 +39,14 @@ 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
|
||||
*
|
||||
* @param callable(): TValue $task Task function to run.
|
||||
*
|
||||
* @return PromiseInterface<TValue, \Throwable>
|
||||
*/
|
||||
public static function task(callable $task): PromiseInterface
|
||||
{
|
||||
|
|
@ -58,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
|
||||
|
|
@ -67,42 +75,75 @@ 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
|
||||
*
|
||||
* @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): 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(),
|
||||
];
|
||||
$inspection->wait(false);
|
||||
} catch (\Throwable $e) {
|
||||
if ($e instanceof AggregateException) {
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
|
||||
$settled = $getResult();
|
||||
if (null !== $settled) {
|
||||
return $settled;
|
||||
}
|
||||
|
||||
if ($e instanceof RejectionException) {
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
|
||||
if (Is::settled($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
|
||||
*
|
||||
* @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): array
|
||||
public static function inspectAll(iterable $promises): array
|
||||
{
|
||||
$promises = self::prepareIterable($promises, __FUNCTION__);
|
||||
|
||||
$results = [];
|
||||
foreach ($promises as $key => $promise) {
|
||||
$results[$key] = self::inspect($promise);
|
||||
|
|
@ -118,14 +159,18 @@ 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
|
||||
*
|
||||
* @param iterable<TKey, PromiseInterface<TValue, TReason>> $promises Iterable of PromiseInterface objects to wait on.
|
||||
*
|
||||
* @return array<TKey, TValue>
|
||||
*
|
||||
* @throws \Throwable on error
|
||||
*/
|
||||
public static function unwrap($promises): array
|
||||
public static function unwrap(iterable $promises): array
|
||||
{
|
||||
$promises = self::prepareIterable($promises, __FUNCTION__);
|
||||
|
||||
$results = [];
|
||||
foreach ($promises as $key => $promise) {
|
||||
$results[$key] = $promise->wait();
|
||||
|
|
@ -142,24 +187,33 @@ 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.
|
||||
*
|
||||
* @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, bool $recursive = false): PromiseInterface
|
||||
public static function all(iterable $promises, bool $recursive = false, array $config = []): PromiseInterface
|
||||
{
|
||||
$promises = self::prepareIterable($promises, __FUNCTION__);
|
||||
|
||||
$results = [];
|
||||
$promise = Each::of(
|
||||
$promises,
|
||||
function ($value, $idx) use (&$results): void {
|
||||
$results[$idx] = $value;
|
||||
},
|
||||
function ($reason, $idx, Promise $aggregate): void {
|
||||
function ($reason, $idx, PromiseInterface $aggregate): void {
|
||||
if (Is::pending($aggregate)) {
|
||||
$aggregate->reject($reason);
|
||||
}
|
||||
}
|
||||
},
|
||||
$config
|
||||
)->then(function () use (&$results) {
|
||||
ksort($results);
|
||||
|
||||
|
|
@ -167,11 +221,9 @@ final class Utils
|
|||
});
|
||||
|
||||
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;
|
||||
|
|
@ -187,22 +239,25 @@ 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
|
||||
*
|
||||
* @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(int $count, $promises): PromiseInterface
|
||||
public static function some(int $count, iterable $promises): PromiseInterface
|
||||
{
|
||||
$promises = self::prepareIterable($promises, __FUNCTION__);
|
||||
|
||||
$results = [];
|
||||
$rejections = [];
|
||||
|
||||
return Each::of(
|
||||
$promise = Each::of(
|
||||
$promises,
|
||||
function ($value, $idx, PromiseInterface $p) use (&$results, $count): void {
|
||||
if (Is::settled($p)) {
|
||||
|
|
@ -229,77 +284,107 @@ final class Utils
|
|||
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
|
||||
*
|
||||
* @param iterable<TValue|PromiseInterface<TValue, TReason>> $promises Promises or values.
|
||||
*
|
||||
* @return PromiseInterface<TValue, \Throwable>
|
||||
*/
|
||||
public static function any($promises): PromiseInterface
|
||||
public static function any(iterable $promises): PromiseInterface
|
||||
{
|
||||
$promises = self::prepareIterable($promises, __FUNCTION__);
|
||||
|
||||
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
|
||||
*
|
||||
* @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): PromiseInterface
|
||||
public static function settle(iterable $promises, bool $recursive = false, array $config = []): PromiseInterface
|
||||
{
|
||||
$promises = self::prepareIterable($promises, __FUNCTION__);
|
||||
|
||||
$results = [];
|
||||
|
||||
return Each::of(
|
||||
$promise = Each::of(
|
||||
$promises,
|
||||
function ($value, $idx) use (&$results): void {
|
||||
$results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
|
||||
},
|
||||
function ($reason, $idx) use (&$results): void {
|
||||
$results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
|
||||
}
|
||||
},
|
||||
$config
|
||||
)->then(function () use (&$results) {
|
||||
ksort($results);
|
||||
|
||||
return $results;
|
||||
});
|
||||
}
|
||||
|
||||
private static function prepareIterable($promises, string $method): iterable
|
||||
{
|
||||
if (is_iterable($promises)) {
|
||||
return $promises;
|
||||
if (true === $recursive) {
|
||||
$promise = $promise->then(function ($results) use (&$promises, $config) {
|
||||
if (self::shouldRecurse($promises, $results)) {
|
||||
return self::settle($promises, true, $config);
|
||||
}
|
||||
|
||||
return $results;
|
||||
});
|
||||
}
|
||||
|
||||
self::triggerNonIterableDeprecation($promises, $method);
|
||||
|
||||
return [$promises];
|
||||
return $promise;
|
||||
}
|
||||
|
||||
private static function triggerNonIterableDeprecation($promises, string $method): void
|
||||
/**
|
||||
* @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
|
||||
{
|
||||
if (is_iterable($promises)) {
|
||||
return;
|
||||
// A consumed generator cannot be traversed again, so a recursive
|
||||
// pass has nothing further to observe.
|
||||
if ($promises instanceof \Generator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
\trigger_deprecation(
|
||||
'guzzlehttp/promises',
|
||||
'2.5',
|
||||
'Passing a non-iterable to %s::%s() is deprecated; guzzlehttp/promises 3.0 will require an iterable.',
|
||||
self::class,
|
||||
$method
|
||||
);
|
||||
foreach ($promises as $key => $promise) {
|
||||
if (!array_key_exists($key, $results)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($promise instanceof PromiseInterface && Is::pending($promise)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue