···1+# Changelog
2+3+## 4.3.0 (2024-06-04)
4+5+* Feature: Improve performance by avoiding unneeded references in `FiberMap`.
6+ (#88 by @clue)
7+8+* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations.
9+ (#87 by @clue)
10+11+* Improve type safety for test environment.
12+ (#86 by @SimonFrings)
13+14+## 4.2.0 (2023-11-22)
15+16+* Feature: Add Promise v3 template types for all public functions.
17+ (#40 by @WyriHaximus and @clue)
18+19+ All our public APIs now use Promise v3 template types to guide IDEs and static
20+ analysis tools (like PHPStan), helping with proper type usage and improving
21+ code quality:
22+23+ ```php
24+ assertType('bool', await(resolve(true)));
25+ assertType('PromiseInterface<bool>', async(fn(): bool => true)());
26+ assertType('PromiseInterface<bool>', coroutine(fn(): bool => true));
27+ ```
28+29+* Feature: Full PHP 8.3 compatibility.
30+ (#81 by @clue)
31+32+* Update test suite to avoid unhandled promise rejections.
33+ (#79 by @clue)
34+35+## 4.1.0 (2023-06-22)
36+37+* Feature: Add new `delay()` function to delay program execution.
38+ (#69 and #78 by @clue)
39+40+ ```php
41+ echo 'a';
42+ Loop::addTimer(1.0, function () {
43+ echo 'b';
44+ });
45+ React\Async\delay(3.0);
46+ echo 'c';
47+48+ // prints "a" at t=0.0s
49+ // prints "b" at t=1.0s
50+ // prints "c" at t=3.0s
51+ ```
52+53+* Update test suite, add PHPStan with `max` level and report failed assertions.
54+ (#66 and #76 by @clue and #61 and #73 by @WyriHaximus)
55+56+## 4.0.0 (2022-07-11)
57+58+A major new feature release, see [**release announcement**](https://clue.engineering/2022/announcing-reactphp-async).
59+60+* We'd like to emphasize that this component is production ready and battle-tested.
61+ We plan to support all long-term support (LTS) releases for at least 24 months,
62+ so you have a rock-solid foundation to build on top of.
63+64+* The v4 release will be the way forward for this package. However, we will still
65+ actively support v3 and v2 to provide a smooth upgrade path for those not yet
66+ on PHP 8.1+. If you're using an older PHP version, you may use either version
67+ which all provide a compatible API but may not take advantage of newer language
68+ features. You may target multiple versions at the same time to support a wider range of
69+ PHP versions:
70+71+ * [`4.x` branch](https://github.com/reactphp/async/tree/4.x) (PHP 8.1+)
72+ * [`3.x` branch](https://github.com/reactphp/async/tree/3.x) (PHP 7.1+)
73+ * [`2.x` branch](https://github.com/reactphp/async/tree/2.x) (PHP 5.3+)
74+75+This update involves some major new features and a minor BC break over the
76+`v3.0.0` release. We've tried hard to avoid BC breaks where possible and
77+minimize impact otherwise. We expect that most consumers of this package will be
78+affected by BC breaks, but updating should take no longer than a few minutes.
79+See below for more details:
80+81+* Feature / BC break: Require PHP 8.1+ and add `mixed` type declarations.
82+ (#14 by @clue)
83+84+* Feature: Add Fiber-based `async()` and `await()` functions.
85+ (#15, #18, #19 and #20 by @WyriHaximus and #26, #28, #30, #32, #34, #55 and #57 by @clue)
86+87+* Project maintenance, rename `main` branch to `4.x` and update installation instructions.
88+ (#29 by @clue)
89+90+The following changes had to be ported to this release due to our branching
91+strategy, but also appeared in the `v3.0.0` release:
92+93+* Feature: Support iterable type for `parallel()` + `series()` + `waterfall()`.
94+ (#49 by @clue)
95+96+* Feature: Forward compatibility with upcoming Promise v3.
97+ (#48 by @clue)
98+99+* Minor documentation improvements.
100+ (#36 by @SimonFrings and #51 by @nhedger)
101+102+## 3.0.0 (2022-07-11)
103+104+See [`3.x` CHANGELOG](https://github.com/reactphp/async/blob/3.x/CHANGELOG.md) for more details.
105+106+## 2.0.0 (2022-07-11)
107+108+See [`2.x` CHANGELOG](https://github.com/reactphp/async/blob/2.x/CHANGELOG.md) for more details.
109+110+## 1.0.0 (2013-02-07)
111+112+* First tagged release
+19
vendor/react/async/LICENSE
···0000000000000000000
···1+Copyright (c) 2012 Christian Lück, Cees-Jan Kiewiet, Jan Sorgalla, Chris Boden, Igor Wiedler
2+3+Permission is hereby granted, free of charge, to any person obtaining a copy
4+of this software and associated documentation files (the "Software"), to deal
5+in the Software without restriction, including without limitation the rights
6+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+copies of the Software, and to permit persons to whom the Software is furnished
8+to do so, subject to the following conditions:
9+10+The above copyright notice and this permission notice shall be included in all
11+copies or substantial portions of the Software.
12+13+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+THE SOFTWARE.
···1+# Async Utilities
2+3+[](https://github.com/reactphp/async/actions)
4+[](https://packagist.org/packages/react/async)
5+6+Async utilities and fibers for [ReactPHP](https://reactphp.org/).
7+8+This library allows you to manage async control flow. It provides a number of
9+combinators for [Promise](https://github.com/reactphp/promise)-based APIs.
10+Instead of nesting or chaining promise callbacks, you can declare them as a
11+list, which is resolved sequentially in an async manner.
12+React/Async will not automagically change blocking code to be async. You need
13+to have an actual event loop and non-blocking libraries interacting with that
14+event loop for it to work. As long as you have a Promise-based API that runs in
15+an event loop, it can be used with this library.
16+17+**Table of Contents**
18+19+* [Usage](#usage)
20+ * [async()](#async)
21+ * [await()](#await)
22+ * [coroutine()](#coroutine)
23+ * [delay()](#delay)
24+ * [parallel()](#parallel)
25+ * [series()](#series)
26+ * [waterfall()](#waterfall)
27+* [Todo](#todo)
28+* [Install](#install)
29+* [Tests](#tests)
30+* [License](#license)
31+32+## Usage
33+34+This lightweight library consists only of a few simple functions.
35+All functions reside under the `React\Async` namespace.
36+37+The below examples refer to all functions with their fully-qualified names like this:
38+39+```php
40+React\Async\await(…);
41+```
42+43+As of PHP 5.6+ you can also import each required function into your code like this:
44+45+```php
46+use function React\Async\await;
47+48+await(…);
49+```
50+51+Alternatively, you can also use an import statement similar to this:
52+53+```php
54+use React\Async;
55+56+Async\await(…);
57+```
58+59+### async()
60+61+The `async(callable():(PromiseInterface<T>|T) $function): (callable():PromiseInterface<T>)` function can be used to
62+return an async function for a function that uses [`await()`](#await) internally.
63+64+This function is specifically designed to complement the [`await()` function](#await).
65+The [`await()` function](#await) can be considered *blocking* from the
66+perspective of the calling code. You can avoid this blocking behavior by
67+wrapping it in an `async()` function call. Everything inside this function
68+will still be blocked, but everything outside this function can be executed
69+asynchronously without blocking:
70+71+```php
72+Loop::addTimer(0.5, React\Async\async(function () {
73+ echo 'a';
74+ React\Async\await(React\Promise\Timer\sleep(1.0));
75+ echo 'c';
76+}));
77+78+Loop::addTimer(1.0, function () {
79+ echo 'b';
80+});
81+82+// prints "a" at t=0.5s
83+// prints "b" at t=1.0s
84+// prints "c" at t=1.5s
85+```
86+87+See also the [`await()` function](#await) for more details.
88+89+Note that this function only works in tandem with the [`await()` function](#await).
90+In particular, this function does not "magically" make any blocking function
91+non-blocking:
92+93+```php
94+Loop::addTimer(0.5, React\Async\async(function () {
95+ echo 'a';
96+ sleep(1); // broken: using PHP's blocking sleep() for demonstration purposes
97+ echo 'c';
98+}));
99+100+Loop::addTimer(1.0, function () {
101+ echo 'b';
102+});
103+104+// prints "a" at t=0.5s
105+// prints "c" at t=1.5s: Correct timing, but wrong order
106+// prints "b" at t=1.5s: Triggered too late because it was blocked
107+```
108+109+As an alternative, you should always make sure to use this function in tandem
110+with the [`await()` function](#await) and an async API returning a promise
111+as shown in the previous example.
112+113+The `async()` function is specifically designed for cases where it is used
114+as a callback (such as an event loop timer, event listener, or promise
115+callback). For this reason, it returns a new function wrapping the given
116+`$function` instead of directly invoking it and returning its value.
117+118+```php
119+use function React\Async\async;
120+121+Loop::addTimer(1.0, async(function () { … }));
122+$connection->on('close', async(function () { … }));
123+$stream->on('data', async(function ($data) { … }));
124+$promise->then(async(function (int $result) { … }));
125+```
126+127+You can invoke this wrapping function to invoke the given `$function` with
128+any arguments given as-is. The function will always return a Promise which
129+will be fulfilled with whatever your `$function` returns. Likewise, it will
130+return a promise that will be rejected if you throw an `Exception` or
131+`Throwable` from your `$function`. This allows you to easily create
132+Promise-based functions:
133+134+```php
135+$promise = React\Async\async(function (): int {
136+ $browser = new React\Http\Browser();
137+ $urls = [
138+ 'https://example.com/alice',
139+ 'https://example.com/bob'
140+ ];
141+142+ $bytes = 0;
143+ foreach ($urls as $url) {
144+ $response = React\Async\await($browser->get($url));
145+ assert($response instanceof Psr\Http\Message\ResponseInterface);
146+ $bytes += $response->getBody()->getSize();
147+ }
148+ return $bytes;
149+})();
150+151+$promise->then(function (int $bytes) {
152+ echo 'Total size: ' . $bytes . PHP_EOL;
153+}, function (Exception $e) {
154+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
155+});
156+```
157+158+The previous example uses [`await()`](#await) inside a loop to highlight how
159+this vastly simplifies consuming asynchronous operations. At the same time,
160+this naive example does not leverage concurrent execution, as it will
161+essentially "await" between each operation. In order to take advantage of
162+concurrent execution within the given `$function`, you can "await" multiple
163+promises by using a single [`await()`](#await) together with Promise-based
164+primitives like this:
165+166+```php
167+$promise = React\Async\async(function (): int {
168+ $browser = new React\Http\Browser();
169+ $urls = [
170+ 'https://example.com/alice',
171+ 'https://example.com/bob'
172+ ];
173+174+ $promises = [];
175+ foreach ($urls as $url) {
176+ $promises[] = $browser->get($url);
177+ }
178+179+ try {
180+ $responses = React\Async\await(React\Promise\all($promises));
181+ } catch (Exception $e) {
182+ foreach ($promises as $promise) {
183+ $promise->cancel();
184+ }
185+ throw $e;
186+ }
187+188+ $bytes = 0;
189+ foreach ($responses as $response) {
190+ assert($response instanceof Psr\Http\Message\ResponseInterface);
191+ $bytes += $response->getBody()->getSize();
192+ }
193+ return $bytes;
194+})();
195+196+$promise->then(function (int $bytes) {
197+ echo 'Total size: ' . $bytes . PHP_EOL;
198+}, function (Exception $e) {
199+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
200+});
201+```
202+203+The returned promise is implemented in such a way that it can be cancelled
204+when it is still pending. Cancelling a pending promise will cancel any awaited
205+promises inside that fiber or any nested fibers. As such, the following example
206+will only output `ab` and cancel the pending [`delay()`](#delay).
207+The [`await()`](#await) calls in this example would throw a `RuntimeException`
208+from the cancelled [`delay()`](#delay) call that bubbles up through the fibers.
209+210+```php
211+$promise = async(static function (): int {
212+ echo 'a';
213+ await(async(static function (): void {
214+ echo 'b';
215+ delay(2);
216+ echo 'c';
217+ })());
218+ echo 'd';
219+220+ return time();
221+})();
222+223+$promise->cancel();
224+await($promise);
225+```
226+227+### await()
228+229+The `await(PromiseInterface<T> $promise): T` function can be used to
230+block waiting for the given `$promise` to be fulfilled.
231+232+```php
233+$result = React\Async\await($promise);
234+```
235+236+This function will only return after the given `$promise` has settled, i.e.
237+either fulfilled or rejected. While the promise is pending, this function
238+can be considered *blocking* from the perspective of the calling code.
239+You can avoid this blocking behavior by wrapping it in an [`async()` function](#async)
240+call. Everything inside this function will still be blocked, but everything
241+outside this function can be executed asynchronously without blocking:
242+243+```php
244+Loop::addTimer(0.5, React\Async\async(function () {
245+ echo 'a';
246+ React\Async\await(React\Promise\Timer\sleep(1.0));
247+ echo 'c';
248+}));
249+250+Loop::addTimer(1.0, function () {
251+ echo 'b';
252+});
253+254+// prints "a" at t=0.5s
255+// prints "b" at t=1.0s
256+// prints "c" at t=1.5s
257+```
258+259+See also the [`async()` function](#async) for more details.
260+261+Once the promise is fulfilled, this function will return whatever the promise
262+resolved to.
263+264+Once the promise is rejected, this will throw whatever the promise rejected
265+with. If the promise did not reject with an `Exception` or `Throwable`, then
266+this function will throw an `UnexpectedValueException` instead.
267+268+```php
269+try {
270+ $result = React\Async\await($promise);
271+ // promise successfully fulfilled with $result
272+ echo 'Result: ' . $result;
273+} catch (Throwable $e) {
274+ // promise rejected with $e
275+ echo 'Error: ' . $e->getMessage();
276+}
277+```
278+279+### coroutine()
280+281+The `coroutine(callable(mixed ...$args):(\Generator|PromiseInterface<T>|T) $function, mixed ...$args): PromiseInterface<T>` function can be used to
282+execute a Generator-based coroutine to "await" promises.
283+284+```php
285+React\Async\coroutine(function () {
286+ $browser = new React\Http\Browser();
287+288+ try {
289+ $response = yield $browser->get('https://example.com/');
290+ assert($response instanceof Psr\Http\Message\ResponseInterface);
291+ echo $response->getBody();
292+ } catch (Exception $e) {
293+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
294+ }
295+});
296+```
297+298+Using Generator-based coroutines is an alternative to directly using the
299+underlying promise APIs. For many use cases, this makes using promise-based
300+APIs much simpler, as it resembles a synchronous code flow more closely.
301+The above example performs the equivalent of directly using the promise APIs:
302+303+```php
304+$browser = new React\Http\Browser();
305+306+$browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) {
307+ echo $response->getBody();
308+}, function (Exception $e) {
309+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
310+});
311+```
312+313+The `yield` keyword can be used to "await" a promise resolution. Internally,
314+it will turn the entire given `$function` into a [`Generator`](https://www.php.net/manual/en/class.generator.php).
315+This allows the execution to be interrupted and resumed at the same place
316+when the promise is fulfilled. The `yield` statement returns whatever the
317+promise is fulfilled with. If the promise is rejected, it will throw an
318+`Exception` or `Throwable`.
319+320+The `coroutine()` function will always return a Promise which will be
321+fulfilled with whatever your `$function` returns. Likewise, it will return
322+a promise that will be rejected if you throw an `Exception` or `Throwable`
323+from your `$function`. This allows you to easily create Promise-based
324+functions:
325+326+```php
327+$promise = React\Async\coroutine(function () {
328+ $browser = new React\Http\Browser();
329+ $urls = [
330+ 'https://example.com/alice',
331+ 'https://example.com/bob'
332+ ];
333+334+ $bytes = 0;
335+ foreach ($urls as $url) {
336+ $response = yield $browser->get($url);
337+ assert($response instanceof Psr\Http\Message\ResponseInterface);
338+ $bytes += $response->getBody()->getSize();
339+ }
340+ return $bytes;
341+});
342+343+$promise->then(function (int $bytes) {
344+ echo 'Total size: ' . $bytes . PHP_EOL;
345+}, function (Exception $e) {
346+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
347+});
348+```
349+350+The previous example uses a `yield` statement inside a loop to highlight how
351+this vastly simplifies consuming asynchronous operations. At the same time,
352+this naive example does not leverage concurrent execution, as it will
353+essentially "await" between each operation. In order to take advantage of
354+concurrent execution within the given `$function`, you can "await" multiple
355+promises by using a single `yield` together with Promise-based primitives
356+like this:
357+358+```php
359+$promise = React\Async\coroutine(function () {
360+ $browser = new React\Http\Browser();
361+ $urls = [
362+ 'https://example.com/alice',
363+ 'https://example.com/bob'
364+ ];
365+366+ $promises = [];
367+ foreach ($urls as $url) {
368+ $promises[] = $browser->get($url);
369+ }
370+371+ try {
372+ $responses = yield React\Promise\all($promises);
373+ } catch (Exception $e) {
374+ foreach ($promises as $promise) {
375+ $promise->cancel();
376+ }
377+ throw $e;
378+ }
379+380+ $bytes = 0;
381+ foreach ($responses as $response) {
382+ assert($response instanceof Psr\Http\Message\ResponseInterface);
383+ $bytes += $response->getBody()->getSize();
384+ }
385+ return $bytes;
386+});
387+388+$promise->then(function (int $bytes) {
389+ echo 'Total size: ' . $bytes . PHP_EOL;
390+}, function (Exception $e) {
391+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
392+});
393+```
394+395+### delay()
396+397+The `delay(float $seconds): void` function can be used to
398+delay program execution for duration given in `$seconds`.
399+400+```php
401+React\Async\delay($seconds);
402+```
403+404+This function will only return after the given number of `$seconds` have
405+elapsed. If there are no other events attached to this loop, it will behave
406+similar to PHP's [`sleep()` function](https://www.php.net/manual/en/function.sleep.php).
407+408+```php
409+echo 'a';
410+React\Async\delay(1.0);
411+echo 'b';
412+413+// prints "a" at t=0.0s
414+// prints "b" at t=1.0s
415+```
416+417+Unlike PHP's [`sleep()` function](https://www.php.net/manual/en/function.sleep.php),
418+this function may not necessarily halt execution of the entire process thread.
419+Instead, it allows the event loop to run any other events attached to the
420+same loop until the delay returns:
421+422+```php
423+echo 'a';
424+Loop::addTimer(1.0, function (): void {
425+ echo 'b';
426+});
427+React\Async\delay(3.0);
428+echo 'c';
429+430+// prints "a" at t=0.0s
431+// prints "b" at t=1.0s
432+// prints "c" at t=3.0s
433+```
434+435+This behavior is especially useful if you want to delay the program execution
436+of a particular routine, such as when building a simple polling or retry
437+mechanism:
438+439+```php
440+try {
441+ something();
442+} catch (Throwable) {
443+ // in case of error, retry after a short delay
444+ React\Async\delay(1.0);
445+ something();
446+}
447+```
448+449+Because this function only returns after some time has passed, it can be
450+considered *blocking* from the perspective of the calling code. You can avoid
451+this blocking behavior by wrapping it in an [`async()` function](#async) call.
452+Everything inside this function will still be blocked, but everything outside
453+this function can be executed asynchronously without blocking:
454+455+```php
456+Loop::addTimer(0.5, React\Async\async(function (): void {
457+ echo 'a';
458+ React\Async\delay(1.0);
459+ echo 'c';
460+}));
461+462+Loop::addTimer(1.0, function (): void {
463+ echo 'b';
464+});
465+466+// prints "a" at t=0.5s
467+// prints "b" at t=1.0s
468+// prints "c" at t=1.5s
469+```
470+471+See also the [`async()` function](#async) for more details.
472+473+Internally, the `$seconds` argument will be used as a timer for the loop so that
474+it keeps running until this timer triggers. This implies that if you pass a
475+really small (or negative) value, it will still start a timer and will thus
476+trigger at the earliest possible time in the future.
477+478+The function is implemented in such a way that it can be cancelled when it is
479+running inside an [`async()` function](#async). Cancelling the resulting
480+promise will clean up any pending timers and throw a `RuntimeException` from
481+the pending delay which in turn would reject the resulting promise.
482+483+```php
484+$promise = async(function (): void {
485+ echo 'a';
486+ delay(3.0);
487+ echo 'b';
488+})();
489+490+Loop::addTimer(2.0, function () use ($promise): void {
491+ $promise->cancel();
492+});
493+494+// prints "a" at t=0.0s
495+// rejects $promise at t=2.0
496+// never prints "b"
497+```
498+499+### parallel()
500+501+The `parallel(iterable<callable():PromiseInterface<T>> $tasks): PromiseInterface<array<T>>` function can be used
502+like this:
503+504+```php
505+<?php
506+507+use React\EventLoop\Loop;
508+use React\Promise\Promise;
509+510+React\Async\parallel([
511+ function () {
512+ return new Promise(function ($resolve) {
513+ Loop::addTimer(1, function () use ($resolve) {
514+ $resolve('Slept for a whole second');
515+ });
516+ });
517+ },
518+ function () {
519+ return new Promise(function ($resolve) {
520+ Loop::addTimer(1, function () use ($resolve) {
521+ $resolve('Slept for another whole second');
522+ });
523+ });
524+ },
525+ function () {
526+ return new Promise(function ($resolve) {
527+ Loop::addTimer(1, function () use ($resolve) {
528+ $resolve('Slept for yet another whole second');
529+ });
530+ });
531+ },
532+])->then(function (array $results) {
533+ foreach ($results as $result) {
534+ var_dump($result);
535+ }
536+}, function (Exception $e) {
537+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
538+});
539+```
540+541+### series()
542+543+The `series(iterable<callable():PromiseInterface<T>> $tasks): PromiseInterface<array<T>>` function can be used
544+like this:
545+546+```php
547+<?php
548+549+use React\EventLoop\Loop;
550+use React\Promise\Promise;
551+552+React\Async\series([
553+ function () {
554+ return new Promise(function ($resolve) {
555+ Loop::addTimer(1, function () use ($resolve) {
556+ $resolve('Slept for a whole second');
557+ });
558+ });
559+ },
560+ function () {
561+ return new Promise(function ($resolve) {
562+ Loop::addTimer(1, function () use ($resolve) {
563+ $resolve('Slept for another whole second');
564+ });
565+ });
566+ },
567+ function () {
568+ return new Promise(function ($resolve) {
569+ Loop::addTimer(1, function () use ($resolve) {
570+ $resolve('Slept for yet another whole second');
571+ });
572+ });
573+ },
574+])->then(function (array $results) {
575+ foreach ($results as $result) {
576+ var_dump($result);
577+ }
578+}, function (Exception $e) {
579+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
580+});
581+```
582+583+### waterfall()
584+585+The `waterfall(iterable<callable(mixed=):PromiseInterface<T>> $tasks): PromiseInterface<T>` function can be used
586+like this:
587+588+```php
589+<?php
590+591+use React\EventLoop\Loop;
592+use React\Promise\Promise;
593+594+$addOne = function ($prev = 0) {
595+ return new Promise(function ($resolve) use ($prev) {
596+ Loop::addTimer(1, function () use ($prev, $resolve) {
597+ $resolve($prev + 1);
598+ });
599+ });
600+};
601+602+React\Async\waterfall([
603+ $addOne,
604+ $addOne,
605+ $addOne
606+])->then(function ($prev) {
607+ echo "Final result is $prev\n";
608+}, function (Exception $e) {
609+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
610+});
611+```
612+613+## Todo
614+615+ * Implement queue()
616+617+## Install
618+619+The recommended way to install this library is [through Composer](https://getcomposer.org/).
620+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
621+622+This project follows [SemVer](https://semver.org/).
623+This will install the latest supported version from this branch:
624+625+```bash
626+composer require react/async:^4.3
627+```
628+629+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
630+631+This project aims to run on any platform and thus does not require any PHP
632+extensions and supports running on PHP 8.1+.
633+It's *highly recommended to use the latest supported PHP version* for this project.
634+635+We're committed to providing long-term support (LTS) options and to provide a
636+smooth upgrade path. If you're using an older PHP version, you may use the
637+[`3.x` branch](https://github.com/reactphp/async/tree/3.x) (PHP 7.1+) or
638+[`2.x` branch](https://github.com/reactphp/async/tree/2.x) (PHP 5.3+) which both
639+provide a compatible API but do not take advantage of newer language features.
640+You may target multiple versions at the same time to support a wider range of
641+PHP versions like this:
642+643+```bash
644+composer require "react/async:^4 || ^3 || ^2"
645+```
646+647+## Tests
648+649+To run the test suite, you first need to clone this repo and then install all
650+dependencies [through Composer](https://getcomposer.org/):
651+652+```bash
653+composer install
654+```
655+656+To run the test suite, go to the project root and run:
657+658+```bash
659+vendor/bin/phpunit
660+```
661+662+On top of this, we use PHPStan on max level to ensure type safety across the project:
663+664+```bash
665+vendor/bin/phpstan
666+```
667+668+## License
669+670+MIT, see [LICENSE file](LICENSE).
671+672+This project is heavily influenced by [async.js](https://github.com/caolan/async).
···1+<?php
2+3+namespace React\Async;
4+5+/**
6+ * This factory its only purpose is interoperability. Where with
7+ * event loops one could simply wrap another event loop. But with fibers
8+ * that has become impossible and as such we provide this factory and the
9+ * FiberInterface.
10+ *
11+ * Usage is not documented and as such not supported and might chang without
12+ * notice. Use at your own risk.
13+ *
14+ * @internal
15+ */
16+final class FiberFactory
17+{
18+ private static ?\Closure $factory = null;
19+20+ public static function create(): FiberInterface
21+ {
22+ return (self::factory())();
23+ }
24+25+ public static function factory(?\Closure $factory = null): \Closure
26+ {
27+ if ($factory !== null) {
28+ self::$factory = $factory;
29+ }
30+31+ return self::$factory ?? static fn (): FiberInterface => new SimpleFiber();
32+ }
33+}
+23
vendor/react/async/src/FiberInterface.php
···00000000000000000000000
···1+<?php
2+3+namespace React\Async;
4+5+/**
6+ * This interface its only purpose is interoperability. Where with
7+ * event loops one could simply wrap another event loop. But with fibers
8+ * that has become impossible and as such we provide this interface and the
9+ * FiberFactory.
10+ *
11+ * Usage is not documented and as such not supported and might chang without
12+ * notice. Use at your own risk.
13+ *
14+ * @internal
15+ */
16+interface FiberInterface
17+{
18+ public function resume(mixed $value): void;
19+20+ public function throw(\Throwable $throwable): void;
21+22+ public function suspend(): mixed;
23+}
···1+# Changelog
2+3+## 1.5.0 (2023-11-13)
4+5+* Feature: Improve performance by using `spl_object_id()` on PHP 7.2+.
6+ (#267 by @samsonasik)
7+8+* Feature: Full PHP 8.3 compatibility.
9+ (#269 by @clue)
10+11+* Update tests for `ext-uv` on PHP 8+ and legacy PHP.
12+ (#270 by @clue and #268 by @SimonFrings)
13+14+## 1.4.0 (2023-05-05)
15+16+* Feature: Improve performance of `Loop` by avoiding unneeded method calls.
17+ (#266 by @clue)
18+19+* Feature: Support checking `EINTR` constant from `ext-pcntl` without `ext-sockets`.
20+ (#265 by @clue)
21+22+* Minor documentation improvements.
23+ (#254 by @nhedger)
24+25+* Improve test suite, run tests on PHP 8.2 and report failed assertions.
26+ (#258 by @WyriHaximus, #264 by @clue and #251, #261 and #262 by @SimonFrings)
27+28+## 1.3.0 (2022-03-17)
29+30+* Feature: Improve default `StreamSelectLoop` to report any warnings for invalid streams.
31+ (#245 by @clue)
32+33+* Feature: Improve performance of `StreamSelectLoop` when no timers are scheduled.
34+ (#246 by @clue)
35+36+* Fix: Fix periodic timer with zero interval for `ExtEvLoop` and legacy `ExtLibevLoop`.
37+ (#243 by @lucasnetau)
38+39+* Minor documentation improvements, update PHP version references.
40+ (#240, #248 and #250 by @SimonFrings, #241 by @dbu and #249 by @clue)
41+42+* Improve test suite and test against PHP 8.1.
43+ (#238 by @WyriHaximus and #242 by @clue)
44+45+## 1.2.0 (2021-07-11)
46+47+A major new feature release, see [**release announcement**](https://clue.engineering/2021/announcing-reactphp-default-loop).
48+49+* Feature: Introduce new concept of default loop with the new `Loop` class.
50+ (#226 by @WyriHaximus, #229, #231 and #232 by @clue)
51+52+ The `Loop` class exists as a convenient global accessor for the event loop.
53+ It provides all methods that exist on the `LoopInterface` as static methods and
54+ will automatically execute the loop at the end of the program:
55+56+ ```php
57+ $timer = Loop::addPeriodicTimer(0.1, function () {
58+ echo 'Tick' . PHP_EOL;
59+ });
60+61+ Loop::addTimer(1.0, function () use ($timer) {
62+ Loop::cancelTimer($timer);
63+ echo 'Done' . PHP_EOL;
64+ });
65+ ```
66+67+ The explicit loop instructions are still valid and may still be useful in some applications,
68+ especially for a transition period towards the more concise style.
69+ The `Loop::get()` method can be used to get the currently active event loop instance.
70+71+ ```php
72+ // deprecated
73+ $loop = React\EventLoop\Factory::create();
74+75+ // new
76+ $loop = React\EventLoop\Loop::get();
77+ ```
78+79+* Minor documentation improvements and mark legacy extensions as deprecated.
80+ (#234 by @SimonFrings, #214 by @WyriHaximus and #233 and #235 by @nhedger)
81+82+* Improve test suite, use GitHub actions for continuous integration (CI),
83+ update PHPUnit config and run tests on PHP 8.
84+ (#212 and #215 by @SimonFrings and #230 by @clue)
85+86+## 1.1.1 (2020-01-01)
87+88+* Fix: Fix reporting connection refused errors with `ExtUvLoop` on Linux and `StreamSelectLoop` on Windows.
89+ (#207 and #208 by @clue)
90+91+* Fix: Fix unsupported EventConfig and `SEGFAULT` on shutdown with `ExtEventLoop` on Windows.
92+ (#205 by @clue)
93+94+* Fix: Prevent interval overflow for timers very far in the future with `ExtUvLoop`.
95+ (#196 by @PabloKowalczyk)
96+97+* Fix: Check PCNTL functions for signal support instead of PCNTL extension with `StreamSelectLoop`.
98+ (#195 by @clue)
99+100+* Add `.gitattributes` to exclude dev files from exports.
101+ (#201 by @reedy)
102+103+* Improve test suite to fix testing `ExtUvLoop` on Travis,
104+ fix Travis CI builds, do not install `libuv` on legacy PHP setups,
105+ fix failing test cases due to inaccurate timers,
106+ run tests on Windows via Travis CI and
107+ run tests on PHP 7.4 and simplify test matrix and test setup.
108+ (#197 by @WyriHaximus and #202, #203, #204 and #209 by @clue)
109+110+## 1.1.0 (2019-02-07)
111+112+* New UV based event loop (ext-uv).
113+ (#112 by @WyriHaximus)
114+115+* Use high resolution timer on PHP 7.3+.
116+ (#182 by @clue)
117+118+* Improve PCNTL signals by using async signal dispatching if available.
119+ (#179 by @CharlotteDunois)
120+121+* Improve test suite and test suite set up.
122+ (#174 by @WyriHaximus, #181 by @clue)
123+124+* Fix PCNTL signals edge case.
125+ (#183 by @clue)
126+127+## 1.0.0 (2018-07-11)
128+129+* First stable LTS release, now following [SemVer](https://semver.org/).
130+ We'd like to emphasize that this component is production ready and battle-tested.
131+ We plan to support all long-term support (LTS) releases for at least 24 months,
132+ so you have a rock-solid foundation to build on top of.
133+134+> Contains no other changes, so it's actually fully compatible with the v0.5.3 release.
135+136+## 0.5.3 (2018-07-09)
137+138+* Improve performance by importing global functions.
139+ (#167 by @Ocramius)
140+141+* Improve test suite by simplifying test bootstrap by using dev autoloader.
142+ (#169 by @lcobucci)
143+144+* Minor internal changes to improved backward compatibility with PHP 5.3.
145+ (#166 by @Donatello-za)
146+147+## 0.5.2 (2018-04-24)
148+149+* Feature: Improve memory consumption and runtime performance for `StreamSelectLoop` timers.
150+ (#164 by @clue)
151+152+* Improve test suite by removing I/O dependency at `StreamSelectLoopTest` to fix Mac OS X tests.
153+ (#161 by @nawarian)
154+155+## 0.5.1 (2018-04-09)
156+157+* Feature: New `ExtEvLoop` (PECL ext-ev) (#148 by @kaduev13)
158+159+## 0.5.0 (2018-04-05)
160+161+A major feature release with a significant documentation overhaul and long overdue API cleanup!
162+163+This update involves a number of BC breaks due to dropped support for deprecated
164+functionality. We've tried hard to avoid BC breaks where possible and minimize
165+impact otherwise. We expect that most consumers of this package will actually
166+not be affected by any BC breaks, see below for more details.
167+168+We realize that the changes listed below may seem overwhelming, but we've tried
169+to be very clear about any possible BC breaks. Don't worry: In fact, all ReactPHP
170+components are already compatible and support both this new release as well as
171+providing backwards compatibility with the last release.
172+173+* Feature / BC break: Add support for signal handling via new
174+ `LoopInterface::addSignal()` and `LoopInterface::removeSignal()` methods.
175+ (#104 by @WyriHaximus and #111 and #150 by @clue)
176+177+ ```php
178+ $loop->addSignal(SIGINT, function () {
179+ echo 'CTRL-C';
180+ });
181+ ```
182+183+* Feature: Significant documentation updates for `LoopInterface` and `Factory`.
184+ (#100, #119, #126, #127, #159 and #160 by @clue, #113 by @WyriHaximus and #81 and #91 by @jsor)
185+186+* Feature: Add examples to ease getting started
187+ (#99, #100 and #125 by @clue, #59 by @WyriHaximus and #143 by @jsor)
188+189+* Feature: Documentation for advanced timer concepts, such as monotonic time source vs wall-clock time
190+ and high precision timers with millisecond accuracy or below.
191+ (#130 and #157 by @clue)
192+193+* Feature: Documentation for advanced stream concepts, such as edge-triggered event listeners
194+ and stream buffers and allow throwing Exception if stream resource is not supported.
195+ (#129 and #158 by @clue)
196+197+* Feature: Throw `BadMethodCallException` on manual loop creation when required extension isn't installed.
198+ (#153 by @WyriHaximus)
199+200+* Feature / BC break: First class support for legacy PHP 5.3 through PHP 7.2 and HHVM
201+ and remove all `callable` type hints for consistency reasons.
202+ (#141 and #151 by @clue)
203+204+* BC break: Documentation for timer API and clean up unneeded timer API.
205+ (#102 by @clue)
206+207+ Remove `TimerInterface::cancel()`, use `LoopInterface::cancelTimer()` instead:
208+209+ ```php
210+ // old (method invoked on timer instance)
211+ $timer->cancel();
212+213+ // already supported before: invoke method on loop instance
214+ $loop->cancelTimer($timer);
215+ ```
216+217+ Remove unneeded `TimerInterface::setData()` and `TimerInterface::getData()`,
218+ use closure binding to add arbitrary data to timer instead:
219+220+ ```php
221+ // old (limited setData() and getData() only allows single variable)
222+ $name = 'Tester';
223+ $timer = $loop->addTimer(1.0, function ($timer) {
224+ echo 'Hello ' . $timer->getData() . PHP_EOL;
225+ });
226+ $timer->setData($name);
227+228+ // already supported before: closure binding allows any number of variables
229+ $name = 'Tester';
230+ $loop->addTimer(1.0, function () use ($name) {
231+ echo 'Hello ' . $name . PHP_EOL;
232+ });
233+ ```
234+235+ Remove unneeded `TimerInterface::getLoop()`, use closure binding instead:
236+237+ ```php
238+ // old (getLoop() called on timer instance)
239+ $loop->addTimer(0.1, function ($timer) {
240+ $timer->getLoop()->stop();
241+ });
242+243+ // already supported before: use closure binding as usual
244+ $loop->addTimer(0.1, function () use ($loop) {
245+ $loop->stop();
246+ });
247+ ```
248+249+* BC break: Remove unneeded `LoopInterface::isTimerActive()` and
250+ `TimerInterface::isActive()` to reduce API surface.
251+ (#133 by @clue)
252+253+ ```php
254+ // old (method on timer instance or on loop instance)
255+ $timer->isActive();
256+ $loop->isTimerActive($timer);
257+ ```
258+259+* BC break: Move `TimerInterface` one level up to `React\EventLoop\TimerInterface`.
260+ (#138 by @WyriHaximus)
261+262+ ```php
263+ // old (notice obsolete "Timer" namespace)
264+ assert($timer instanceof React\EventLoop\Timer\TimerInterface);
265+266+ // new
267+ assert($timer instanceof React\EventLoop\TimerInterface);
268+ ```
269+270+* BC break: Remove unneeded `LoopInterface::nextTick()` (and internal `NextTickQueue`),
271+ use `LoopInterface::futureTick()` instead.
272+ (#30 by @clue)
273+274+ ```php
275+ // old (removed)
276+ $loop->nextTick(function () {
277+ echo 'tick';
278+ });
279+280+ // already supported before
281+ $loop->futureTick(function () {
282+ echo 'tick';
283+ });
284+ ```
285+286+* BC break: Remove unneeded `$loop` argument for `LoopInterface::futureTick()`
287+ (and fix internal cyclic dependency).
288+ (#103 by @clue)
289+290+ ```php
291+ // old ($loop gets passed by default)
292+ $loop->futureTick(function ($loop) {
293+ $loop->stop();
294+ });
295+296+ // already supported before: use closure binding as usual
297+ $loop->futureTick(function () use ($loop) {
298+ $loop->stop();
299+ });
300+ ```
301+302+* BC break: Remove unneeded `LoopInterface::tick()`.
303+ (#72 by @jsor)
304+305+ ```php
306+ // old (removed)
307+ $loop->tick();
308+309+ // suggested work around for testing purposes only
310+ $loop->futureTick(function () use ($loop) {
311+ $loop->stop();
312+ });
313+ ```
314+315+* BC break: Documentation for advanced stream API and clean up unneeded stream API.
316+ (#110 by @clue)
317+318+ Remove unneeded `$loop` argument for `LoopInterface::addReadStream()`
319+ and `LoopInterface::addWriteStream()`, use closure binding instead:
320+321+ ```php
322+ // old ($loop gets passed by default)
323+ $loop->addReadStream($stream, function ($stream, $loop) {
324+ $loop->removeReadStream($stream);
325+ });
326+327+ // already supported before: use closure binding as usual
328+ $loop->addReadStream($stream, function ($stream) use ($loop) {
329+ $loop->removeReadStream($stream);
330+ });
331+ ```
332+333+* BC break: Remove unneeded `LoopInterface::removeStream()` method,
334+ use `LoopInterface::removeReadStream()` and `LoopInterface::removeWriteStream()` instead.
335+ (#118 by @clue)
336+337+ ```php
338+ // old
339+ $loop->removeStream($stream);
340+341+ // already supported before
342+ $loop->removeReadStream($stream);
343+ $loop->removeWriteStream($stream);
344+ ```
345+346+* BC break: Rename `LibEventLoop` to `ExtLibeventLoop` and `LibEvLoop` to `ExtLibevLoop`
347+ for consistent naming for event loop implementations.
348+ (#128 by @clue)
349+350+* BC break: Remove optional `EventBaseConfig` argument from `ExtEventLoop`
351+ and make its `FEATURE_FDS` enabled by default.
352+ (#156 by @WyriHaximus)
353+354+* BC break: Mark all classes as final to discourage inheritance.
355+ (#131 by @clue)
356+357+* Fix: Fix `ExtEventLoop` to keep track of stream resources (refcount)
358+ (#123 by @clue)
359+360+* Fix: Ensure large timer interval does not overflow on 32bit systems
361+ (#132 by @clue)
362+363+* Fix: Fix separately removing readable and writable side of stream when closing
364+ (#139 by @clue)
365+366+* Fix: Properly clean up event watchers for `ext-event` and `ext-libev`
367+ (#149 by @clue)
368+369+* Fix: Minor code cleanup and remove unneeded references
370+ (#145 by @seregazhuk)
371+372+* Fix: Discourage outdated `ext-libevent` on PHP 7
373+ (#62 by @cboden)
374+375+* Improve test suite by adding forward compatibility with PHPUnit 6 and PHPUnit 5,
376+ lock Travis distro so new defaults will not break the build,
377+ improve test suite to be less fragile and increase test timeouts,
378+ test against PHP 7.2 and reduce fwrite() call length to one chunk.
379+ (#106 and #144 by @clue, #120 and #124 by @carusogabriel, #147 by nawarian and #92 by @kelunik)
380+381+* A number of changes were originally planned for this release but have been backported
382+ to the last `v0.4.3` already: #74, #76, #79, #81 (refs #65, #66, #67), #88 and #93
383+384+## 0.4.3 (2017-04-27)
385+386+* Bug fix: Bugfix in the usage sample code #57 (@dandelionred)
387+* Improvement: Remove branch-alias definition #53 (@WyriHaximus)
388+* Improvement: StreamSelectLoop: Use fresh time so Timers added during stream events are accurate #51 (@andrewminerd)
389+* Improvement: Avoid deprecation warnings in test suite due to deprecation of getMock() in PHPUnit #68 (@martinschroeder)
390+* Improvement: Add PHPUnit 4.8 to require-dev #69 (@shaunbramley)
391+* Improvement: Increase test timeouts for HHVM and unify timeout handling #70 (@clue)
392+* Improvement: Travis improvements (backported from #74) #75 (@clue)
393+* Improvement: Test suite now uses socket pairs instead of memory streams #66 (@martinschroeder)
394+* Improvement: StreamSelectLoop: Test suite uses signal constant names in data provider #67 (@martinschroeder)
395+* Improvement: ExtEventLoop: No longer suppress all errors #65 (@mamciek)
396+* Improvement: Readme cleanup #89 (@jsor)
397+* Improvement: Restructure and improve README #90 (@jsor)
398+* Bug fix: StreamSelectLoop: Fix erroneous zero-time sleep (backport to 0.4) #94 (@jsor)
399+400+## 0.4.2 (2016-03-07)
401+402+* Bug fix: No longer error when signals sent to StreamSelectLoop
403+* Support HHVM and PHP7 (@ondrejmirtes, @cebe)
404+* Feature: Added support for EventConfig for ExtEventLoop (@steverhoades)
405+* Bug fix: Fixed an issue loading loop extension libs via autoloader (@czarpino)
406+407+## 0.4.1 (2014-04-13)
408+409+* Bug fix: null timeout in StreamSelectLoop causing 100% CPU usage (@clue)
410+* Bug fix: v0.3.4 changes merged for v0.4.1
411+412+## 0.4.0 (2014-02-02)
413+414+* Feature: Added `EventLoopInterface::nextTick()`, implemented in all event loops (@jmalloc)
415+* Feature: Added `EventLoopInterface::futureTick()`, implemented in all event loops (@jmalloc)
416+* Feature: Added `ExtEventLoop` implementation using pecl/event (@jmalloc)
417+* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks
418+* BC break: New method: `EventLoopInterface::nextTick()`
419+* BC break: New method: `EventLoopInterface::futureTick()`
420+* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0
421+422+## 0.3.5 (2016-12-28)
423+424+This is a compatibility release that eases upgrading to the v0.4 release branch.
425+You should consider upgrading to the v0.4 release branch.
426+427+* Feature: Cap min timer interval at 1µs, thus improving compatibility with v0.4
428+ (#47 by @clue)
429+430+## 0.3.4 (2014-03-30)
431+432+* Bug fix: Changed StreamSelectLoop to use non-blocking behavior on tick() (@astephens25)
433+434+## 0.3.3 (2013-07-08)
435+436+* Bug fix: No error on removing non-existent streams (@clue)
437+* Bug fix: Do not silently remove feof listeners in `LibEvLoop`
438+439+## 0.3.0 (2013-04-14)
440+441+* BC break: New timers API (@nrk)
442+* BC break: Remove check on return value from stream callbacks (@nrk)
443+444+## 0.2.7 (2013-01-05)
445+446+* Bug fix: Fix libevent timers with PHP 5.3
447+* Bug fix: Fix libevent timer cancellation (@nrk)
448+449+## 0.2.6 (2012-12-26)
450+451+* Bug fix: Plug memory issue in libevent timers (@cameronjacobson)
452+* Bug fix: Correctly pause LibEvLoop on stop()
453+454+## 0.2.3 (2012-11-14)
455+456+* Feature: LibEvLoop, integration of `php-libev`
457+458+## 0.2.0 (2012-09-10)
459+460+* Version bump
461+462+## 0.1.1 (2012-07-12)
463+464+* Version bump
465+466+## 0.1.0 (2012-07-11)
467+468+* First tagged release
+21
vendor/react/event-loop/LICENSE
···000000000000000000000
···1+The MIT License (MIT)
2+3+Copyright (c) 2012 Christian Lück, Cees-Jan Kiewiet, Jan Sorgalla, Chris Boden, Igor Wiedler
4+5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is furnished
10+to do so, subject to the following conditions:
11+12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+THE SOFTWARE.
···1+# EventLoop
2+3+[](https://github.com/reactphp/event-loop/actions)
4+[](https://packagist.org/packages/react/event-loop)
5+6+[ReactPHP](https://reactphp.org/)'s core reactor event loop that libraries can use for evented I/O.
7+8+In order for async based libraries to be interoperable, they need to use the
9+same event loop. This component provides a common `LoopInterface` that any
10+library can target. This allows them to be used in the same loop, with one
11+single [`run()`](#run) call that is controlled by the user.
12+13+**Table of contents**
14+15+* [Quickstart example](#quickstart-example)
16+* [Usage](#usage)
17+ * [Loop](#loop)
18+ * [Loop methods](#loop-methods)
19+ * [Loop autorun](#loop-autorun)
20+ * [get()](#get)
21+ * [~~Factory~~](#factory)
22+ * [~~create()~~](#create)
23+ * [Loop implementations](#loop-implementations)
24+ * [StreamSelectLoop](#streamselectloop)
25+ * [ExtEventLoop](#exteventloop)
26+ * [ExtEvLoop](#extevloop)
27+ * [ExtUvLoop](#extuvloop)
28+ * [~~ExtLibeventLoop~~](#extlibeventloop)
29+ * [~~ExtLibevLoop~~](#extlibevloop)
30+ * [LoopInterface](#loopinterface)
31+ * [run()](#run)
32+ * [stop()](#stop)
33+ * [addTimer()](#addtimer)
34+ * [addPeriodicTimer()](#addperiodictimer)
35+ * [cancelTimer()](#canceltimer)
36+ * [futureTick()](#futuretick)
37+ * [addSignal()](#addsignal)
38+ * [removeSignal()](#removesignal)
39+ * [addReadStream()](#addreadstream)
40+ * [addWriteStream()](#addwritestream)
41+ * [removeReadStream()](#removereadstream)
42+ * [removeWriteStream()](#removewritestream)
43+* [Install](#install)
44+* [Tests](#tests)
45+* [License](#license)
46+* [More](#more)
47+48+## Quickstart example
49+50+Here is an async HTTP server built with just the event loop.
51+52+```php
53+<?php
54+55+use React\EventLoop\Loop;
56+57+require __DIR__ . '/vendor/autoload.php';
58+59+$server = stream_socket_server('tcp://127.0.0.1:8080');
60+stream_set_blocking($server, false);
61+62+Loop::addReadStream($server, function ($server) {
63+ $conn = stream_socket_accept($server);
64+ $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
65+ Loop::addWriteStream($conn, function ($conn) use (&$data) {
66+ $written = fwrite($conn, $data);
67+ if ($written === strlen($data)) {
68+ fclose($conn);
69+ Loop::removeWriteStream($conn);
70+ } else {
71+ $data = substr($data, $written);
72+ }
73+ });
74+});
75+76+Loop::addPeriodicTimer(5, function () {
77+ $memory = memory_get_usage() / 1024;
78+ $formatted = number_format($memory, 3).'K';
79+ echo "Current memory usage: {$formatted}\n";
80+});
81+```
82+83+See also the [examples](examples).
84+85+## Usage
86+87+Typical applications would use the [`Loop` class](#loop) to use the default
88+event loop like this:
89+90+```php
91+use React\EventLoop\Loop;
92+93+$timer = Loop::addPeriodicTimer(0.1, function () {
94+ echo 'Tick' . PHP_EOL;
95+});
96+97+Loop::addTimer(1.0, function () use ($timer) {
98+ Loop::cancelTimer($timer);
99+ echo 'Done' . PHP_EOL;
100+});
101+```
102+103+As an alternative, you can also explicitly create an event loop instance at the
104+beginning, reuse it throughout your program and finally run it at the end of the
105+program like this:
106+107+```php
108+$loop = React\EventLoop\Loop::get(); // or deprecated React\EventLoop\Factory::create();
109+110+$timer = $loop->addPeriodicTimer(0.1, function () {
111+ echo 'Tick' . PHP_EOL;
112+});
113+114+$loop->addTimer(1.0, function () use ($loop, $timer) {
115+ $loop->cancelTimer($timer);
116+ echo 'Done' . PHP_EOL;
117+});
118+119+$loop->run();
120+```
121+122+While the former is more concise, the latter is more explicit.
123+In both cases, the program would perform the exact same steps.
124+125+1. The event loop instance is created at the beginning of the program. This is
126+ implicitly done the first time you call the [`Loop` class](#loop) or
127+ explicitly when using the deprecated [`Factory::create()` method](#create)
128+ (or manually instantiating any of the [loop implementations](#loop-implementations)).
129+2. The event loop is used directly or passed as an instance to library and
130+ application code. In this example, a periodic timer is registered with the
131+ event loop which simply outputs `Tick` every fraction of a second until another
132+ timer stops the periodic timer after a second.
133+3. The event loop is run at the end of the program. This is automatically done
134+ when using the [`Loop` class](#loop) or explicitly with a single [`run()`](#run)
135+ call at the end of the program.
136+137+As of `v1.2.0`, we highly recommend using the [`Loop` class](#loop).
138+The explicit loop instructions are still valid and may still be useful in some
139+applications, especially for a transition period towards the more concise style.
140+141+### Loop
142+143+The `Loop` class exists as a convenient global accessor for the event loop.
144+145+#### Loop methods
146+147+The `Loop` class provides all methods that exist on the [`LoopInterface`](#loopinterface)
148+as static methods:
149+150+* [run()](#run)
151+* [stop()](#stop)
152+* [addTimer()](#addtimer)
153+* [addPeriodicTimer()](#addperiodictimer)
154+* [cancelTimer()](#canceltimer)
155+* [futureTick()](#futuretick)
156+* [addSignal()](#addsignal)
157+* [removeSignal()](#removesignal)
158+* [addReadStream()](#addreadstream)
159+* [addWriteStream()](#addwritestream)
160+* [removeReadStream()](#removereadstream)
161+* [removeWriteStream()](#removewritestream)
162+163+If you're working with the event loop in your application code, it's often
164+easiest to directly interface with the static methods defined on the `Loop` class
165+like this:
166+167+```php
168+use React\EventLoop\Loop;
169+170+$timer = Loop::addPeriodicTimer(0.1, function () {
171+ echo 'Tick' . PHP_EOL;
172+});
173+174+Loop::addTimer(1.0, function () use ($timer) {
175+ Loop::cancelTimer($timer);
176+ echo 'Done' . PHP_EOL;
177+});
178+```
179+180+On the other hand, if you're familiar with object-oriented programming (OOP) and
181+dependency injection (DI), you may want to inject an event loop instance and
182+invoke instance methods on the `LoopInterface` like this:
183+184+```php
185+use React\EventLoop\Loop;
186+use React\EventLoop\LoopInterface;
187+188+class Greeter
189+{
190+ private $loop;
191+192+ public function __construct(LoopInterface $loop)
193+ {
194+ $this->loop = $loop;
195+ }
196+197+ public function greet(string $name)
198+ {
199+ $this->loop->addTimer(1.0, function () use ($name) {
200+ echo 'Hello ' . $name . '!' . PHP_EOL;
201+ });
202+ }
203+}
204+205+$greeter = new Greeter(Loop::get());
206+$greeter->greet('Alice');
207+$greeter->greet('Bob');
208+```
209+210+Each static method call will be forwarded as-is to the underlying event loop
211+instance by using the [`Loop::get()`](#get) call internally.
212+See [`LoopInterface`](#loopinterface) for more details about available methods.
213+214+#### Loop autorun
215+216+When using the `Loop` class, it will automatically execute the loop at the end of
217+the program. This means the following example will schedule a timer and will
218+automatically execute the program until the timer event fires:
219+220+```php
221+use React\EventLoop\Loop;
222+223+Loop::addTimer(1.0, function () {
224+ echo 'Hello' . PHP_EOL;
225+});
226+```
227+228+As of `v1.2.0`, we highly recommend using the `Loop` class this way and omitting any
229+explicit [`run()`](#run) calls. For BC reasons, the explicit [`run()`](#run)
230+method is still valid and may still be useful in some applications, especially
231+for a transition period towards the more concise style.
232+233+If you don't want the `Loop` to run automatically, you can either explicitly
234+[`run()`](#run) or [`stop()`](#stop) it. This can be useful if you're using
235+a global exception handler like this:
236+237+```php
238+use React\EventLoop\Loop;
239+240+Loop::addTimer(10.0, function () {
241+ echo 'Never happens';
242+});
243+244+set_exception_handler(function (Throwable $e) {
245+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
246+ Loop::stop();
247+});
248+249+throw new RuntimeException('Demo');
250+```
251+252+#### get()
253+254+The `get(): LoopInterface` method can be used to
255+get the currently active event loop instance.
256+257+This method will always return the same event loop instance throughout the
258+lifetime of your application.
259+260+```php
261+use React\EventLoop\Loop;
262+use React\EventLoop\LoopInterface;
263+264+$loop = Loop::get();
265+266+assert($loop instanceof LoopInterface);
267+assert($loop === Loop::get());
268+```
269+270+This is particularly useful if you're using object-oriented programming (OOP)
271+and dependency injection (DI). In this case, you may want to inject an event
272+loop instance and invoke instance methods on the `LoopInterface` like this:
273+274+```php
275+use React\EventLoop\Loop;
276+use React\EventLoop\LoopInterface;
277+278+class Greeter
279+{
280+ private $loop;
281+282+ public function __construct(LoopInterface $loop)
283+ {
284+ $this->loop = $loop;
285+ }
286+287+ public function greet(string $name)
288+ {
289+ $this->loop->addTimer(1.0, function () use ($name) {
290+ echo 'Hello ' . $name . '!' . PHP_EOL;
291+ });
292+ }
293+}
294+295+$greeter = new Greeter(Loop::get());
296+$greeter->greet('Alice');
297+$greeter->greet('Bob');
298+```
299+300+See [`LoopInterface`](#loopinterface) for more details about available methods.
301+302+### ~~Factory~~
303+304+> Deprecated since v1.2.0, see [`Loop` class](#loop) instead.
305+306+The deprecated `Factory` class exists as a convenient way to pick the best available
307+[event loop implementation](#loop-implementations).
308+309+#### ~~create()~~
310+311+> Deprecated since v1.2.0, see [`Loop::get()`](#get) instead.
312+313+The deprecated `create(): LoopInterface` method can be used to
314+create a new event loop instance:
315+316+```php
317+// deprecated
318+$loop = React\EventLoop\Factory::create();
319+320+// new
321+$loop = React\EventLoop\Loop::get();
322+```
323+324+This method always returns an instance implementing [`LoopInterface`](#loopinterface),
325+the actual [event loop implementation](#loop-implementations) is an implementation detail.
326+327+This method should usually only be called once at the beginning of the program.
328+329+### Loop implementations
330+331+In addition to the [`LoopInterface`](#loopinterface), there are a number of
332+event loop implementations provided.
333+334+All of the event loops support these features:
335+336+* File descriptor polling
337+* One-off timers
338+* Periodic timers
339+* Deferred execution on future loop tick
340+341+For most consumers of this package, the underlying event loop implementation is
342+an implementation detail.
343+You should use the [`Loop` class](#loop) to automatically create a new instance.
344+345+Advanced! If you explicitly need a certain event loop implementation, you can
346+manually instantiate one of the following classes.
347+Note that you may have to install the required PHP extensions for the respective
348+event loop implementation first or they will throw a `BadMethodCallException` on creation.
349+350+#### StreamSelectLoop
351+352+A `stream_select()` based event loop.
353+354+This uses the [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php)
355+function and is the only implementation that works out of the box with PHP.
356+357+This event loop works out of the box on PHP 5.3 through PHP 8+ and HHVM.
358+This means that no installation is required and this library works on all
359+platforms and supported PHP versions.
360+Accordingly, the [`Loop` class](#loop) and the deprecated [`Factory`](#factory)
361+will use this event loop by default if you do not install any of the event loop
362+extensions listed below.
363+364+Under the hood, it does a simple `select` system call.
365+This system call is limited to the maximum file descriptor number of
366+`FD_SETSIZE` (platform dependent, commonly 1024) and scales with `O(m)`
367+(`m` being the maximum file descriptor number passed).
368+This means that you may run into issues when handling thousands of streams
369+concurrently and you may want to look into using one of the alternative
370+event loop implementations listed below in this case.
371+If your use case is among the many common use cases that involve handling only
372+dozens or a few hundred streams at once, then this event loop implementation
373+performs really well.
374+375+If you want to use signal handling (see also [`addSignal()`](#addsignal) below),
376+this event loop implementation requires `ext-pcntl`.
377+This extension is only available for Unix-like platforms and does not support
378+Windows.
379+It is commonly installed as part of many PHP distributions.
380+If this extension is missing (or you're running on Windows), signal handling is
381+not supported and throws a `BadMethodCallException` instead.
382+383+This event loop is known to rely on wall-clock time to schedule future timers
384+when using any version before PHP 7.3, because a monotonic time source is
385+only available as of PHP 7.3 (`hrtime()`).
386+While this does not affect many common use cases, this is an important
387+distinction for programs that rely on a high time precision or on systems
388+that are subject to discontinuous time adjustments (time jumps).
389+This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and
390+then adjust your system time forward by 20s, the timer may trigger in 10s.
391+See also [`addTimer()`](#addtimer) for more details.
392+393+#### ExtEventLoop
394+395+An `ext-event` based event loop.
396+397+This uses the [`event` PECL extension](https://pecl.php.net/package/event),
398+that provides an interface to `libevent` library.
399+`libevent` itself supports a number of system-specific backends (epoll, kqueue).
400+401+This loop is known to work with PHP 5.4 through PHP 8+.
402+403+#### ExtEvLoop
404+405+An `ext-ev` based event loop.
406+407+This loop uses the [`ev` PECL extension](https://pecl.php.net/package/ev),
408+that provides an interface to `libev` library.
409+`libev` itself supports a number of system-specific backends (epoll, kqueue).
410+411+412+This loop is known to work with PHP 5.4 through PHP 8+.
413+414+#### ExtUvLoop
415+416+An `ext-uv` based event loop.
417+418+This loop uses the [`uv` PECL extension](https://pecl.php.net/package/uv),
419+that provides an interface to `libuv` library.
420+`libuv` itself supports a number of system-specific backends (epoll, kqueue).
421+422+This loop is known to work with PHP 7+.
423+424+#### ~~ExtLibeventLoop~~
425+426+> Deprecated since v1.2.0, use [`ExtEventLoop`](#exteventloop) instead.
427+428+An `ext-libevent` based event loop.
429+430+This uses the [`libevent` PECL extension](https://pecl.php.net/package/libevent),
431+that provides an interface to `libevent` library.
432+`libevent` itself supports a number of system-specific backends (epoll, kqueue).
433+434+This event loop does only work with PHP 5.
435+An [unofficial update](https://github.com/php/pecl-event-libevent/pull/2) for
436+PHP 7 does exist, but it is known to cause regular crashes due to `SEGFAULT`s.
437+To reiterate: Using this event loop on PHP 7 is not recommended.
438+Accordingly, neither the [`Loop` class](#loop) nor the deprecated
439+[`Factory` class](#factory) will try to use this event loop on PHP 7.
440+441+This event loop is known to trigger a readable listener only if
442+the stream *becomes* readable (edge-triggered) and may not trigger if the
443+stream has already been readable from the beginning.
444+This also implies that a stream may not be recognized as readable when data
445+is still left in PHP's internal stream buffers.
446+As such, it's recommended to use `stream_set_read_buffer($stream, 0);`
447+to disable PHP's internal read buffer in this case.
448+See also [`addReadStream()`](#addreadstream) for more details.
449+450+#### ~~ExtLibevLoop~~
451+452+> Deprecated since v1.2.0, use [`ExtEvLoop`](#extevloop) instead.
453+454+An `ext-libev` based event loop.
455+456+This uses an [unofficial `libev` extension](https://github.com/m4rw3r/php-libev),
457+that provides an interface to `libev` library.
458+`libev` itself supports a number of system-specific backends (epoll, kqueue).
459+460+This loop does only work with PHP 5.
461+An update for PHP 7 is [unlikely](https://github.com/m4rw3r/php-libev/issues/8)
462+to happen any time soon.
463+464+### LoopInterface
465+466+#### run()
467+468+The `run(): void` method can be used to
469+run the event loop until there are no more tasks to perform.
470+471+For many applications, this method is the only directly visible
472+invocation on the event loop.
473+As a rule of thumb, it is usually recommended to attach everything to the
474+same loop instance and then run the loop once at the bottom end of the
475+application.
476+477+```php
478+$loop->run();
479+```
480+481+This method will keep the loop running until there are no more tasks
482+to perform. In other words: This method will block until the last
483+timer, stream and/or signal has been removed.
484+485+Likewise, it is imperative to ensure the application actually invokes
486+this method once. Adding listeners to the loop and missing to actually
487+run it will result in the application exiting without actually waiting
488+for any of the attached listeners.
489+490+This method MUST NOT be called while the loop is already running.
491+This method MAY be called more than once after it has explicitly been
492+[`stop()`ped](#stop) or after it automatically stopped because it
493+previously did no longer have anything to do.
494+495+#### stop()
496+497+The `stop(): void` method can be used to
498+instruct a running event loop to stop.
499+500+This method is considered advanced usage and should be used with care.
501+As a rule of thumb, it is usually recommended to let the loop stop
502+only automatically when it no longer has anything to do.
503+504+This method can be used to explicitly instruct the event loop to stop:
505+506+```php
507+$loop->addTimer(3.0, function () use ($loop) {
508+ $loop->stop();
509+});
510+```
511+512+Calling this method on a loop instance that is not currently running or
513+on a loop instance that has already been stopped has no effect.
514+515+#### addTimer()
516+517+The `addTimer(float $interval, callable $callback): TimerInterface` method can be used to
518+enqueue a callback to be invoked once after the given interval.
519+520+The second parameter MUST be a timer callback function that accepts
521+the timer instance as its only parameter.
522+If you don't use the timer instance inside your timer callback function
523+you MAY use a function which has no parameters at all.
524+525+The timer callback function MUST NOT throw an `Exception`.
526+The return value of the timer callback function will be ignored and has
527+no effect, so for performance reasons you're recommended to not return
528+any excessive data structures.
529+530+This method returns a timer instance. The same timer instance will also be
531+passed into the timer callback function as described above.
532+You can invoke [`cancelTimer`](#canceltimer) to cancel a pending timer.
533+Unlike [`addPeriodicTimer()`](#addperiodictimer), this method will ensure
534+the callback will be invoked only once after the given interval.
535+536+```php
537+$loop->addTimer(0.8, function () {
538+ echo 'world!' . PHP_EOL;
539+});
540+541+$loop->addTimer(0.3, function () {
542+ echo 'hello ';
543+});
544+```
545+546+See also [example #1](examples).
547+548+If you want to access any variables within your callback function, you
549+can bind arbitrary data to a callback closure like this:
550+551+```php
552+function hello($name, LoopInterface $loop)
553+{
554+ $loop->addTimer(1.0, function () use ($name) {
555+ echo "hello $name\n";
556+ });
557+}
558+559+hello('Tester', $loop);
560+```
561+562+This interface does not enforce any particular timer resolution, so
563+special care may have to be taken if you rely on very high precision with
564+millisecond accuracy or below. Event loop implementations SHOULD work on
565+a best effort basis and SHOULD provide at least millisecond accuracy
566+unless otherwise noted. Many existing event loop implementations are
567+known to provide microsecond accuracy, but it's generally not recommended
568+to rely on this high precision.
569+570+Similarly, the execution order of timers scheduled to execute at the
571+same time (within its possible accuracy) is not guaranteed.
572+573+This interface suggests that event loop implementations SHOULD use a
574+monotonic time source if available. Given that a monotonic time source is
575+only available as of PHP 7.3 by default, event loop implementations MAY
576+fall back to using wall-clock time.
577+While this does not affect many common use cases, this is an important
578+distinction for programs that rely on a high time precision or on systems
579+that are subject to discontinuous time adjustments (time jumps).
580+This means that if you schedule a timer to trigger in 30s and then adjust
581+your system time forward by 20s, the timer SHOULD still trigger in 30s.
582+See also [event loop implementations](#loop-implementations) for more details.
583+584+#### addPeriodicTimer()
585+586+The `addPeriodicTimer(float $interval, callable $callback): TimerInterface` method can be used to
587+enqueue a callback to be invoked repeatedly after the given interval.
588+589+The second parameter MUST be a timer callback function that accepts
590+the timer instance as its only parameter.
591+If you don't use the timer instance inside your timer callback function
592+you MAY use a function which has no parameters at all.
593+594+The timer callback function MUST NOT throw an `Exception`.
595+The return value of the timer callback function will be ignored and has
596+no effect, so for performance reasons you're recommended to not return
597+any excessive data structures.
598+599+This method returns a timer instance. The same timer instance will also be
600+passed into the timer callback function as described above.
601+Unlike [`addTimer()`](#addtimer), this method will ensure the callback
602+will be invoked infinitely after the given interval or until you invoke
603+[`cancelTimer`](#canceltimer).
604+605+```php
606+$timer = $loop->addPeriodicTimer(0.1, function () {
607+ echo 'tick!' . PHP_EOL;
608+});
609+610+$loop->addTimer(1.0, function () use ($loop, $timer) {
611+ $loop->cancelTimer($timer);
612+ echo 'Done' . PHP_EOL;
613+});
614+```
615+616+See also [example #2](examples).
617+618+If you want to limit the number of executions, you can bind
619+arbitrary data to a callback closure like this:
620+621+```php
622+function hello($name, LoopInterface $loop)
623+{
624+ $n = 3;
625+ $loop->addPeriodicTimer(1.0, function ($timer) use ($name, $loop, &$n) {
626+ if ($n > 0) {
627+ --$n;
628+ echo "hello $name\n";
629+ } else {
630+ $loop->cancelTimer($timer);
631+ }
632+ });
633+}
634+635+hello('Tester', $loop);
636+```
637+638+This interface does not enforce any particular timer resolution, so
639+special care may have to be taken if you rely on very high precision with
640+millisecond accuracy or below. Event loop implementations SHOULD work on
641+a best effort basis and SHOULD provide at least millisecond accuracy
642+unless otherwise noted. Many existing event loop implementations are
643+known to provide microsecond accuracy, but it's generally not recommended
644+to rely on this high precision.
645+646+Similarly, the execution order of timers scheduled to execute at the
647+same time (within its possible accuracy) is not guaranteed.
648+649+This interface suggests that event loop implementations SHOULD use a
650+monotonic time source if available. Given that a monotonic time source is
651+only available as of PHP 7.3 by default, event loop implementations MAY
652+fall back to using wall-clock time.
653+While this does not affect many common use cases, this is an important
654+distinction for programs that rely on a high time precision or on systems
655+that are subject to discontinuous time adjustments (time jumps).
656+This means that if you schedule a timer to trigger in 30s and then adjust
657+your system time forward by 20s, the timer SHOULD still trigger in 30s.
658+See also [event loop implementations](#loop-implementations) for more details.
659+660+Additionally, periodic timers may be subject to timer drift due to
661+re-scheduling after each invocation. As such, it's generally not
662+recommended to rely on this for high precision intervals with millisecond
663+accuracy or below.
664+665+#### cancelTimer()
666+667+The `cancelTimer(TimerInterface $timer): void` method can be used to
668+cancel a pending timer.
669+670+See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples).
671+672+Calling this method on a timer instance that has not been added to this
673+loop instance or on a timer that has already been cancelled has no effect.
674+675+#### futureTick()
676+677+The `futureTick(callable $listener): void` method can be used to
678+schedule a callback to be invoked on a future tick of the event loop.
679+680+This works very much similar to timers with an interval of zero seconds,
681+but does not require the overhead of scheduling a timer queue.
682+683+The tick callback function MUST be able to accept zero parameters.
684+685+The tick callback function MUST NOT throw an `Exception`.
686+The return value of the tick callback function will be ignored and has
687+no effect, so for performance reasons you're recommended to not return
688+any excessive data structures.
689+690+If you want to access any variables within your callback function, you
691+can bind arbitrary data to a callback closure like this:
692+693+```php
694+function hello($name, LoopInterface $loop)
695+{
696+ $loop->futureTick(function () use ($name) {
697+ echo "hello $name\n";
698+ });
699+}
700+701+hello('Tester', $loop);
702+```
703+704+Unlike timers, tick callbacks are guaranteed to be executed in the order
705+they are enqueued.
706+Also, once a callback is enqueued, there's no way to cancel this operation.
707+708+This is often used to break down bigger tasks into smaller steps (a form
709+of cooperative multitasking).
710+711+```php
712+$loop->futureTick(function () {
713+ echo 'b';
714+});
715+$loop->futureTick(function () {
716+ echo 'c';
717+});
718+echo 'a';
719+```
720+721+See also [example #3](examples).
722+723+#### addSignal()
724+725+The `addSignal(int $signal, callable $listener): void` method can be used to
726+register a listener to be notified when a signal has been caught by this process.
727+728+This is useful to catch user interrupt signals or shutdown signals from
729+tools like `supervisor` or `systemd`.
730+731+The second parameter MUST be a listener callback function that accepts
732+the signal as its only parameter.
733+If you don't use the signal inside your listener callback function
734+you MAY use a function which has no parameters at all.
735+736+The listener callback function MUST NOT throw an `Exception`.
737+The return value of the listener callback function will be ignored and has
738+no effect, so for performance reasons you're recommended to not return
739+any excessive data structures.
740+741+```php
742+$loop->addSignal(SIGINT, function (int $signal) {
743+ echo 'Caught user interrupt signal' . PHP_EOL;
744+});
745+```
746+747+See also [example #4](examples).
748+749+Signaling is only available on Unix-like platforms, Windows isn't
750+supported due to operating system limitations.
751+This method may throw a `BadMethodCallException` if signals aren't
752+supported on this platform, for example when required extensions are
753+missing.
754+755+**Note: A listener can only be added once to the same signal, any
756+attempts to add it more than once will be ignored.**
757+758+#### removeSignal()
759+760+The `removeSignal(int $signal, callable $listener): void` method can be used to
761+remove a previously added signal listener.
762+763+```php
764+$loop->removeSignal(SIGINT, $listener);
765+```
766+767+Any attempts to remove listeners that aren't registered will be ignored.
768+769+#### addReadStream()
770+771+> Advanced! Note that this low-level API is considered advanced usage.
772+ Most use cases should probably use the higher-level
773+ [readable Stream API](https://github.com/reactphp/stream#readablestreaminterface)
774+ instead.
775+776+The `addReadStream(resource $stream, callable $callback): void` method can be used to
777+register a listener to be notified when a stream is ready to read.
778+779+The first parameter MUST be a valid stream resource that supports
780+checking whether it is ready to read by this loop implementation.
781+A single stream resource MUST NOT be added more than once.
782+Instead, either call [`removeReadStream()`](#removereadstream) first or
783+react to this event with a single listener and then dispatch from this
784+listener. This method MAY throw an `Exception` if the given resource type
785+is not supported by this loop implementation.
786+787+The second parameter MUST be a listener callback function that accepts
788+the stream resource as its only parameter.
789+If you don't use the stream resource inside your listener callback function
790+you MAY use a function which has no parameters at all.
791+792+The listener callback function MUST NOT throw an `Exception`.
793+The return value of the listener callback function will be ignored and has
794+no effect, so for performance reasons you're recommended to not return
795+any excessive data structures.
796+797+If you want to access any variables within your callback function, you
798+can bind arbitrary data to a callback closure like this:
799+800+```php
801+$loop->addReadStream($stream, function ($stream) use ($name) {
802+ echo $name . ' said: ' . fread($stream);
803+});
804+```
805+806+See also [example #11](examples).
807+808+You can invoke [`removeReadStream()`](#removereadstream) to remove the
809+read event listener for this stream.
810+811+The execution order of listeners when multiple streams become ready at
812+the same time is not guaranteed.
813+814+Some event loop implementations are known to only trigger the listener if
815+the stream *becomes* readable (edge-triggered) and may not trigger if the
816+stream has already been readable from the beginning.
817+This also implies that a stream may not be recognized as readable when data
818+is still left in PHP's internal stream buffers.
819+As such, it's recommended to use `stream_set_read_buffer($stream, 0);`
820+to disable PHP's internal read buffer in this case.
821+822+#### addWriteStream()
823+824+> Advanced! Note that this low-level API is considered advanced usage.
825+ Most use cases should probably use the higher-level
826+ [writable Stream API](https://github.com/reactphp/stream#writablestreaminterface)
827+ instead.
828+829+The `addWriteStream(resource $stream, callable $callback): void` method can be used to
830+register a listener to be notified when a stream is ready to write.
831+832+The first parameter MUST be a valid stream resource that supports
833+checking whether it is ready to write by this loop implementation.
834+A single stream resource MUST NOT be added more than once.
835+Instead, either call [`removeWriteStream()`](#removewritestream) first or
836+react to this event with a single listener and then dispatch from this
837+listener. This method MAY throw an `Exception` if the given resource type
838+is not supported by this loop implementation.
839+840+The second parameter MUST be a listener callback function that accepts
841+the stream resource as its only parameter.
842+If you don't use the stream resource inside your listener callback function
843+you MAY use a function which has no parameters at all.
844+845+The listener callback function MUST NOT throw an `Exception`.
846+The return value of the listener callback function will be ignored and has
847+no effect, so for performance reasons you're recommended to not return
848+any excessive data structures.
849+850+If you want to access any variables within your callback function, you
851+can bind arbitrary data to a callback closure like this:
852+853+```php
854+$loop->addWriteStream($stream, function ($stream) use ($name) {
855+ fwrite($stream, 'Hello ' . $name);
856+});
857+```
858+859+See also [example #12](examples).
860+861+You can invoke [`removeWriteStream()`](#removewritestream) to remove the
862+write event listener for this stream.
863+864+The execution order of listeners when multiple streams become ready at
865+the same time is not guaranteed.
866+867+#### removeReadStream()
868+869+The `removeReadStream(resource $stream): void` method can be used to
870+remove the read event listener for the given stream.
871+872+Removing a stream from the loop that has already been removed or trying
873+to remove a stream that was never added or is invalid has no effect.
874+875+#### removeWriteStream()
876+877+The `removeWriteStream(resource $stream): void` method can be used to
878+remove the write event listener for the given stream.
879+880+Removing a stream from the loop that has already been removed or trying
881+to remove a stream that was never added or is invalid has no effect.
882+883+## Install
884+885+The recommended way to install this library is [through Composer](https://getcomposer.org/).
886+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
887+888+This project follows [SemVer](https://semver.org/).
889+This will install the latest supported version:
890+891+```bash
892+composer require react/event-loop:^1.5
893+```
894+895+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
896+897+This project aims to run on any platform and thus does not require any PHP
898+extensions and supports running on legacy PHP 5.3 through current PHP 8+ and
899+HHVM.
900+It's *highly recommended to use the latest supported PHP version* for this project.
901+902+Installing any of the event loop extensions is suggested, but entirely optional.
903+See also [event loop implementations](#loop-implementations) for more details.
904+905+## Tests
906+907+To run the test suite, you first need to clone this repo and then install all
908+dependencies [through Composer](https://getcomposer.org/):
909+910+```bash
911+composer install
912+```
913+914+To run the test suite, go to the project root and run:
915+916+```bash
917+vendor/bin/phpunit
918+```
919+920+## License
921+922+MIT, see [LICENSE file](LICENSE).
923+924+## More
925+926+* See our [Stream component](https://github.com/reactphp/stream) for more
927+ information on how streams are used in real-world applications.
928+* See our [users wiki](https://github.com/reactphp/react/wiki/Users) and the
929+ [dependents on Packagist](https://packagist.org/packages/react/event-loop/dependents)
930+ for a list of packages that use the EventLoop in real-world applications.
···1+<?php
2+3+namespace React\EventLoop;
4+5+/**
6+ * [Deprecated] The `Factory` class exists as a convenient way to pick the best available event loop implementation.
7+ *
8+ * @deprecated 1.2.0 See Loop instead.
9+ * @see Loop
10+ */
11+final class Factory
12+{
13+ /**
14+ * [Deprecated] Creates a new event loop instance
15+ *
16+ * ```php
17+ * // deprecated
18+ * $loop = React\EventLoop\Factory::create();
19+ *
20+ * // new
21+ * $loop = React\EventLoop\Loop::get();
22+ * ```
23+ *
24+ * This method always returns an instance implementing `LoopInterface`,
25+ * the actual event loop implementation is an implementation detail.
26+ *
27+ * This method should usually only be called once at the beginning of the program.
28+ *
29+ * @deprecated 1.2.0 See Loop::get() instead.
30+ * @see Loop::get()
31+ *
32+ * @return LoopInterface
33+ */
34+ public static function create()
35+ {
36+ $loop = self::construct();
37+38+ Loop::set($loop);
39+40+ return $loop;
41+ }
42+43+ /**
44+ * @internal
45+ * @return LoopInterface
46+ */
47+ private static function construct()
48+ {
49+ // @codeCoverageIgnoreStart
50+ if (\function_exists('uv_loop_new')) {
51+ // only use ext-uv on PHP 7
52+ return new ExtUvLoop();
53+ }
54+55+ if (\class_exists('libev\EventLoop', false)) {
56+ return new ExtLibevLoop();
57+ }
58+59+ if (\class_exists('EvLoop', false)) {
60+ return new ExtEvLoop();
61+ }
62+63+ if (\class_exists('EventBase', false)) {
64+ return new ExtEventLoop();
65+ }
66+67+ if (\function_exists('event_base_new') && \PHP_MAJOR_VERSION === 5) {
68+ // only use ext-libevent on PHP 5 for now
69+ return new ExtLibeventLoop();
70+ }
71+72+ return new StreamSelectLoop();
73+ // @codeCoverageIgnoreEnd
74+ }
75+}
···1+<?php
2+3+namespace React\EventLoop;
4+5+interface LoopInterface
6+{
7+ /**
8+ * [Advanced] Register a listener to be notified when a stream is ready to read.
9+ *
10+ * Note that this low-level API is considered advanced usage.
11+ * Most use cases should probably use the higher-level
12+ * [readable Stream API](https://github.com/reactphp/stream#readablestreaminterface)
13+ * instead.
14+ *
15+ * The first parameter MUST be a valid stream resource that supports
16+ * checking whether it is ready to read by this loop implementation.
17+ * A single stream resource MUST NOT be added more than once.
18+ * Instead, either call [`removeReadStream()`](#removereadstream) first or
19+ * react to this event with a single listener and then dispatch from this
20+ * listener. This method MAY throw an `Exception` if the given resource type
21+ * is not supported by this loop implementation.
22+ *
23+ * The second parameter MUST be a listener callback function that accepts
24+ * the stream resource as its only parameter.
25+ * If you don't use the stream resource inside your listener callback function
26+ * you MAY use a function which has no parameters at all.
27+ *
28+ * The listener callback function MUST NOT throw an `Exception`.
29+ * The return value of the listener callback function will be ignored and has
30+ * no effect, so for performance reasons you're recommended to not return
31+ * any excessive data structures.
32+ *
33+ * If you want to access any variables within your callback function, you
34+ * can bind arbitrary data to a callback closure like this:
35+ *
36+ * ```php
37+ * $loop->addReadStream($stream, function ($stream) use ($name) {
38+ * echo $name . ' said: ' . fread($stream);
39+ * });
40+ * ```
41+ *
42+ * See also [example #11](examples).
43+ *
44+ * You can invoke [`removeReadStream()`](#removereadstream) to remove the
45+ * read event listener for this stream.
46+ *
47+ * The execution order of listeners when multiple streams become ready at
48+ * the same time is not guaranteed.
49+ *
50+ * @param resource $stream The PHP stream resource to check.
51+ * @param callable $listener Invoked when the stream is ready.
52+ * @throws \Exception if the given resource type is not supported by this loop implementation
53+ * @see self::removeReadStream()
54+ */
55+ public function addReadStream($stream, $listener);
56+57+ /**
58+ * [Advanced] Register a listener to be notified when a stream is ready to write.
59+ *
60+ * Note that this low-level API is considered advanced usage.
61+ * Most use cases should probably use the higher-level
62+ * [writable Stream API](https://github.com/reactphp/stream#writablestreaminterface)
63+ * instead.
64+ *
65+ * The first parameter MUST be a valid stream resource that supports
66+ * checking whether it is ready to write by this loop implementation.
67+ * A single stream resource MUST NOT be added more than once.
68+ * Instead, either call [`removeWriteStream()`](#removewritestream) first or
69+ * react to this event with a single listener and then dispatch from this
70+ * listener. This method MAY throw an `Exception` if the given resource type
71+ * is not supported by this loop implementation.
72+ *
73+ * The second parameter MUST be a listener callback function that accepts
74+ * the stream resource as its only parameter.
75+ * If you don't use the stream resource inside your listener callback function
76+ * you MAY use a function which has no parameters at all.
77+ *
78+ * The listener callback function MUST NOT throw an `Exception`.
79+ * The return value of the listener callback function will be ignored and has
80+ * no effect, so for performance reasons you're recommended to not return
81+ * any excessive data structures.
82+ *
83+ * If you want to access any variables within your callback function, you
84+ * can bind arbitrary data to a callback closure like this:
85+ *
86+ * ```php
87+ * $loop->addWriteStream($stream, function ($stream) use ($name) {
88+ * fwrite($stream, 'Hello ' . $name);
89+ * });
90+ * ```
91+ *
92+ * See also [example #12](examples).
93+ *
94+ * You can invoke [`removeWriteStream()`](#removewritestream) to remove the
95+ * write event listener for this stream.
96+ *
97+ * The execution order of listeners when multiple streams become ready at
98+ * the same time is not guaranteed.
99+ *
100+ * Some event loop implementations are known to only trigger the listener if
101+ * the stream *becomes* readable (edge-triggered) and may not trigger if the
102+ * stream has already been readable from the beginning.
103+ * This also implies that a stream may not be recognized as readable when data
104+ * is still left in PHP's internal stream buffers.
105+ * As such, it's recommended to use `stream_set_read_buffer($stream, 0);`
106+ * to disable PHP's internal read buffer in this case.
107+ *
108+ * @param resource $stream The PHP stream resource to check.
109+ * @param callable $listener Invoked when the stream is ready.
110+ * @throws \Exception if the given resource type is not supported by this loop implementation
111+ * @see self::removeWriteStream()
112+ */
113+ public function addWriteStream($stream, $listener);
114+115+ /**
116+ * Remove the read event listener for the given stream.
117+ *
118+ * Removing a stream from the loop that has already been removed or trying
119+ * to remove a stream that was never added or is invalid has no effect.
120+ *
121+ * @param resource $stream The PHP stream resource.
122+ */
123+ public function removeReadStream($stream);
124+125+ /**
126+ * Remove the write event listener for the given stream.
127+ *
128+ * Removing a stream from the loop that has already been removed or trying
129+ * to remove a stream that was never added or is invalid has no effect.
130+ *
131+ * @param resource $stream The PHP stream resource.
132+ */
133+ public function removeWriteStream($stream);
134+135+ /**
136+ * Enqueue a callback to be invoked once after the given interval.
137+ *
138+ * The second parameter MUST be a timer callback function that accepts
139+ * the timer instance as its only parameter.
140+ * If you don't use the timer instance inside your timer callback function
141+ * you MAY use a function which has no parameters at all.
142+ *
143+ * The timer callback function MUST NOT throw an `Exception`.
144+ * The return value of the timer callback function will be ignored and has
145+ * no effect, so for performance reasons you're recommended to not return
146+ * any excessive data structures.
147+ *
148+ * This method returns a timer instance. The same timer instance will also be
149+ * passed into the timer callback function as described above.
150+ * You can invoke [`cancelTimer`](#canceltimer) to cancel a pending timer.
151+ * Unlike [`addPeriodicTimer()`](#addperiodictimer), this method will ensure
152+ * the callback will be invoked only once after the given interval.
153+ *
154+ * ```php
155+ * $loop->addTimer(0.8, function () {
156+ * echo 'world!' . PHP_EOL;
157+ * });
158+ *
159+ * $loop->addTimer(0.3, function () {
160+ * echo 'hello ';
161+ * });
162+ * ```
163+ *
164+ * See also [example #1](examples).
165+ *
166+ * If you want to access any variables within your callback function, you
167+ * can bind arbitrary data to a callback closure like this:
168+ *
169+ * ```php
170+ * function hello($name, LoopInterface $loop)
171+ * {
172+ * $loop->addTimer(1.0, function () use ($name) {
173+ * echo "hello $name\n";
174+ * });
175+ * }
176+ *
177+ * hello('Tester', $loop);
178+ * ```
179+ *
180+ * This interface does not enforce any particular timer resolution, so
181+ * special care may have to be taken if you rely on very high precision with
182+ * millisecond accuracy or below. Event loop implementations SHOULD work on
183+ * a best effort basis and SHOULD provide at least millisecond accuracy
184+ * unless otherwise noted. Many existing event loop implementations are
185+ * known to provide microsecond accuracy, but it's generally not recommended
186+ * to rely on this high precision.
187+ *
188+ * Similarly, the execution order of timers scheduled to execute at the
189+ * same time (within its possible accuracy) is not guaranteed.
190+ *
191+ * This interface suggests that event loop implementations SHOULD use a
192+ * monotonic time source if available. Given that a monotonic time source is
193+ * only available as of PHP 7.3 by default, event loop implementations MAY
194+ * fall back to using wall-clock time.
195+ * While this does not affect many common use cases, this is an important
196+ * distinction for programs that rely on a high time precision or on systems
197+ * that are subject to discontinuous time adjustments (time jumps).
198+ * This means that if you schedule a timer to trigger in 30s and then adjust
199+ * your system time forward by 20s, the timer SHOULD still trigger in 30s.
200+ * See also [event loop implementations](#loop-implementations) for more details.
201+ *
202+ * @param int|float $interval The number of seconds to wait before execution.
203+ * @param callable $callback The callback to invoke.
204+ *
205+ * @return TimerInterface
206+ */
207+ public function addTimer($interval, $callback);
208+209+ /**
210+ * Enqueue a callback to be invoked repeatedly after the given interval.
211+ *
212+ * The second parameter MUST be a timer callback function that accepts
213+ * the timer instance as its only parameter.
214+ * If you don't use the timer instance inside your timer callback function
215+ * you MAY use a function which has no parameters at all.
216+ *
217+ * The timer callback function MUST NOT throw an `Exception`.
218+ * The return value of the timer callback function will be ignored and has
219+ * no effect, so for performance reasons you're recommended to not return
220+ * any excessive data structures.
221+ *
222+ * This method returns a timer instance. The same timer instance will also be
223+ * passed into the timer callback function as described above.
224+ * Unlike [`addTimer()`](#addtimer), this method will ensure the callback
225+ * will be invoked infinitely after the given interval or until you invoke
226+ * [`cancelTimer`](#canceltimer).
227+ *
228+ * ```php
229+ * $timer = $loop->addPeriodicTimer(0.1, function () {
230+ * echo 'tick!' . PHP_EOL;
231+ * });
232+ *
233+ * $loop->addTimer(1.0, function () use ($loop, $timer) {
234+ * $loop->cancelTimer($timer);
235+ * echo 'Done' . PHP_EOL;
236+ * });
237+ * ```
238+ *
239+ * See also [example #2](examples).
240+ *
241+ * If you want to limit the number of executions, you can bind
242+ * arbitrary data to a callback closure like this:
243+ *
244+ * ```php
245+ * function hello($name, LoopInterface $loop)
246+ * {
247+ * $n = 3;
248+ * $loop->addPeriodicTimer(1.0, function ($timer) use ($name, $loop, &$n) {
249+ * if ($n > 0) {
250+ * --$n;
251+ * echo "hello $name\n";
252+ * } else {
253+ * $loop->cancelTimer($timer);
254+ * }
255+ * });
256+ * }
257+ *
258+ * hello('Tester', $loop);
259+ * ```
260+ *
261+ * This interface does not enforce any particular timer resolution, so
262+ * special care may have to be taken if you rely on very high precision with
263+ * millisecond accuracy or below. Event loop implementations SHOULD work on
264+ * a best effort basis and SHOULD provide at least millisecond accuracy
265+ * unless otherwise noted. Many existing event loop implementations are
266+ * known to provide microsecond accuracy, but it's generally not recommended
267+ * to rely on this high precision.
268+ *
269+ * Similarly, the execution order of timers scheduled to execute at the
270+ * same time (within its possible accuracy) is not guaranteed.
271+ *
272+ * This interface suggests that event loop implementations SHOULD use a
273+ * monotonic time source if available. Given that a monotonic time source is
274+ * only available as of PHP 7.3 by default, event loop implementations MAY
275+ * fall back to using wall-clock time.
276+ * While this does not affect many common use cases, this is an important
277+ * distinction for programs that rely on a high time precision or on systems
278+ * that are subject to discontinuous time adjustments (time jumps).
279+ * This means that if you schedule a timer to trigger in 30s and then adjust
280+ * your system time forward by 20s, the timer SHOULD still trigger in 30s.
281+ * See also [event loop implementations](#loop-implementations) for more details.
282+ *
283+ * Additionally, periodic timers may be subject to timer drift due to
284+ * re-scheduling after each invocation. As such, it's generally not
285+ * recommended to rely on this for high precision intervals with millisecond
286+ * accuracy or below.
287+ *
288+ * @param int|float $interval The number of seconds to wait before execution.
289+ * @param callable $callback The callback to invoke.
290+ *
291+ * @return TimerInterface
292+ */
293+ public function addPeriodicTimer($interval, $callback);
294+295+ /**
296+ * Cancel a pending timer.
297+ *
298+ * See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples).
299+ *
300+ * Calling this method on a timer instance that has not been added to this
301+ * loop instance or on a timer that has already been cancelled has no effect.
302+ *
303+ * @param TimerInterface $timer The timer to cancel.
304+ *
305+ * @return void
306+ */
307+ public function cancelTimer(TimerInterface $timer);
308+309+ /**
310+ * Schedule a callback to be invoked on a future tick of the event loop.
311+ *
312+ * This works very much similar to timers with an interval of zero seconds,
313+ * but does not require the overhead of scheduling a timer queue.
314+ *
315+ * The tick callback function MUST be able to accept zero parameters.
316+ *
317+ * The tick callback function MUST NOT throw an `Exception`.
318+ * The return value of the tick callback function will be ignored and has
319+ * no effect, so for performance reasons you're recommended to not return
320+ * any excessive data structures.
321+ *
322+ * If you want to access any variables within your callback function, you
323+ * can bind arbitrary data to a callback closure like this:
324+ *
325+ * ```php
326+ * function hello($name, LoopInterface $loop)
327+ * {
328+ * $loop->futureTick(function () use ($name) {
329+ * echo "hello $name\n";
330+ * });
331+ * }
332+ *
333+ * hello('Tester', $loop);
334+ * ```
335+ *
336+ * Unlike timers, tick callbacks are guaranteed to be executed in the order
337+ * they are enqueued.
338+ * Also, once a callback is enqueued, there's no way to cancel this operation.
339+ *
340+ * This is often used to break down bigger tasks into smaller steps (a form
341+ * of cooperative multitasking).
342+ *
343+ * ```php
344+ * $loop->futureTick(function () {
345+ * echo 'b';
346+ * });
347+ * $loop->futureTick(function () {
348+ * echo 'c';
349+ * });
350+ * echo 'a';
351+ * ```
352+ *
353+ * See also [example #3](examples).
354+ *
355+ * @param callable $listener The callback to invoke.
356+ *
357+ * @return void
358+ */
359+ public function futureTick($listener);
360+361+ /**
362+ * Register a listener to be notified when a signal has been caught by this process.
363+ *
364+ * This is useful to catch user interrupt signals or shutdown signals from
365+ * tools like `supervisor` or `systemd`.
366+ *
367+ * The second parameter MUST be a listener callback function that accepts
368+ * the signal as its only parameter.
369+ * If you don't use the signal inside your listener callback function
370+ * you MAY use a function which has no parameters at all.
371+ *
372+ * The listener callback function MUST NOT throw an `Exception`.
373+ * The return value of the listener callback function will be ignored and has
374+ * no effect, so for performance reasons you're recommended to not return
375+ * any excessive data structures.
376+ *
377+ * ```php
378+ * $loop->addSignal(SIGINT, function (int $signal) {
379+ * echo 'Caught user interrupt signal' . PHP_EOL;
380+ * });
381+ * ```
382+ *
383+ * See also [example #4](examples).
384+ *
385+ * Signaling is only available on Unix-like platforms, Windows isn't
386+ * supported due to operating system limitations.
387+ * This method may throw a `BadMethodCallException` if signals aren't
388+ * supported on this platform, for example when required extensions are
389+ * missing.
390+ *
391+ * **Note: A listener can only be added once to the same signal, any
392+ * attempts to add it more than once will be ignored.**
393+ *
394+ * @param int $signal
395+ * @param callable $listener
396+ *
397+ * @throws \BadMethodCallException when signals aren't supported on this
398+ * platform, for example when required extensions are missing.
399+ *
400+ * @return void
401+ */
402+ public function addSignal($signal, $listener);
403+404+ /**
405+ * Removes a previously added signal listener.
406+ *
407+ * ```php
408+ * $loop->removeSignal(SIGINT, $listener);
409+ * ```
410+ *
411+ * Any attempts to remove listeners that aren't registered will be ignored.
412+ *
413+ * @param int $signal
414+ * @param callable $listener
415+ *
416+ * @return void
417+ */
418+ public function removeSignal($signal, $listener);
419+420+ /**
421+ * Run the event loop until there are no more tasks to perform.
422+ *
423+ * For many applications, this method is the only directly visible
424+ * invocation on the event loop.
425+ * As a rule of thumb, it is usually recommended to attach everything to the
426+ * same loop instance and then run the loop once at the bottom end of the
427+ * application.
428+ *
429+ * ```php
430+ * $loop->run();
431+ * ```
432+ *
433+ * This method will keep the loop running until there are no more tasks
434+ * to perform. In other words: This method will block until the last
435+ * timer, stream and/or signal has been removed.
436+ *
437+ * Likewise, it is imperative to ensure the application actually invokes
438+ * this method once. Adding listeners to the loop and missing to actually
439+ * run it will result in the application exiting without actually waiting
440+ * for any of the attached listeners.
441+ *
442+ * This method MUST NOT be called while the loop is already running.
443+ * This method MAY be called more than once after it has explicitly been
444+ * [`stop()`ped](#stop) or after it automatically stopped because it
445+ * previously did no longer have anything to do.
446+ *
447+ * @return void
448+ */
449+ public function run();
450+451+ /**
452+ * Instruct a running event loop to stop.
453+ *
454+ * This method is considered advanced usage and should be used with care.
455+ * As a rule of thumb, it is usually recommended to let the loop stop
456+ * only automatically when it no longer has anything to do.
457+ *
458+ * This method can be used to explicitly instruct the event loop to stop:
459+ *
460+ * ```php
461+ * $loop->addTimer(3.0, function () use ($loop) {
462+ * $loop->stop();
463+ * });
464+ * ```
465+ *
466+ * Calling this method on a loop instance that is not currently running or
467+ * on a loop instance that has already been stopped has no effect.
468+ *
469+ * @return void
470+ */
471+ public function stop();
472+}
···1+<?php
2+3+namespace React\EventLoop;
4+5+use React\EventLoop\Tick\FutureTickQueue;
6+use React\EventLoop\Timer\Timer;
7+use React\EventLoop\Timer\Timers;
8+9+/**
10+ * A `stream_select()` based event loop.
11+ *
12+ * This uses the [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php)
13+ * function and is the only implementation that works out of the box with PHP.
14+ *
15+ * This event loop works out of the box on PHP 5.4 through PHP 8+ and HHVM.
16+ * This means that no installation is required and this library works on all
17+ * platforms and supported PHP versions.
18+ * Accordingly, the [`Loop` class](#loop) and the deprecated [`Factory`](#factory)
19+ * will use this event loop by default if you do not install any of the event loop
20+ * extensions listed below.
21+ *
22+ * Under the hood, it does a simple `select` system call.
23+ * This system call is limited to the maximum file descriptor number of
24+ * `FD_SETSIZE` (platform dependent, commonly 1024) and scales with `O(m)`
25+ * (`m` being the maximum file descriptor number passed).
26+ * This means that you may run into issues when handling thousands of streams
27+ * concurrently and you may want to look into using one of the alternative
28+ * event loop implementations listed below in this case.
29+ * If your use case is among the many common use cases that involve handling only
30+ * dozens or a few hundred streams at once, then this event loop implementation
31+ * performs really well.
32+ *
33+ * If you want to use signal handling (see also [`addSignal()`](#addsignal) below),
34+ * this event loop implementation requires `ext-pcntl`.
35+ * This extension is only available for Unix-like platforms and does not support
36+ * Windows.
37+ * It is commonly installed as part of many PHP distributions.
38+ * If this extension is missing (or you're running on Windows), signal handling is
39+ * not supported and throws a `BadMethodCallException` instead.
40+ *
41+ * This event loop is known to rely on wall-clock time to schedule future timers
42+ * when using any version before PHP 7.3, because a monotonic time source is
43+ * only available as of PHP 7.3 (`hrtime()`).
44+ * While this does not affect many common use cases, this is an important
45+ * distinction for programs that rely on a high time precision or on systems
46+ * that are subject to discontinuous time adjustments (time jumps).
47+ * This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and
48+ * then adjust your system time forward by 20s, the timer may trigger in 10s.
49+ * See also [`addTimer()`](#addtimer) for more details.
50+ *
51+ * @link https://www.php.net/manual/en/function.stream-select.php
52+ */
53+final class StreamSelectLoop implements LoopInterface
54+{
55+ /** @internal */
56+ const MICROSECONDS_PER_SECOND = 1000000;
57+58+ private $futureTickQueue;
59+ private $timers;
60+ private $readStreams = array();
61+ private $readListeners = array();
62+ private $writeStreams = array();
63+ private $writeListeners = array();
64+ private $running;
65+ private $pcntl = false;
66+ private $pcntlPoll = false;
67+ private $signals;
68+69+ public function __construct()
70+ {
71+ $this->futureTickQueue = new FutureTickQueue();
72+ $this->timers = new Timers();
73+ $this->pcntl = \function_exists('pcntl_signal') && \function_exists('pcntl_signal_dispatch');
74+ $this->pcntlPoll = $this->pcntl && !\function_exists('pcntl_async_signals');
75+ $this->signals = new SignalsHandler();
76+77+ // prefer async signals if available (PHP 7.1+) or fall back to dispatching on each tick
78+ if ($this->pcntl && !$this->pcntlPoll) {
79+ \pcntl_async_signals(true);
80+ }
81+ }
82+83+ public function addReadStream($stream, $listener)
84+ {
85+ $key = (int) $stream;
86+87+ if (!isset($this->readStreams[$key])) {
88+ $this->readStreams[$key] = $stream;
89+ $this->readListeners[$key] = $listener;
90+ }
91+ }
92+93+ public function addWriteStream($stream, $listener)
94+ {
95+ $key = (int) $stream;
96+97+ if (!isset($this->writeStreams[$key])) {
98+ $this->writeStreams[$key] = $stream;
99+ $this->writeListeners[$key] = $listener;
100+ }
101+ }
102+103+ public function removeReadStream($stream)
104+ {
105+ $key = (int) $stream;
106+107+ unset(
108+ $this->readStreams[$key],
109+ $this->readListeners[$key]
110+ );
111+ }
112+113+ public function removeWriteStream($stream)
114+ {
115+ $key = (int) $stream;
116+117+ unset(
118+ $this->writeStreams[$key],
119+ $this->writeListeners[$key]
120+ );
121+ }
122+123+ public function addTimer($interval, $callback)
124+ {
125+ $timer = new Timer($interval, $callback, false);
126+127+ $this->timers->add($timer);
128+129+ return $timer;
130+ }
131+132+ public function addPeriodicTimer($interval, $callback)
133+ {
134+ $timer = new Timer($interval, $callback, true);
135+136+ $this->timers->add($timer);
137+138+ return $timer;
139+ }
140+141+ public function cancelTimer(TimerInterface $timer)
142+ {
143+ $this->timers->cancel($timer);
144+ }
145+146+ public function futureTick($listener)
147+ {
148+ $this->futureTickQueue->add($listener);
149+ }
150+151+ public function addSignal($signal, $listener)
152+ {
153+ if ($this->pcntl === false) {
154+ throw new \BadMethodCallException('Event loop feature "signals" isn\'t supported by the "StreamSelectLoop"');
155+ }
156+157+ $first = $this->signals->count($signal) === 0;
158+ $this->signals->add($signal, $listener);
159+160+ if ($first) {
161+ \pcntl_signal($signal, array($this->signals, 'call'));
162+ }
163+ }
164+165+ public function removeSignal($signal, $listener)
166+ {
167+ if (!$this->signals->count($signal)) {
168+ return;
169+ }
170+171+ $this->signals->remove($signal, $listener);
172+173+ if ($this->signals->count($signal) === 0) {
174+ \pcntl_signal($signal, \SIG_DFL);
175+ }
176+ }
177+178+ public function run()
179+ {
180+ $this->running = true;
181+182+ while ($this->running) {
183+ $this->futureTickQueue->tick();
184+185+ $this->timers->tick();
186+187+ // Future-tick queue has pending callbacks ...
188+ if (!$this->running || !$this->futureTickQueue->isEmpty()) {
189+ $timeout = 0;
190+191+ // There is a pending timer, only block until it is due ...
192+ } elseif ($scheduledAt = $this->timers->getFirst()) {
193+ $timeout = $scheduledAt - $this->timers->getTime();
194+ if ($timeout < 0) {
195+ $timeout = 0;
196+ } else {
197+ // Convert float seconds to int microseconds.
198+ // Ensure we do not exceed maximum integer size, which may
199+ // cause the loop to tick once every ~35min on 32bit systems.
200+ $timeout *= self::MICROSECONDS_PER_SECOND;
201+ $timeout = $timeout > \PHP_INT_MAX ? \PHP_INT_MAX : (int)$timeout;
202+ }
203+204+ // The only possible event is stream or signal activity, so wait forever ...
205+ } elseif ($this->readStreams || $this->writeStreams || !$this->signals->isEmpty()) {
206+ $timeout = null;
207+208+ // There's nothing left to do ...
209+ } else {
210+ break;
211+ }
212+213+ $this->waitForStreamActivity($timeout);
214+ }
215+ }
216+217+ public function stop()
218+ {
219+ $this->running = false;
220+ }
221+222+ /**
223+ * Wait/check for stream activity, or until the next timer is due.
224+ *
225+ * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever.
226+ */
227+ private function waitForStreamActivity($timeout)
228+ {
229+ $read = $this->readStreams;
230+ $write = $this->writeStreams;
231+232+ $available = $this->streamSelect($read, $write, $timeout);
233+ if ($this->pcntlPoll) {
234+ \pcntl_signal_dispatch();
235+ }
236+ if (false === $available) {
237+ // if a system call has been interrupted,
238+ // we cannot rely on it's outcome
239+ return;
240+ }
241+242+ foreach ($read as $stream) {
243+ $key = (int) $stream;
244+245+ if (isset($this->readListeners[$key])) {
246+ \call_user_func($this->readListeners[$key], $stream);
247+ }
248+ }
249+250+ foreach ($write as $stream) {
251+ $key = (int) $stream;
252+253+ if (isset($this->writeListeners[$key])) {
254+ \call_user_func($this->writeListeners[$key], $stream);
255+ }
256+ }
257+ }
258+259+ /**
260+ * Emulate a stream_select() implementation that does not break when passed
261+ * empty stream arrays.
262+ *
263+ * @param array $read An array of read streams to select upon.
264+ * @param array $write An array of write streams to select upon.
265+ * @param int|null $timeout Activity timeout in microseconds, or null to wait forever.
266+ *
267+ * @return int|false The total number of streams that are ready for read/write.
268+ * Can return false if stream_select() is interrupted by a signal.
269+ */
270+ private function streamSelect(array &$read, array &$write, $timeout)
271+ {
272+ if ($read || $write) {
273+ // We do not usually use or expose the `exceptfds` parameter passed to the underlying `select`.
274+ // However, Windows does not report failed connection attempts in `writefds` passed to `select` like most other platforms.
275+ // Instead, it uses `writefds` only for successful connection attempts and `exceptfds` for failed connection attempts.
276+ // We work around this by adding all sockets that look like a pending connection attempt to `exceptfds` automatically on Windows and merge it back later.
277+ // This ensures the public API matches other loop implementations across all platforms (see also test suite or rather test matrix).
278+ // Lacking better APIs, every write-only socket that has not yet read any data is assumed to be in a pending connection attempt state.
279+ // @link https://docs.microsoft.com/de-de/windows/win32/api/winsock2/nf-winsock2-select
280+ $except = null;
281+ if (\DIRECTORY_SEPARATOR === '\\') {
282+ $except = array();
283+ foreach ($write as $key => $socket) {
284+ if (!isset($read[$key]) && @\ftell($socket) === 0) {
285+ $except[$key] = $socket;
286+ }
287+ }
288+ }
289+290+ /** @var ?callable $previous */
291+ $previous = \set_error_handler(function ($errno, $errstr) use (&$previous) {
292+ // suppress warnings that occur when `stream_select()` is interrupted by a signal
293+ // PHP defines `EINTR` through `ext-sockets` or `ext-pcntl`, otherwise use common default (Linux & Mac)
294+ $eintr = \defined('SOCKET_EINTR') ? \SOCKET_EINTR : (\defined('PCNTL_EINTR') ? \PCNTL_EINTR : 4);
295+ if ($errno === \E_WARNING && \strpos($errstr, '[' . $eintr .']: ') !== false) {
296+ return;
297+ }
298+299+ // forward any other error to registered error handler or print warning
300+ return ($previous !== null) ? \call_user_func_array($previous, \func_get_args()) : false;
301+ });
302+303+ try {
304+ $ret = \stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
305+ \restore_error_handler();
306+ } catch (\Throwable $e) { // @codeCoverageIgnoreStart
307+ \restore_error_handler();
308+ throw $e;
309+ } catch (\Exception $e) {
310+ \restore_error_handler();
311+ throw $e;
312+ } // @codeCoverageIgnoreEnd
313+314+ if ($except) {
315+ $write = \array_merge($write, $except);
316+ }
317+ return $ret;
318+ }
319+320+ if ($timeout > 0) {
321+ \usleep($timeout);
322+ } elseif ($timeout === null) {
323+ // wait forever (we only reach this if we're only awaiting signals)
324+ // this may be interrupted and return earlier when a signal is received
325+ \sleep(PHP_INT_MAX);
326+ }
327+328+ return 0;
329+ }
330+}
···1+<?php
2+3+namespace React\EventLoop\Tick;
4+5+use SplQueue;
6+7+/**
8+ * A tick queue implementation that can hold multiple callback functions
9+ *
10+ * This class should only be used internally, see LoopInterface instead.
11+ *
12+ * @see LoopInterface
13+ * @internal
14+ */
15+final class FutureTickQueue
16+{
17+ private $queue;
18+19+ public function __construct()
20+ {
21+ $this->queue = new SplQueue();
22+ }
23+24+ /**
25+ * Add a callback to be invoked on a future tick of the event loop.
26+ *
27+ * Callbacks are guaranteed to be executed in the order they are enqueued.
28+ *
29+ * @param callable $listener The callback to invoke.
30+ */
31+ public function add($listener)
32+ {
33+ $this->queue->enqueue($listener);
34+ }
35+36+ /**
37+ * Flush the callback queue.
38+ */
39+ public function tick()
40+ {
41+ // Only invoke as many callbacks as were on the queue when tick() was called.
42+ $count = $this->queue->count();
43+44+ while ($count--) {
45+ \call_user_func(
46+ $this->queue->dequeue()
47+ );
48+ }
49+ }
50+51+ /**
52+ * Check if the next tick queue is empty.
53+ *
54+ * @return boolean
55+ */
56+ public function isEmpty()
57+ {
58+ return $this->queue->isEmpty();
59+ }
60+}
···1+<?php
2+3+namespace React\EventLoop\Timer;
4+5+use React\EventLoop\TimerInterface;
6+7+/**
8+ * The actual connection implementation for TimerInterface
9+ *
10+ * This class should only be used internally, see TimerInterface instead.
11+ *
12+ * @see TimerInterface
13+ * @internal
14+ */
15+final class Timer implements TimerInterface
16+{
17+ const MIN_INTERVAL = 0.000001;
18+19+ private $interval;
20+ private $callback;
21+ private $periodic;
22+23+ /**
24+ * Constructor initializes the fields of the Timer
25+ *
26+ * @param float $interval The interval after which this timer will execute, in seconds
27+ * @param callable $callback The callback that will be executed when this timer elapses
28+ * @param bool $periodic Whether the time is periodic
29+ */
30+ public function __construct($interval, $callback, $periodic = false)
31+ {
32+ if ($interval < self::MIN_INTERVAL) {
33+ $interval = self::MIN_INTERVAL;
34+ }
35+36+ $this->interval = (float) $interval;
37+ $this->callback = $callback;
38+ $this->periodic = (bool) $periodic;
39+ }
40+41+ public function getInterval()
42+ {
43+ return $this->interval;
44+ }
45+46+ public function getCallback()
47+ {
48+ return $this->callback;
49+ }
50+51+ public function isPeriodic()
52+ {
53+ return $this->periodic;
54+ }
55+}
···1+<?php
2+3+namespace React\EventLoop\Timer;
4+5+use React\EventLoop\TimerInterface;
6+7+/**
8+ * A scheduler implementation that can hold multiple timer instances
9+ *
10+ * This class should only be used internally, see TimerInterface instead.
11+ *
12+ * @see TimerInterface
13+ * @internal
14+ */
15+final class Timers
16+{
17+ private $time;
18+ private $timers = array();
19+ private $schedule = array();
20+ private $sorted = true;
21+ private $useHighResolution;
22+23+ public function __construct()
24+ {
25+ // prefer high-resolution timer, available as of PHP 7.3+
26+ $this->useHighResolution = \function_exists('hrtime');
27+ }
28+29+ public function updateTime()
30+ {
31+ return $this->time = $this->useHighResolution ? \hrtime(true) * 1e-9 : \microtime(true);
32+ }
33+34+ public function getTime()
35+ {
36+ return $this->time ?: $this->updateTime();
37+ }
38+39+ public function add(TimerInterface $timer)
40+ {
41+ $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
42+ $this->timers[$id] = $timer;
43+ $this->schedule[$id] = $timer->getInterval() + $this->updateTime();
44+ $this->sorted = false;
45+ }
46+47+ public function contains(TimerInterface $timer)
48+ {
49+ $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
50+ return isset($this->timers[$id]);
51+ }
52+53+ public function cancel(TimerInterface $timer)
54+ {
55+ $id = \PHP_VERSION_ID < 70200 ? \spl_object_hash($timer) : \spl_object_id($timer);
56+ unset($this->timers[$id], $this->schedule[$id]);
57+ }
58+59+ public function getFirst()
60+ {
61+ // ensure timers are sorted to simply accessing next (first) one
62+ if (!$this->sorted) {
63+ $this->sorted = true;
64+ \asort($this->schedule);
65+ }
66+67+ return \reset($this->schedule);
68+ }
69+70+ public function isEmpty()
71+ {
72+ return \count($this->timers) === 0;
73+ }
74+75+ public function tick()
76+ {
77+ // hot path: skip timers if nothing is scheduled
78+ if (!$this->schedule) {
79+ return;
80+ }
81+82+ // ensure timers are sorted so we can execute in order
83+ if (!$this->sorted) {
84+ $this->sorted = true;
85+ \asort($this->schedule);
86+ }
87+88+ $time = $this->updateTime();
89+90+ foreach ($this->schedule as $id => $scheduled) {
91+ // schedule is ordered, so loop until first timer that is not scheduled for execution now
92+ if ($scheduled >= $time) {
93+ break;
94+ }
95+96+ // skip any timers that are removed while we process the current schedule
97+ if (!isset($this->schedule[$id]) || $this->schedule[$id] !== $scheduled) {
98+ continue;
99+ }
100+101+ $timer = $this->timers[$id];
102+ \call_user_func($timer->getCallback(), $timer);
103+104+ // re-schedule if this is a periodic timer and it has not been cancelled explicitly already
105+ if ($timer->isPeriodic() && isset($this->timers[$id])) {
106+ $this->schedule[$id] = $timer->getInterval() + $time;
107+ $this->sorted = false;
108+ } else {
109+ unset($this->timers[$id], $this->schedule[$id]);
110+ }
111+ }
112+ }
113+}
+27
vendor/react/event-loop/src/TimerInterface.php
···000000000000000000000000000
···1+<?php
2+3+namespace React\EventLoop;
4+5+interface TimerInterface
6+{
7+ /**
8+ * Get the interval after which this timer will execute, in seconds
9+ *
10+ * @return float
11+ */
12+ public function getInterval();
13+14+ /**
15+ * Get the callback that will be executed when this timer elapses
16+ *
17+ * @return callable
18+ */
19+ public function getCallback();
20+21+ /**
22+ * Determine whether the time is periodic
23+ *
24+ * @return bool
25+ */
26+ public function isPeriodic();
27+}
···1+# Changelog
2+3+## 3.2.0 (2024-05-24)
4+5+* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations.
6+ (#260 by @Ayesh)
7+8+* Feature: Include previous exceptions when reporting unhandled promise rejections.
9+ (#262 by @clue)
10+11+* Update test suite to improve PHP 8.4+ support.
12+ (#261 by @SimonFrings)
13+14+## 3.1.0 (2023-11-16)
15+16+* Feature: Full PHP 8.3 compatibility.
17+ (#255 by @clue)
18+19+* Feature: Describe all callable arguments with types for `Promise` and `Deferred`.
20+ (#253 by @clue)
21+22+* Update test suite and minor documentation improvements.
23+ (#251 by @ondrejmirtes and #250 by @SQKo)
24+25+## 3.0.0 (2023-07-11)
26+27+A major new feature release, see [**release announcement**](https://clue.engineering/2023/announcing-reactphp-promise-v3).
28+29+* We'd like to emphasize that this component is production ready and battle-tested.
30+ We plan to support all long-term support (LTS) releases for at least 24 months,
31+ so you have a rock-solid foundation to build on top of.
32+33+* The v3 release will be the way forward for this package. However, we will still
34+ actively support v2 and v1 to provide a smooth upgrade path for those not yet
35+ on the latest versions.
36+37+This update involves some major new features and a minor BC break over the
38+`v2.0.0` release. We've tried hard to avoid BC breaks where possible and
39+minimize impact otherwise. We expect that most consumers of this package will be
40+affected by BC breaks, but updating should take no longer than a few minutes.
41+See below for more details:
42+43+* BC break: PHP 8.1+ recommended, PHP 7.1+ required.
44+ (#138 and #149 by @WyriHaximus)
45+46+* Feature / BC break: The `PromiseInterface` now includes the functionality of the old ~~`ExtendedPromiseInterface`~~ and ~~`CancellablePromiseInterface`~~.
47+ Each promise now always includes the `then()`, `catch()`, `finally()` and `cancel()` methods.
48+ The new `catch()` and `finally()` methods replace the deprecated ~~`otherwise()`~~ and ~~`always()`~~ methods which continue to exist for BC reasons.
49+ The old ~~`ExtendedPromiseInterface`~~ and ~~`CancellablePromiseInterface`~~ are no longer needed and have been removed as a consequence.
50+ (#75 by @jsor and #208 by @clue and @WyriHaximus)
51+52+ ```php
53+ // old (multiple interfaces may or may not be implemented)
54+ assert($promise instanceof PromiseInterface);
55+ assert(method_exists($promise, 'then'));
56+ if ($promise instanceof ExtendedPromiseInterface) { assert(method_exists($promise, 'otherwise')); }
57+ if ($promise instanceof ExtendedPromiseInterface) { assert(method_exists($promise, 'always')); }
58+ if ($promise instanceof CancellablePromiseInterface) { assert(method_exists($promise, 'cancel')); }
59+60+ // new (single PromiseInterface with all methods)
61+ assert($promise instanceof PromiseInterface);
62+ assert(method_exists($promise, 'then'));
63+ assert(method_exists($promise, 'catch'));
64+ assert(method_exists($promise, 'finally'));
65+ assert(method_exists($promise, 'cancel'));
66+ ```
67+68+* Feature / BC break: Improve type safety of promises. Require `mixed` fulfillment value argument and `Throwable` (or `Exception`) as rejection reason.
69+ Add PHPStan template types to ensure strict types for `resolve(T $value): PromiseInterface<T>` and `reject(Throwable $reason): PromiseInterface<never>`.
70+ It is no longer possible to resolve a promise without a value (use `null` instead) or reject a promise without a reason (use `Throwable` instead).
71+ (#93, #141 and #142 by @jsor, #138, #149 and #247 by @WyriHaximus and #213 and #246 by @clue)
72+73+ ```php
74+ // old (arguments used to be optional)
75+ $promise = resolve();
76+ $promise = reject();
77+78+ // new (already supported before)
79+ $promise = resolve(null);
80+ $promise = reject(new RuntimeException());
81+ ```
82+83+* Feature / BC break: Report all unhandled rejections by default and remove ~~`done()`~~ method.
84+ Add new `set_rejection_handler()` function to set the global rejection handler for unhandled promise rejections.
85+ (#248, #249 and #224 by @clue)
86+87+ ```php
88+ // Unhandled promise rejection with RuntimeException: Unhandled in example.php:2
89+ reject(new RuntimeException('Unhandled'));
90+ ```
91+92+* BC break: Remove all deprecated APIs and reduce API surface.
93+ Remove ~~`some()`~~, ~~`map()`~~, ~~`reduce()`~~ functions, use `any()` and `all()` functions instead.
94+ Remove internal ~~`FulfilledPromise`~~ and ~~`RejectedPromise`~~ classes, use `resolve()` and `reject()` functions instead.
95+ Remove legacy promise progress API (deprecated third argument to `then()` method) and deprecated ~~`LazyPromise`~~ class.
96+ (#32 and #98 by @jsor and #164, #219 and #220 by @clue)
97+98+* BC break: Make all classes final to encourage composition over inheritance.
99+ (#80 by @jsor)
100+101+* Feature / BC break: Require `array` (or `iterable`) type for `all()` + `race()` + `any()` functions and bring in line with ES6 specification.
102+ These functions now require a single argument with a variable number of promises or values as input.
103+ (#225 by @clue and #35 by @jsor)
104+105+* Fix / BC break: Fix `race()` to return a forever pending promise when called with an empty `array` (or `iterable`) and bring in line with ES6 specification.
106+ (#83 by @jsor and #225 by @clue)
107+108+* Minor performance improvements by initializing `Deferred` in the constructor and avoiding `call_user_func()` calls.
109+ (#151 by @WyriHaximus and #171 by @Kubo2)
110+111+* Minor documentation improvements.
112+ (#110 by @seregazhuk, #132 by @CharlotteDunois, #145 by @danielecr, #178 by @WyriHaximus, #189 by @srdante, #212 by @clue, #214, #239 and #243 by @SimonFrings and #231 by @nhedger)
113+114+The following changes had to be ported to this release due to our branching
115+strategy, but also appeared in the [`2.x` branch](https://github.com/reactphp/promise/tree/2.x):
116+117+* Feature: Support union types and address deprecation of `ReflectionType::getClass()` (PHP 8+).
118+ (#197 by @cdosoftei and @SimonFrings)
119+120+* Feature: Support intersection types (PHP 8.1+).
121+ (#209 by @bzikarsky)
122+123+* Feature: Support DNS types (PHP 8.2+).
124+ (#236 by @nhedger)
125+126+* Feature: Port all memory improvements from `2.x` to `3.x`.
127+ (#150 by @clue and @WyriHaximus)
128+129+* Fix: Fix checking whether cancellable promise is an object and avoid possible warning.
130+ (#161 by @smscr)
131+132+* Improve performance by prefixing all global functions calls with \ to skip the look up and resolve process and go straight to the global function.
133+ (#134 by @WyriHaximus)
134+135+* Improve test suite, update PHPUnit and PHP versions and add `.gitattributes` to exclude dev files from exports.
136+ (#107 by @carusogabriel, #148 and #234 by @WyriHaximus, #153 by @reedy, #162, #230 and #240 by @clue, #173, #177, #185 and #199 by @SimonFrings, #193 by @woodongwong and #210 by @bzikarsky)
137+138+The following changes were originally planned for this release but later reverted
139+and are not part of the final release:
140+141+* Add iterative callback queue handler to avoid recursion (later removed to improve Fiber support).
142+ (#28, #82 and #86 by @jsor, #158 by @WyriHaximus and #229 and #238 by @clue)
143+144+* Trigger an `E_USER_ERROR` instead of throwing an exception from `done()` (later removed entire `done()` method to globally report unhandled rejections).
145+ (#97 by @jsor and #224 and #248 by @clue)
146+147+* Add type declarations for `some()` (later removed entire `some()` function).
148+ (#172 by @WyriHaximus and #219 by @clue)
149+150+## 2.0.0 (2013-12-10)
151+152+See [`2.x` CHANGELOG](https://github.com/reactphp/promise/blob/2.x/CHANGELOG.md) for more details.
153+154+## 1.0.0 (2012-11-07)
155+156+See [`1.x` CHANGELOG](https://github.com/reactphp/promise/blob/1.x/CHANGELOG.md) for more details.
+24
vendor/react/promise/LICENSE
···000000000000000000000000
···1+The MIT License (MIT)
2+3+Copyright (c) 2012 Jan Sorgalla, Christian Lück, Cees-Jan Kiewiet, Chris Boden
4+5+Permission is hereby granted, free of charge, to any person
6+obtaining a copy of this software and associated documentation
7+files (the "Software"), to deal in the Software without
8+restriction, including without limitation the rights to use,
9+copy, modify, merge, publish, distribute, sublicense, and/or sell
10+copies of the Software, and to permit persons to whom the
11+Software is furnished to do so, subject to the following
12+conditions:
13+14+The above copyright notice and this permission notice shall be
15+included in all copies or substantial portions of the Software.
16+17+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24+OTHER DEALINGS IN THE SOFTWARE.
···1+Promise
2+=======
3+4+A lightweight implementation of
5+[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP.
6+7+[](https://github.com/reactphp/promise/actions)
8+[](https://packagist.org/packages/react/promise)
9+10+Table of Contents
11+-----------------
12+13+1. [Introduction](#introduction)
14+2. [Concepts](#concepts)
15+ * [Deferred](#deferred)
16+ * [Promise](#promise-1)
17+3. [API](#api)
18+ * [Deferred](#deferred-1)
19+ * [Deferred::promise()](#deferredpromise)
20+ * [Deferred::resolve()](#deferredresolve)
21+ * [Deferred::reject()](#deferredreject)
22+ * [PromiseInterface](#promiseinterface)
23+ * [PromiseInterface::then()](#promiseinterfacethen)
24+ * [PromiseInterface::catch()](#promiseinterfacecatch)
25+ * [PromiseInterface::finally()](#promiseinterfacefinally)
26+ * [PromiseInterface::cancel()](#promiseinterfacecancel)
27+ * [~~PromiseInterface::otherwise()~~](#promiseinterfaceotherwise)
28+ * [~~PromiseInterface::always()~~](#promiseinterfacealways)
29+ * [Promise](#promise-2)
30+ * [Functions](#functions)
31+ * [resolve()](#resolve)
32+ * [reject()](#reject)
33+ * [all()](#all)
34+ * [race()](#race)
35+ * [any()](#any)
36+ * [set_rejection_handler()](#set_rejection_handler)
37+4. [Examples](#examples)
38+ * [How to use Deferred](#how-to-use-deferred)
39+ * [How promise forwarding works](#how-promise-forwarding-works)
40+ * [Resolution forwarding](#resolution-forwarding)
41+ * [Rejection forwarding](#rejection-forwarding)
42+ * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding)
43+5. [Install](#install)
44+6. [Tests](#tests)
45+7. [Credits](#credits)
46+8. [License](#license)
47+48+Introduction
49+------------
50+51+Promise is a library implementing
52+[CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP.
53+54+It also provides several other useful promise-related concepts, such as joining
55+multiple promises and mapping and reducing collections of promises.
56+57+If you've never heard about promises before,
58+[read this first](https://gist.github.com/domenic/3889970).
59+60+Concepts
61+--------
62+63+### Deferred
64+65+A **Deferred** represents a computation or unit of work that may not have
66+completed yet. Typically (but not always), that computation will be something
67+that executes asynchronously and completes at some point in the future.
68+69+### Promise
70+71+While a deferred represents the computation itself, a **Promise** represents
72+the result of that computation. Thus, each deferred has a promise that acts as
73+a placeholder for its actual result.
74+75+API
76+---
77+78+### Deferred
79+80+A deferred represents an operation whose resolution is pending. It has separate
81+promise and resolver parts.
82+83+```php
84+$deferred = new React\Promise\Deferred();
85+86+$promise = $deferred->promise();
87+88+$deferred->resolve(mixed $value);
89+$deferred->reject(\Throwable $reason);
90+```
91+92+The `promise` method returns the promise of the deferred.
93+94+The `resolve` and `reject` methods control the state of the deferred.
95+96+The constructor of the `Deferred` accepts an optional `$canceller` argument.
97+See [Promise](#promise-2) for more information.
98+99+#### Deferred::promise()
100+101+```php
102+$promise = $deferred->promise();
103+```
104+105+Returns the promise of the deferred, which you can hand out to others while
106+keeping the authority to modify its state to yourself.
107+108+#### Deferred::resolve()
109+110+```php
111+$deferred->resolve(mixed $value);
112+```
113+114+Resolves the promise returned by `promise()`. All consumers are notified by
115+having `$onFulfilled` (which they registered via `$promise->then()`) called with
116+`$value`.
117+118+If `$value` itself is a promise, the promise will transition to the state of
119+this promise once it is resolved.
120+121+See also the [`resolve()` function](#resolve).
122+123+#### Deferred::reject()
124+125+```php
126+$deferred->reject(\Throwable $reason);
127+```
128+129+Rejects the promise returned by `promise()`, signalling that the deferred's
130+computation failed.
131+All consumers are notified by having `$onRejected` (which they registered via
132+`$promise->then()`) called with `$reason`.
133+134+See also the [`reject()` function](#reject).
135+136+### PromiseInterface
137+138+The promise interface provides the common interface for all promise
139+implementations.
140+See [Promise](#promise-2) for the only public implementation exposed by this
141+package.
142+143+A promise represents an eventual outcome, which is either fulfillment (success)
144+and an associated value, or rejection (failure) and an associated reason.
145+146+Once in the fulfilled or rejected state, a promise becomes immutable.
147+Neither its state nor its result (or error) can be modified.
148+149+#### PromiseInterface::then()
150+151+```php
152+$transformedPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null);
153+```
154+155+Transforms a promise's value by applying a function to the promise's fulfillment
156+or rejection value. Returns a new promise for the transformed result.
157+158+The `then()` method registers new fulfilled and rejection handlers with a promise
159+(all parameters are optional):
160+161+ * `$onFulfilled` will be invoked once the promise is fulfilled and passed
162+ the result as the first argument.
163+ * `$onRejected` will be invoked once the promise is rejected and passed the
164+ reason as the first argument.
165+166+It returns a new promise that will fulfill with the return value of either
167+`$onFulfilled` or `$onRejected`, whichever is called, or will reject with
168+the thrown exception if either throws.
169+170+A promise makes the following guarantees about handlers registered in
171+the same call to `then()`:
172+173+ 1. Only one of `$onFulfilled` or `$onRejected` will be called,
174+ never both.
175+ 2. `$onFulfilled` and `$onRejected` will never be called more
176+ than once.
177+178+#### See also
179+180+* [resolve()](#resolve) - Creating a resolved promise
181+* [reject()](#reject) - Creating a rejected promise
182+183+#### PromiseInterface::catch()
184+185+```php
186+$promise->catch(callable $onRejected);
187+```
188+189+Registers a rejection handler for promise. It is a shortcut for:
190+191+```php
192+$promise->then(null, $onRejected);
193+```
194+195+Additionally, you can type hint the `$reason` argument of `$onRejected` to catch
196+only specific errors.
197+198+```php
199+$promise
200+ ->catch(function (\RuntimeException $reason) {
201+ // Only catch \RuntimeException instances
202+ // All other types of errors will propagate automatically
203+ })
204+ ->catch(function (\Throwable $reason) {
205+ // Catch other errors
206+ });
207+```
208+209+#### PromiseInterface::finally()
210+211+```php
212+$newPromise = $promise->finally(callable $onFulfilledOrRejected);
213+```
214+215+Allows you to execute "cleanup" type tasks in a promise chain.
216+217+It arranges for `$onFulfilledOrRejected` to be called, with no arguments,
218+when the promise is either fulfilled or rejected.
219+220+* If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully,
221+ `$newPromise` will fulfill with the same value as `$promise`.
222+* If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a
223+ rejected promise, `$newPromise` will reject with the thrown exception or
224+ rejected promise's reason.
225+* If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully,
226+ `$newPromise` will reject with the same reason as `$promise`.
227+* If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a
228+ rejected promise, `$newPromise` will reject with the thrown exception or
229+ rejected promise's reason.
230+231+`finally()` behaves similarly to the synchronous finally statement. When combined
232+with `catch()`, `finally()` allows you to write code that is similar to the familiar
233+synchronous catch/finally pair.
234+235+Consider the following synchronous code:
236+237+```php
238+try {
239+ return doSomething();
240+} catch (\Throwable $e) {
241+ return handleError($e);
242+} finally {
243+ cleanup();
244+}
245+```
246+247+Similar asynchronous code (with `doSomething()` that returns a promise) can be
248+written:
249+250+```php
251+return doSomething()
252+ ->catch('handleError')
253+ ->finally('cleanup');
254+```
255+256+#### PromiseInterface::cancel()
257+258+``` php
259+$promise->cancel();
260+```
261+262+The `cancel()` method notifies the creator of the promise that there is no
263+further interest in the results of the operation.
264+265+Once a promise is settled (either fulfilled or rejected), calling `cancel()` on
266+a promise has no effect.
267+268+#### ~~PromiseInterface::otherwise()~~
269+270+> Deprecated since v3.0.0, see [`catch()`](#promiseinterfacecatch) instead.
271+272+The `otherwise()` method registers a rejection handler for a promise.
273+274+This method continues to exist only for BC reasons and to ease upgrading
275+between versions. It is an alias for:
276+277+```php
278+$promise->catch($onRejected);
279+```
280+281+#### ~~PromiseInterface::always()~~
282+283+> Deprecated since v3.0.0, see [`finally()`](#promiseinterfacefinally) instead.
284+285+The `always()` method allows you to execute "cleanup" type tasks in a promise chain.
286+287+This method continues to exist only for BC reasons and to ease upgrading
288+between versions. It is an alias for:
289+290+```php
291+$promise->finally($onFulfilledOrRejected);
292+```
293+294+### Promise
295+296+Creates a promise whose state is controlled by the functions passed to
297+`$resolver`.
298+299+```php
300+$resolver = function (callable $resolve, callable $reject) {
301+ // Do some work, possibly asynchronously, and then
302+ // resolve or reject.
303+304+ $resolve($awesomeResult);
305+ // or throw new Exception('Promise rejected');
306+ // or $resolve($anotherPromise);
307+ // or $reject($nastyError);
308+};
309+310+$canceller = function () {
311+ // Cancel/abort any running operations like network connections, streams etc.
312+313+ // Reject promise by throwing an exception
314+ throw new Exception('Promise cancelled');
315+};
316+317+$promise = new React\Promise\Promise($resolver, $canceller);
318+```
319+320+The promise constructor receives a resolver function and an optional canceller
321+function which both will be called with two arguments:
322+323+ * `$resolve($value)` - Primary function that seals the fate of the
324+ returned promise. Accepts either a non-promise value, or another promise.
325+ When called with a non-promise value, fulfills promise with that value.
326+ When called with another promise, e.g. `$resolve($otherPromise)`, promise's
327+ fate will be equivalent to that of `$otherPromise`.
328+ * `$reject($reason)` - Function that rejects the promise. It is recommended to
329+ just throw an exception instead of using `$reject()`.
330+331+If the resolver or canceller throw an exception, the promise will be rejected
332+with that thrown exception as the rejection reason.
333+334+The resolver function will be called immediately, the canceller function only
335+once all consumers called the `cancel()` method of the promise.
336+337+### Functions
338+339+Useful functions for creating and joining collections of promises.
340+341+All functions working on promise collections (like `all()`, `race()`,
342+etc.) support cancellation. This means, if you call `cancel()` on the returned
343+promise, all promises in the collection are cancelled.
344+345+#### resolve()
346+347+```php
348+$promise = React\Promise\resolve(mixed $promiseOrValue);
349+```
350+351+Creates a promise for the supplied `$promiseOrValue`.
352+353+If `$promiseOrValue` is a value, it will be the resolution value of the
354+returned promise.
355+356+If `$promiseOrValue` is a thenable (any object that provides a `then()` method),
357+a trusted promise that follows the state of the thenable is returned.
358+359+If `$promiseOrValue` is a promise, it will be returned as is.
360+361+The resulting `$promise` implements the [`PromiseInterface`](#promiseinterface)
362+and can be consumed like any other promise:
363+364+```php
365+$promise = React\Promise\resolve(42);
366+367+$promise->then(function (int $result): void {
368+ var_dump($result);
369+}, function (\Throwable $e): void {
370+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
371+});
372+```
373+374+#### reject()
375+376+```php
377+$promise = React\Promise\reject(\Throwable $reason);
378+```
379+380+Creates a rejected promise for the supplied `$reason`.
381+382+Note that the [`\Throwable`](https://www.php.net/manual/en/class.throwable.php) interface introduced in PHP 7 covers
383+both user land [`\Exception`](https://www.php.net/manual/en/class.exception.php)'s and
384+[`\Error`](https://www.php.net/manual/en/class.error.php) internal PHP errors. By enforcing `\Throwable` as reason to
385+reject a promise, any language error or user land exception can be used to reject a promise.
386+387+The resulting `$promise` implements the [`PromiseInterface`](#promiseinterface)
388+and can be consumed like any other promise:
389+390+```php
391+$promise = React\Promise\reject(new RuntimeException('Request failed'));
392+393+$promise->then(function (int $result): void {
394+ var_dump($result);
395+}, function (\Throwable $e): void {
396+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
397+});
398+```
399+400+Note that rejected promises should always be handled similar to how any
401+exceptions should always be caught in a `try` + `catch` block. If you remove the
402+last reference to a rejected promise that has not been handled, it will
403+report an unhandled promise rejection:
404+405+```php
406+function incorrect(): int
407+{
408+ $promise = React\Promise\reject(new RuntimeException('Request failed'));
409+410+ // Commented out: No rejection handler registered here.
411+ // $promise->then(null, function (\Throwable $e): void { /* ignore */ });
412+413+ // Returning from a function will remove all local variable references, hence why
414+ // this will report an unhandled promise rejection here.
415+ return 42;
416+}
417+418+// Calling this function will log an error message plus its stack trace:
419+// Unhandled promise rejection with RuntimeException: Request failed in example.php:10
420+incorrect();
421+```
422+423+A rejected promise will be considered "handled" if you catch the rejection
424+reason with either the [`then()` method](#promiseinterfacethen), the
425+[`catch()` method](#promiseinterfacecatch), or the
426+[`finally()` method](#promiseinterfacefinally). Note that each of these methods
427+return a new promise that may again be rejected if you re-throw an exception.
428+429+A rejected promise will also be considered "handled" if you abort the operation
430+with the [`cancel()` method](#promiseinterfacecancel) (which in turn would
431+usually reject the promise if it is still pending).
432+433+See also the [`set_rejection_handler()` function](#set_rejection_handler).
434+435+#### all()
436+437+```php
438+$promise = React\Promise\all(iterable $promisesOrValues);
439+```
440+441+Returns a promise that will resolve only once all the items in
442+`$promisesOrValues` have resolved. The resolution value of the returned promise
443+will be an array containing the resolution values of each of the items in
444+`$promisesOrValues`.
445+446+#### race()
447+448+```php
449+$promise = React\Promise\race(iterable $promisesOrValues);
450+```
451+452+Initiates a competitive race that allows one winner. Returns a promise which is
453+resolved in the same way the first settled promise resolves.
454+455+The returned promise will become **infinitely pending** if `$promisesOrValues`
456+contains 0 items.
457+458+#### any()
459+460+```php
461+$promise = React\Promise\any(iterable $promisesOrValues);
462+```
463+464+Returns a promise that will resolve when any one of the items in
465+`$promisesOrValues` resolves. The resolution value of the returned promise
466+will be the resolution value of the triggering item.
467+468+The returned promise will only reject if *all* items in `$promisesOrValues` are
469+rejected. The rejection value will be a `React\Promise\Exception\CompositeException`
470+which holds all rejection reasons. The rejection reasons can be obtained with
471+`CompositeException::getThrowables()`.
472+473+The returned promise will also reject with a `React\Promise\Exception\LengthException`
474+if `$promisesOrValues` contains 0 items.
475+476+#### set_rejection_handler()
477+478+```php
479+React\Promise\set_rejection_handler(?callable $callback): ?callable;
480+```
481+482+Sets the global rejection handler for unhandled promise rejections.
483+484+Note that rejected promises should always be handled similar to how any
485+exceptions should always be caught in a `try` + `catch` block. If you remove
486+the last reference to a rejected promise that has not been handled, it will
487+report an unhandled promise rejection. See also the [`reject()` function](#reject)
488+for more details.
489+490+The `?callable $callback` argument MUST be a valid callback function that
491+accepts a single `Throwable` argument or a `null` value to restore the
492+default promise rejection handler. The return value of the callback function
493+will be ignored and has no effect, so you SHOULD return a `void` value. The
494+callback function MUST NOT throw or the program will be terminated with a
495+fatal error.
496+497+The function returns the previous rejection handler or `null` if using the
498+default promise rejection handler.
499+500+The default promise rejection handler will log an error message plus its stack
501+trace:
502+503+```php
504+// Unhandled promise rejection with RuntimeException: Unhandled in example.php:2
505+React\Promise\reject(new RuntimeException('Unhandled'));
506+```
507+508+The promise rejection handler may be used to use customize the log message or
509+write to custom log targets. As a rule of thumb, this function should only be
510+used as a last resort and promise rejections are best handled with either the
511+[`then()` method](#promiseinterfacethen), the
512+[`catch()` method](#promiseinterfacecatch), or the
513+[`finally()` method](#promiseinterfacefinally).
514+See also the [`reject()` function](#reject) for more details.
515+516+Examples
517+--------
518+519+### How to use Deferred
520+521+```php
522+function getAwesomeResultPromise()
523+{
524+ $deferred = new React\Promise\Deferred();
525+526+ // Execute a Node.js-style function using the callback pattern
527+ computeAwesomeResultAsynchronously(function (\Throwable $error, $result) use ($deferred) {
528+ if ($error) {
529+ $deferred->reject($error);
530+ } else {
531+ $deferred->resolve($result);
532+ }
533+ });
534+535+ // Return the promise
536+ return $deferred->promise();
537+}
538+539+getAwesomeResultPromise()
540+ ->then(
541+ function ($value) {
542+ // Deferred resolved, do something with $value
543+ },
544+ function (\Throwable $reason) {
545+ // Deferred rejected, do something with $reason
546+ }
547+ );
548+```
549+550+### How promise forwarding works
551+552+A few simple examples to show how the mechanics of Promises/A forwarding works.
553+These examples are contrived, of course, and in real usage, promise chains will
554+typically be spread across several function calls, or even several levels of
555+your application architecture.
556+557+#### Resolution forwarding
558+559+Resolved promises forward resolution values to the next promise.
560+The first promise, `$deferred->promise()`, will resolve with the value passed
561+to `$deferred->resolve()` below.
562+563+Each call to `then()` returns a new promise that will resolve with the return
564+value of the previous handler. This creates a promise "pipeline".
565+566+```php
567+$deferred = new React\Promise\Deferred();
568+569+$deferred->promise()
570+ ->then(function ($x) {
571+ // $x will be the value passed to $deferred->resolve() below
572+ // and returns a *new promise* for $x + 1
573+ return $x + 1;
574+ })
575+ ->then(function ($x) {
576+ // $x === 2
577+ // This handler receives the return value of the
578+ // previous handler.
579+ return $x + 1;
580+ })
581+ ->then(function ($x) {
582+ // $x === 3
583+ // This handler receives the return value of the
584+ // previous handler.
585+ return $x + 1;
586+ })
587+ ->then(function ($x) {
588+ // $x === 4
589+ // This handler receives the return value of the
590+ // previous handler.
591+ echo 'Resolve ' . $x;
592+ });
593+594+$deferred->resolve(1); // Prints "Resolve 4"
595+```
596+597+#### Rejection forwarding
598+599+Rejected promises behave similarly, and also work similarly to try/catch:
600+When you catch an exception, you must rethrow for it to propagate.
601+602+Similarly, when you handle a rejected promise, to propagate the rejection,
603+"rethrow" it by either returning a rejected promise, or actually throwing
604+(since promise translates thrown exceptions into rejections)
605+606+```php
607+$deferred = new React\Promise\Deferred();
608+609+$deferred->promise()
610+ ->then(function ($x) {
611+ throw new \Exception($x + 1);
612+ })
613+ ->catch(function (\Exception $x) {
614+ // Propagate the rejection
615+ throw $x;
616+ })
617+ ->catch(function (\Exception $x) {
618+ // Can also propagate by returning another rejection
619+ return React\Promise\reject(
620+ new \Exception($x->getMessage() + 1)
621+ );
622+ })
623+ ->catch(function ($x) {
624+ echo 'Reject ' . $x->getMessage(); // 3
625+ });
626+627+$deferred->resolve(1); // Prints "Reject 3"
628+```
629+630+#### Mixed resolution and rejection forwarding
631+632+Just like try/catch, you can choose to propagate or not. Mixing resolutions and
633+rejections will still forward handler results in a predictable way.
634+635+```php
636+$deferred = new React\Promise\Deferred();
637+638+$deferred->promise()
639+ ->then(function ($x) {
640+ return $x + 1;
641+ })
642+ ->then(function ($x) {
643+ throw new \Exception($x + 1);
644+ })
645+ ->catch(function (\Exception $x) {
646+ // Handle the rejection, and don't propagate.
647+ // This is like catch without a rethrow
648+ return $x->getMessage() + 1;
649+ })
650+ ->then(function ($x) {
651+ echo 'Mixed ' . $x; // 4
652+ });
653+654+$deferred->resolve(1); // Prints "Mixed 4"
655+```
656+657+Install
658+-------
659+660+The recommended way to install this library is [through Composer](https://getcomposer.org/).
661+[New to Composer?](https://getcomposer.org/doc/00-intro.md)
662+663+This project follows [SemVer](https://semver.org/).
664+This will install the latest supported version from this branch:
665+666+```bash
667+composer require react/promise:^3.2
668+```
669+670+See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
671+672+This project aims to run on any platform and thus does not require any PHP
673+extensions and supports running on PHP 7.1 through current PHP 8+.
674+It's *highly recommended to use the latest supported PHP version* for this project.
675+676+We're committed to providing long-term support (LTS) options and to provide a
677+smooth upgrade path. If you're using an older PHP version, you may use the
678+[`2.x` branch](https://github.com/reactphp/promise/tree/2.x) (PHP 5.4+) or
679+[`1.x` branch](https://github.com/reactphp/promise/tree/1.x) (PHP 5.3+) which both
680+provide a compatible API but do not take advantage of newer language features.
681+You may target multiple versions at the same time to support a wider range of
682+PHP versions like this:
683+684+```bash
685+composer require "react/promise:^3 || ^2 || ^1"
686+```
687+688+## Tests
689+690+To run the test suite, you first need to clone this repo and then install all
691+dependencies [through Composer](https://getcomposer.org/):
692+693+```bash
694+composer install
695+```
696+697+To run the test suite, go to the project root and run:
698+699+```bash
700+vendor/bin/phpunit
701+```
702+703+On top of this, we use PHPStan on max level to ensure type safety across the project:
704+705+```bash
706+vendor/bin/phpstan
707+```
708+709+Credits
710+-------
711+712+Promise is a port of [when.js](https://github.com/cujojs/when)
713+by [Brian Cavalier](https://github.com/briancavalier).
714+715+Also, large parts of the documentation have been ported from the when.js
716+[Wiki](https://github.com/cujojs/when/wiki) and the
717+[API docs](https://github.com/cujojs/when/blob/master/docs/api.md).
718+719+License
720+-------
721+722+Released under the [MIT](LICENSE) license.
···1+<?php
2+3+namespace React\Promise\Exception;
4+5+/**
6+ * Represents an exception that is a composite of one or more other exceptions.
7+ *
8+ * This exception is useful in situations where a promise must be rejected
9+ * with multiple exceptions. It is used for example to reject the returned
10+ * promise from `some()` and `any()` when too many input promises reject.
11+ */
12+class CompositeException extends \Exception
13+{
14+ /** @var \Throwable[] */
15+ private $throwables;
16+17+ /** @param \Throwable[] $throwables */
18+ public function __construct(array $throwables, string $message = '', int $code = 0, ?\Throwable $previous = null)
19+ {
20+ parent::__construct($message, $code, $previous);
21+22+ $this->throwables = $throwables;
23+ }
24+25+ /**
26+ * @return \Throwable[]
27+ */
28+ public function getThrowables(): array
29+ {
30+ return $this->throwables;
31+ }
32+}
···1+<?php
2+3+namespace React\Promise;
4+5+/**
6+ * @template-covariant T
7+ */
8+interface PromiseInterface
9+{
10+ /**
11+ * Transforms a promise's value by applying a function to the promise's fulfillment
12+ * or rejection value. Returns a new promise for the transformed result.
13+ *
14+ * The `then()` method registers new fulfilled and rejection handlers with a promise
15+ * (all parameters are optional):
16+ *
17+ * * `$onFulfilled` will be invoked once the promise is fulfilled and passed
18+ * the result as the first argument.
19+ * * `$onRejected` will be invoked once the promise is rejected and passed the
20+ * reason as the first argument.
21+ *
22+ * It returns a new promise that will fulfill with the return value of either
23+ * `$onFulfilled` or `$onRejected`, whichever is called, or will reject with
24+ * the thrown exception if either throws.
25+ *
26+ * A promise makes the following guarantees about handlers registered in
27+ * the same call to `then()`:
28+ *
29+ * 1. Only one of `$onFulfilled` or `$onRejected` will be called,
30+ * never both.
31+ * 2. `$onFulfilled` and `$onRejected` will never be called more
32+ * than once.
33+ *
34+ * @template TFulfilled
35+ * @template TRejected
36+ * @param ?(callable((T is void ? null : T)): (PromiseInterface<TFulfilled>|TFulfilled)) $onFulfilled
37+ * @param ?(callable(\Throwable): (PromiseInterface<TRejected>|TRejected)) $onRejected
38+ * @return PromiseInterface<($onRejected is null ? ($onFulfilled is null ? T : TFulfilled) : ($onFulfilled is null ? T|TRejected : TFulfilled|TRejected))>
39+ */
40+ public function then(?callable $onFulfilled = null, ?callable $onRejected = null): PromiseInterface;
41+42+ /**
43+ * Registers a rejection handler for promise. It is a shortcut for:
44+ *
45+ * ```php
46+ * $promise->then(null, $onRejected);
47+ * ```
48+ *
49+ * Additionally, you can type hint the `$reason` argument of `$onRejected` to catch
50+ * only specific errors.
51+ *
52+ * @template TThrowable of \Throwable
53+ * @template TRejected
54+ * @param callable(TThrowable): (PromiseInterface<TRejected>|TRejected) $onRejected
55+ * @return PromiseInterface<T|TRejected>
56+ */
57+ public function catch(callable $onRejected): PromiseInterface;
58+59+ /**
60+ * Allows you to execute "cleanup" type tasks in a promise chain.
61+ *
62+ * It arranges for `$onFulfilledOrRejected` to be called, with no arguments,
63+ * when the promise is either fulfilled or rejected.
64+ *
65+ * * If `$promise` fulfills, and `$onFulfilledOrRejected` returns successfully,
66+ * `$newPromise` will fulfill with the same value as `$promise`.
67+ * * If `$promise` fulfills, and `$onFulfilledOrRejected` throws or returns a
68+ * rejected promise, `$newPromise` will reject with the thrown exception or
69+ * rejected promise's reason.
70+ * * If `$promise` rejects, and `$onFulfilledOrRejected` returns successfully,
71+ * `$newPromise` will reject with the same reason as `$promise`.
72+ * * If `$promise` rejects, and `$onFulfilledOrRejected` throws or returns a
73+ * rejected promise, `$newPromise` will reject with the thrown exception or
74+ * rejected promise's reason.
75+ *
76+ * `finally()` behaves similarly to the synchronous finally statement. When combined
77+ * with `catch()`, `finally()` allows you to write code that is similar to the familiar
78+ * synchronous catch/finally pair.
79+ *
80+ * Consider the following synchronous code:
81+ *
82+ * ```php
83+ * try {
84+ * return doSomething();
85+ * } catch(\Exception $e) {
86+ * return handleError($e);
87+ * } finally {
88+ * cleanup();
89+ * }
90+ * ```
91+ *
92+ * Similar asynchronous code (with `doSomething()` that returns a promise) can be
93+ * written:
94+ *
95+ * ```php
96+ * return doSomething()
97+ * ->catch('handleError')
98+ * ->finally('cleanup');
99+ * ```
100+ *
101+ * @param callable(): (void|PromiseInterface<void>) $onFulfilledOrRejected
102+ * @return PromiseInterface<T>
103+ */
104+ public function finally(callable $onFulfilledOrRejected): PromiseInterface;
105+106+ /**
107+ * The `cancel()` method notifies the creator of the promise that there is no
108+ * further interest in the results of the operation.
109+ *
110+ * Once a promise is settled (either fulfilled or rejected), calling `cancel()` on
111+ * a promise has no effect.
112+ *
113+ * @return void
114+ */
115+ public function cancel(): void;
116+117+ /**
118+ * [Deprecated] Registers a rejection handler for a promise.
119+ *
120+ * This method continues to exist only for BC reasons and to ease upgrading
121+ * between versions. It is an alias for:
122+ *
123+ * ```php
124+ * $promise->catch($onRejected);
125+ * ```
126+ *
127+ * @template TThrowable of \Throwable
128+ * @template TRejected
129+ * @param callable(TThrowable): (PromiseInterface<TRejected>|TRejected) $onRejected
130+ * @return PromiseInterface<T|TRejected>
131+ * @deprecated 3.0.0 Use catch() instead
132+ * @see self::catch()
133+ */
134+ public function otherwise(callable $onRejected): PromiseInterface;
135+136+ /**
137+ * [Deprecated] Allows you to execute "cleanup" type tasks in a promise chain.
138+ *
139+ * This method continues to exist only for BC reasons and to ease upgrading
140+ * between versions. It is an alias for:
141+ *
142+ * ```php
143+ * $promise->finally($onFulfilledOrRejected);
144+ * ```
145+ *
146+ * @param callable(): (void|PromiseInterface<void>) $onFulfilledOrRejected
147+ * @return PromiseInterface<T>
148+ * @deprecated 3.0.0 Use finally() instead
149+ * @see self::finally()
150+ */
151+ public function always(callable $onFulfilledOrRejected): PromiseInterface;
152+}
···1+<?php
2+3+namespace React\Promise;
4+5+use React\Promise\Exception\CompositeException;
6+use React\Promise\Internal\FulfilledPromise;
7+use React\Promise\Internal\RejectedPromise;
8+9+/**
10+ * Creates a promise for the supplied `$promiseOrValue`.
11+ *
12+ * If `$promiseOrValue` is a value, it will be the resolution value of the
13+ * returned promise.
14+ *
15+ * If `$promiseOrValue` is a thenable (any object that provides a `then()` method),
16+ * a trusted promise that follows the state of the thenable is returned.
17+ *
18+ * If `$promiseOrValue` is a promise, it will be returned as is.
19+ *
20+ * @template T
21+ * @param PromiseInterface<T>|T $promiseOrValue
22+ * @return PromiseInterface<T>
23+ */
24+function resolve($promiseOrValue): PromiseInterface
25+{
26+ if ($promiseOrValue instanceof PromiseInterface) {
27+ return $promiseOrValue;
28+ }
29+30+ if (\is_object($promiseOrValue) && \method_exists($promiseOrValue, 'then')) {
31+ $canceller = null;
32+33+ if (\method_exists($promiseOrValue, 'cancel')) {
34+ $canceller = [$promiseOrValue, 'cancel'];
35+ assert(\is_callable($canceller));
36+ }
37+38+ /** @var Promise<T> */
39+ return new Promise(function (callable $resolve, callable $reject) use ($promiseOrValue): void {
40+ $promiseOrValue->then($resolve, $reject);
41+ }, $canceller);
42+ }
43+44+ return new FulfilledPromise($promiseOrValue);
45+}
46+47+/**
48+ * Creates a rejected promise for the supplied `$reason`.
49+ *
50+ * If `$reason` is a value, it will be the rejection value of the
51+ * returned promise.
52+ *
53+ * If `$reason` is a promise, its completion value will be the rejected
54+ * value of the returned promise.
55+ *
56+ * This can be useful in situations where you need to reject a promise without
57+ * throwing an exception. For example, it allows you to propagate a rejection with
58+ * the value of another promise.
59+ *
60+ * @return PromiseInterface<never>
61+ */
62+function reject(\Throwable $reason): PromiseInterface
63+{
64+ return new RejectedPromise($reason);
65+}
66+67+/**
68+ * Returns a promise that will resolve only once all the items in
69+ * `$promisesOrValues` have resolved. The resolution value of the returned promise
70+ * will be an array containing the resolution values of each of the items in
71+ * `$promisesOrValues`.
72+ *
73+ * @template T
74+ * @param iterable<PromiseInterface<T>|T> $promisesOrValues
75+ * @return PromiseInterface<array<T>>
76+ */
77+function all(iterable $promisesOrValues): PromiseInterface
78+{
79+ $cancellationQueue = new Internal\CancellationQueue();
80+81+ /** @var Promise<array<T>> */
82+ return new Promise(function (callable $resolve, callable $reject) use ($promisesOrValues, $cancellationQueue): void {
83+ $toResolve = 0;
84+ /** @var bool */
85+ $continue = true;
86+ $values = [];
87+88+ foreach ($promisesOrValues as $i => $promiseOrValue) {
89+ $cancellationQueue->enqueue($promiseOrValue);
90+ $values[$i] = null;
91+ ++$toResolve;
92+93+ resolve($promiseOrValue)->then(
94+ function ($value) use ($i, &$values, &$toResolve, &$continue, $resolve): void {
95+ $values[$i] = $value;
96+97+ if (0 === --$toResolve && !$continue) {
98+ $resolve($values);
99+ }
100+ },
101+ function (\Throwable $reason) use (&$continue, $reject): void {
102+ $continue = false;
103+ $reject($reason);
104+ }
105+ );
106+107+ if (!$continue && !\is_array($promisesOrValues)) {
108+ break;
109+ }
110+ }
111+112+ $continue = false;
113+ if ($toResolve === 0) {
114+ $resolve($values);
115+ }
116+ }, $cancellationQueue);
117+}
118+119+/**
120+ * Initiates a competitive race that allows one winner. Returns a promise which is
121+ * resolved in the same way the first settled promise resolves.
122+ *
123+ * The returned promise will become **infinitely pending** if `$promisesOrValues`
124+ * contains 0 items.
125+ *
126+ * @template T
127+ * @param iterable<PromiseInterface<T>|T> $promisesOrValues
128+ * @return PromiseInterface<T>
129+ */
130+function race(iterable $promisesOrValues): PromiseInterface
131+{
132+ $cancellationQueue = new Internal\CancellationQueue();
133+134+ /** @var Promise<T> */
135+ return new Promise(function (callable $resolve, callable $reject) use ($promisesOrValues, $cancellationQueue): void {
136+ $continue = true;
137+138+ foreach ($promisesOrValues as $promiseOrValue) {
139+ $cancellationQueue->enqueue($promiseOrValue);
140+141+ resolve($promiseOrValue)->then($resolve, $reject)->finally(function () use (&$continue): void {
142+ $continue = false;
143+ });
144+145+ if (!$continue && !\is_array($promisesOrValues)) {
146+ break;
147+ }
148+ }
149+ }, $cancellationQueue);
150+}
151+152+/**
153+ * Returns a promise that will resolve when any one of the items in
154+ * `$promisesOrValues` resolves. The resolution value of the returned promise
155+ * will be the resolution value of the triggering item.
156+ *
157+ * The returned promise will only reject if *all* items in `$promisesOrValues` are
158+ * rejected. The rejection value will be an array of all rejection reasons.
159+ *
160+ * The returned promise will also reject with a `React\Promise\Exception\LengthException`
161+ * if `$promisesOrValues` contains 0 items.
162+ *
163+ * @template T
164+ * @param iterable<PromiseInterface<T>|T> $promisesOrValues
165+ * @return PromiseInterface<T>
166+ */
167+function any(iterable $promisesOrValues): PromiseInterface
168+{
169+ $cancellationQueue = new Internal\CancellationQueue();
170+171+ /** @var Promise<T> */
172+ return new Promise(function (callable $resolve, callable $reject) use ($promisesOrValues, $cancellationQueue): void {
173+ $toReject = 0;
174+ $continue = true;
175+ $reasons = [];
176+177+ foreach ($promisesOrValues as $i => $promiseOrValue) {
178+ $cancellationQueue->enqueue($promiseOrValue);
179+ ++$toReject;
180+181+ resolve($promiseOrValue)->then(
182+ function ($value) use ($resolve, &$continue): void {
183+ $continue = false;
184+ $resolve($value);
185+ },
186+ function (\Throwable $reason) use ($i, &$reasons, &$toReject, $reject, &$continue): void {
187+ $reasons[$i] = $reason;
188+189+ if (0 === --$toReject && !$continue) {
190+ $reject(new CompositeException(
191+ $reasons,
192+ 'All promises rejected.'
193+ ));
194+ }
195+ }
196+ );
197+198+ if (!$continue && !\is_array($promisesOrValues)) {
199+ break;
200+ }
201+ }
202+203+ $continue = false;
204+ if ($toReject === 0 && !$reasons) {
205+ $reject(new Exception\LengthException(
206+ 'Must contain at least 1 item but contains only 0 items.'
207+ ));
208+ } elseif ($toReject === 0) {
209+ $reject(new CompositeException(
210+ $reasons,
211+ 'All promises rejected.'
212+ ));
213+ }
214+ }, $cancellationQueue);
215+}
216+217+/**
218+ * Sets the global rejection handler for unhandled promise rejections.
219+ *
220+ * Note that rejected promises should always be handled similar to how any
221+ * exceptions should always be caught in a `try` + `catch` block. If you remove
222+ * the last reference to a rejected promise that has not been handled, it will
223+ * report an unhandled promise rejection. See also the [`reject()` function](#reject)
224+ * for more details.
225+ *
226+ * The `?callable $callback` argument MUST be a valid callback function that
227+ * accepts a single `Throwable` argument or a `null` value to restore the
228+ * default promise rejection handler. The return value of the callback function
229+ * will be ignored and has no effect, so you SHOULD return a `void` value. The
230+ * callback function MUST NOT throw or the program will be terminated with a
231+ * fatal error.
232+ *
233+ * The function returns the previous rejection handler or `null` if using the
234+ * default promise rejection handler.
235+ *
236+ * The default promise rejection handler will log an error message plus its
237+ * stack trace:
238+ *
239+ * ```php
240+ * // Unhandled promise rejection with RuntimeException: Unhandled in example.php:2
241+ * React\Promise\reject(new RuntimeException('Unhandled'));
242+ * ```
243+ *
244+ * The promise rejection handler may be used to use customize the log message or
245+ * write to custom log targets. As a rule of thumb, this function should only be
246+ * used as a last resort and promise rejections are best handled with either the
247+ * [`then()` method](#promiseinterfacethen), the
248+ * [`catch()` method](#promiseinterfacecatch), or the
249+ * [`finally()` method](#promiseinterfacefinally).
250+ * See also the [`reject()` function](#reject) for more details.
251+ *
252+ * @param callable(\Throwable):void|null $callback
253+ * @return callable(\Throwable):void|null
254+ */
255+function set_rejection_handler(?callable $callback): ?callable
256+{
257+ static $current = null;
258+ $previous = $current;
259+ $current = $callback;
260+261+ return $previous;
262+}
263+264+/**
265+ * @internal
266+ */
267+function _checkTypehint(callable $callback, \Throwable $reason): bool
268+{
269+ if (\is_array($callback)) {
270+ $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]);
271+ } elseif (\is_object($callback) && !$callback instanceof \Closure) {
272+ $callbackReflection = new \ReflectionMethod($callback, '__invoke');
273+ } else {
274+ assert($callback instanceof \Closure || \is_string($callback));
275+ $callbackReflection = new \ReflectionFunction($callback);
276+ }
277+278+ $parameters = $callbackReflection->getParameters();
279+280+ if (!isset($parameters[0])) {
281+ return true;
282+ }
283+284+ $expectedException = $parameters[0];
285+286+ // Extract the type of the argument and handle different possibilities
287+ $type = $expectedException->getType();
288+289+ $isTypeUnion = true;
290+ $types = [];
291+292+ switch (true) {
293+ case $type === null:
294+ break;
295+ case $type instanceof \ReflectionNamedType:
296+ $types = [$type];
297+ break;
298+ case $type instanceof \ReflectionIntersectionType:
299+ $isTypeUnion = false;
300+ case $type instanceof \ReflectionUnionType:
301+ $types = $type->getTypes();
302+ break;
303+ default:
304+ throw new \LogicException('Unexpected return value of ReflectionParameter::getType');
305+ }
306+307+ // If there is no type restriction, it matches
308+ if (empty($types)) {
309+ return true;
310+ }
311+312+ foreach ($types as $type) {
313+314+ if ($type instanceof \ReflectionIntersectionType) {
315+ foreach ($type->getTypes() as $typeToMatch) {
316+ assert($typeToMatch instanceof \ReflectionNamedType);
317+ $name = $typeToMatch->getName();
318+ if (!($matches = (!$typeToMatch->isBuiltin() && $reason instanceof $name))) {
319+ break;
320+ }
321+ }
322+ assert(isset($matches));
323+ } else {
324+ assert($type instanceof \ReflectionNamedType);
325+ $name = $type->getName();
326+ $matches = !$type->isBuiltin() && $reason instanceof $name;
327+ }
328+329+ // If we look for a single match (union), we can return early on match
330+ // If we look for a full match (intersection), we can return early on mismatch
331+ if ($matches) {
332+ if ($isTypeUnion) {
333+ return true;
334+ }
335+ } else {
336+ if (!$isTypeUnion) {
337+ return false;
338+ }
339+ }
340+ }
341+342+ // If we look for a single match (union) and did not return early, we matched no type and are false
343+ // If we look for a full match (intersection) and did not return early, we matched all types and are true
344+ return $isTypeUnion ? false : true;
345+}