friendship ended with social-app. php is my new best friend
1# EventLoop 2 3[![CI status](https://github.com/reactphp/event-loop/actions/workflows/ci.yml/badge.svg)](https://github.com/reactphp/event-loop/actions) 4[![installs on Packagist](https://img.shields.io/packagist/dt/react/event-loop?color=blue&label=installs%20on%20Packagist)](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 8In order for async based libraries to be interoperable, they need to use the 9same event loop. This component provides a common `LoopInterface` that any 10library can target. This allows them to be used in the same loop, with one 11single [`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 50Here is an async HTTP server built with just the event loop. 51 52```php 53<?php 54 55use React\EventLoop\Loop; 56 57require __DIR__ . '/vendor/autoload.php'; 58 59$server = stream_socket_server('tcp://127.0.0.1:8080'); 60stream_set_blocking($server, false); 61 62Loop::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 76Loop::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 83See also the [examples](examples). 84 85## Usage 86 87Typical applications would use the [`Loop` class](#loop) to use the default 88event loop like this: 89 90```php 91use React\EventLoop\Loop; 92 93$timer = Loop::addPeriodicTimer(0.1, function () { 94 echo 'Tick' . PHP_EOL; 95}); 96 97Loop::addTimer(1.0, function () use ($timer) { 98 Loop::cancelTimer($timer); 99 echo 'Done' . PHP_EOL; 100}); 101``` 102 103As an alternative, you can also explicitly create an event loop instance at the 104beginning, reuse it throughout your program and finally run it at the end of the 105program 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 122While the former is more concise, the latter is more explicit. 123In both cases, the program would perform the exact same steps. 124 1251. 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)). 1292. 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. 1333. 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 137As of `v1.2.0`, we highly recommend using the [`Loop` class](#loop). 138The explicit loop instructions are still valid and may still be useful in some 139applications, especially for a transition period towards the more concise style. 140 141### Loop 142 143The `Loop` class exists as a convenient global accessor for the event loop. 144 145#### Loop methods 146 147The `Loop` class provides all methods that exist on the [`LoopInterface`](#loopinterface) 148as 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 163If you're working with the event loop in your application code, it's often 164easiest to directly interface with the static methods defined on the `Loop` class 165like this: 166 167```php 168use React\EventLoop\Loop; 169 170$timer = Loop::addPeriodicTimer(0.1, function () { 171 echo 'Tick' . PHP_EOL; 172}); 173 174Loop::addTimer(1.0, function () use ($timer) { 175 Loop::cancelTimer($timer); 176 echo 'Done' . PHP_EOL; 177}); 178``` 179 180On the other hand, if you're familiar with object-oriented programming (OOP) and 181dependency injection (DI), you may want to inject an event loop instance and 182invoke instance methods on the `LoopInterface` like this: 183 184```php 185use React\EventLoop\Loop; 186use React\EventLoop\LoopInterface; 187 188class 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 210Each static method call will be forwarded as-is to the underlying event loop 211instance by using the [`Loop::get()`](#get) call internally. 212See [`LoopInterface`](#loopinterface) for more details about available methods. 213 214#### Loop autorun 215 216When using the `Loop` class, it will automatically execute the loop at the end of 217the program. This means the following example will schedule a timer and will 218automatically execute the program until the timer event fires: 219 220```php 221use React\EventLoop\Loop; 222 223Loop::addTimer(1.0, function () { 224 echo 'Hello' . PHP_EOL; 225}); 226``` 227 228As of `v1.2.0`, we highly recommend using the `Loop` class this way and omitting any 229explicit [`run()`](#run) calls. For BC reasons, the explicit [`run()`](#run) 230method is still valid and may still be useful in some applications, especially 231for a transition period towards the more concise style. 232 233If 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 235a global exception handler like this: 236 237```php 238use React\EventLoop\Loop; 239 240Loop::addTimer(10.0, function () { 241 echo 'Never happens'; 242}); 243 244set_exception_handler(function (Throwable $e) { 245 echo 'Error: ' . $e->getMessage() . PHP_EOL; 246 Loop::stop(); 247}); 248 249throw new RuntimeException('Demo'); 250``` 251 252#### get() 253 254The `get(): LoopInterface` method can be used to 255get the currently active event loop instance. 256 257This method will always return the same event loop instance throughout the 258lifetime of your application. 259 260```php 261use React\EventLoop\Loop; 262use React\EventLoop\LoopInterface; 263 264$loop = Loop::get(); 265 266assert($loop instanceof LoopInterface); 267assert($loop === Loop::get()); 268``` 269 270This is particularly useful if you're using object-oriented programming (OOP) 271and dependency injection (DI). In this case, you may want to inject an event 272loop instance and invoke instance methods on the `LoopInterface` like this: 273 274```php 275use React\EventLoop\Loop; 276use React\EventLoop\LoopInterface; 277 278class 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 300See [`LoopInterface`](#loopinterface) for more details about available methods. 301 302### ~~Factory~~ 303 304> Deprecated since v1.2.0, see [`Loop` class](#loop) instead. 305 306The 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 313The deprecated `create(): LoopInterface` method can be used to 314create 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 324This method always returns an instance implementing [`LoopInterface`](#loopinterface), 325the actual [event loop implementation](#loop-implementations) is an implementation detail. 326 327This method should usually only be called once at the beginning of the program. 328 329### Loop implementations 330 331In addition to the [`LoopInterface`](#loopinterface), there are a number of 332event loop implementations provided. 333 334All 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 341For most consumers of this package, the underlying event loop implementation is 342an implementation detail. 343You should use the [`Loop` class](#loop) to automatically create a new instance. 344 345Advanced! If you explicitly need a certain event loop implementation, you can 346manually instantiate one of the following classes. 347Note that you may have to install the required PHP extensions for the respective 348event loop implementation first or they will throw a `BadMethodCallException` on creation. 349 350#### StreamSelectLoop 351 352A `stream_select()` based event loop. 353 354This uses the [`stream_select()`](https://www.php.net/manual/en/function.stream-select.php) 355function and is the only implementation that works out of the box with PHP. 356 357This event loop works out of the box on PHP 5.3 through PHP 8+ and HHVM. 358This means that no installation is required and this library works on all 359platforms and supported PHP versions. 360Accordingly, the [`Loop` class](#loop) and the deprecated [`Factory`](#factory) 361will use this event loop by default if you do not install any of the event loop 362extensions listed below. 363 364Under the hood, it does a simple `select` system call. 365This 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). 368This means that you may run into issues when handling thousands of streams 369concurrently and you may want to look into using one of the alternative 370event loop implementations listed below in this case. 371If your use case is among the many common use cases that involve handling only 372dozens or a few hundred streams at once, then this event loop implementation 373performs really well. 374 375If you want to use signal handling (see also [`addSignal()`](#addsignal) below), 376this event loop implementation requires `ext-pcntl`. 377This extension is only available for Unix-like platforms and does not support 378Windows. 379It is commonly installed as part of many PHP distributions. 380If this extension is missing (or you're running on Windows), signal handling is 381not supported and throws a `BadMethodCallException` instead. 382 383This event loop is known to rely on wall-clock time to schedule future timers 384when using any version before PHP 7.3, because a monotonic time source is 385only available as of PHP 7.3 (`hrtime()`). 386While this does not affect many common use cases, this is an important 387distinction for programs that rely on a high time precision or on systems 388that are subject to discontinuous time adjustments (time jumps). 389This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and 390then adjust your system time forward by 20s, the timer may trigger in 10s. 391See also [`addTimer()`](#addtimer) for more details. 392 393#### ExtEventLoop 394 395An `ext-event` based event loop. 396 397This uses the [`event` PECL extension](https://pecl.php.net/package/event), 398that provides an interface to `libevent` library. 399`libevent` itself supports a number of system-specific backends (epoll, kqueue). 400 401This loop is known to work with PHP 5.4 through PHP 8+. 402 403#### ExtEvLoop 404 405An `ext-ev` based event loop. 406 407This loop uses the [`ev` PECL extension](https://pecl.php.net/package/ev), 408that provides an interface to `libev` library. 409`libev` itself supports a number of system-specific backends (epoll, kqueue). 410 411 412This loop is known to work with PHP 5.4 through PHP 8+. 413 414#### ExtUvLoop 415 416An `ext-uv` based event loop. 417 418This loop uses the [`uv` PECL extension](https://pecl.php.net/package/uv), 419that provides an interface to `libuv` library. 420`libuv` itself supports a number of system-specific backends (epoll, kqueue). 421 422This loop is known to work with PHP 7+. 423 424#### ~~ExtLibeventLoop~~ 425 426> Deprecated since v1.2.0, use [`ExtEventLoop`](#exteventloop) instead. 427 428An `ext-libevent` based event loop. 429 430This uses the [`libevent` PECL extension](https://pecl.php.net/package/libevent), 431that provides an interface to `libevent` library. 432`libevent` itself supports a number of system-specific backends (epoll, kqueue). 433 434This event loop does only work with PHP 5. 435An [unofficial update](https://github.com/php/pecl-event-libevent/pull/2) for 436PHP 7 does exist, but it is known to cause regular crashes due to `SEGFAULT`s. 437To reiterate: Using this event loop on PHP 7 is not recommended. 438Accordingly, neither the [`Loop` class](#loop) nor the deprecated 439[`Factory` class](#factory) will try to use this event loop on PHP 7. 440 441This event loop is known to trigger a readable listener only if 442the stream *becomes* readable (edge-triggered) and may not trigger if the 443stream has already been readable from the beginning. 444This also implies that a stream may not be recognized as readable when data 445is still left in PHP's internal stream buffers. 446As such, it's recommended to use `stream_set_read_buffer($stream, 0);` 447to disable PHP's internal read buffer in this case. 448See also [`addReadStream()`](#addreadstream) for more details. 449 450#### ~~ExtLibevLoop~~ 451 452> Deprecated since v1.2.0, use [`ExtEvLoop`](#extevloop) instead. 453 454An `ext-libev` based event loop. 455 456This uses an [unofficial `libev` extension](https://github.com/m4rw3r/php-libev), 457that provides an interface to `libev` library. 458`libev` itself supports a number of system-specific backends (epoll, kqueue). 459 460This loop does only work with PHP 5. 461An update for PHP 7 is [unlikely](https://github.com/m4rw3r/php-libev/issues/8) 462to happen any time soon. 463 464### LoopInterface 465 466#### run() 467 468The `run(): void` method can be used to 469run the event loop until there are no more tasks to perform. 470 471For many applications, this method is the only directly visible 472invocation on the event loop. 473As a rule of thumb, it is usually recommended to attach everything to the 474same loop instance and then run the loop once at the bottom end of the 475application. 476 477```php 478$loop->run(); 479``` 480 481This method will keep the loop running until there are no more tasks 482to perform. In other words: This method will block until the last 483timer, stream and/or signal has been removed. 484 485Likewise, it is imperative to ensure the application actually invokes 486this method once. Adding listeners to the loop and missing to actually 487run it will result in the application exiting without actually waiting 488for any of the attached listeners. 489 490This method MUST NOT be called while the loop is already running. 491This method MAY be called more than once after it has explicitly been 492[`stop()`ped](#stop) or after it automatically stopped because it 493previously did no longer have anything to do. 494 495#### stop() 496 497The `stop(): void` method can be used to 498instruct a running event loop to stop. 499 500This method is considered advanced usage and should be used with care. 501As a rule of thumb, it is usually recommended to let the loop stop 502only automatically when it no longer has anything to do. 503 504This 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 512Calling this method on a loop instance that is not currently running or 513on a loop instance that has already been stopped has no effect. 514 515#### addTimer() 516 517The `addTimer(float $interval, callable $callback): TimerInterface` method can be used to 518enqueue a callback to be invoked once after the given interval. 519 520The second parameter MUST be a timer callback function that accepts 521the timer instance as its only parameter. 522If you don't use the timer instance inside your timer callback function 523you MAY use a function which has no parameters at all. 524 525The timer callback function MUST NOT throw an `Exception`. 526The return value of the timer callback function will be ignored and has 527no effect, so for performance reasons you're recommended to not return 528any excessive data structures. 529 530This method returns a timer instance. The same timer instance will also be 531passed into the timer callback function as described above. 532You can invoke [`cancelTimer`](#canceltimer) to cancel a pending timer. 533Unlike [`addPeriodicTimer()`](#addperiodictimer), this method will ensure 534the 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 546See also [example #1](examples). 547 548If you want to access any variables within your callback function, you 549can bind arbitrary data to a callback closure like this: 550 551```php 552function hello($name, LoopInterface $loop) 553{ 554 $loop->addTimer(1.0, function () use ($name) { 555 echo "hello $name\n"; 556 }); 557} 558 559hello('Tester', $loop); 560``` 561 562This interface does not enforce any particular timer resolution, so 563special care may have to be taken if you rely on very high precision with 564millisecond accuracy or below. Event loop implementations SHOULD work on 565a best effort basis and SHOULD provide at least millisecond accuracy 566unless otherwise noted. Many existing event loop implementations are 567known to provide microsecond accuracy, but it's generally not recommended 568to rely on this high precision. 569 570Similarly, the execution order of timers scheduled to execute at the 571same time (within its possible accuracy) is not guaranteed. 572 573This interface suggests that event loop implementations SHOULD use a 574monotonic time source if available. Given that a monotonic time source is 575only available as of PHP 7.3 by default, event loop implementations MAY 576fall back to using wall-clock time. 577While this does not affect many common use cases, this is an important 578distinction for programs that rely on a high time precision or on systems 579that are subject to discontinuous time adjustments (time jumps). 580This means that if you schedule a timer to trigger in 30s and then adjust 581your system time forward by 20s, the timer SHOULD still trigger in 30s. 582See also [event loop implementations](#loop-implementations) for more details. 583 584#### addPeriodicTimer() 585 586The `addPeriodicTimer(float $interval, callable $callback): TimerInterface` method can be used to 587enqueue a callback to be invoked repeatedly after the given interval. 588 589The second parameter MUST be a timer callback function that accepts 590the timer instance as its only parameter. 591If you don't use the timer instance inside your timer callback function 592you MAY use a function which has no parameters at all. 593 594The timer callback function MUST NOT throw an `Exception`. 595The return value of the timer callback function will be ignored and has 596no effect, so for performance reasons you're recommended to not return 597any excessive data structures. 598 599This method returns a timer instance. The same timer instance will also be 600passed into the timer callback function as described above. 601Unlike [`addTimer()`](#addtimer), this method will ensure the callback 602will 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 616See also [example #2](examples). 617 618If you want to limit the number of executions, you can bind 619arbitrary data to a callback closure like this: 620 621```php 622function 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 635hello('Tester', $loop); 636``` 637 638This interface does not enforce any particular timer resolution, so 639special care may have to be taken if you rely on very high precision with 640millisecond accuracy or below. Event loop implementations SHOULD work on 641a best effort basis and SHOULD provide at least millisecond accuracy 642unless otherwise noted. Many existing event loop implementations are 643known to provide microsecond accuracy, but it's generally not recommended 644to rely on this high precision. 645 646Similarly, the execution order of timers scheduled to execute at the 647same time (within its possible accuracy) is not guaranteed. 648 649This interface suggests that event loop implementations SHOULD use a 650monotonic time source if available. Given that a monotonic time source is 651only available as of PHP 7.3 by default, event loop implementations MAY 652fall back to using wall-clock time. 653While this does not affect many common use cases, this is an important 654distinction for programs that rely on a high time precision or on systems 655that are subject to discontinuous time adjustments (time jumps). 656This means that if you schedule a timer to trigger in 30s and then adjust 657your system time forward by 20s, the timer SHOULD still trigger in 30s. 658See also [event loop implementations](#loop-implementations) for more details. 659 660Additionally, periodic timers may be subject to timer drift due to 661re-scheduling after each invocation. As such, it's generally not 662recommended to rely on this for high precision intervals with millisecond 663accuracy or below. 664 665#### cancelTimer() 666 667The `cancelTimer(TimerInterface $timer): void` method can be used to 668cancel a pending timer. 669 670See also [`addPeriodicTimer()`](#addperiodictimer) and [example #2](examples). 671 672Calling this method on a timer instance that has not been added to this 673loop instance or on a timer that has already been cancelled has no effect. 674 675#### futureTick() 676 677The `futureTick(callable $listener): void` method can be used to 678schedule a callback to be invoked on a future tick of the event loop. 679 680This works very much similar to timers with an interval of zero seconds, 681but does not require the overhead of scheduling a timer queue. 682 683The tick callback function MUST be able to accept zero parameters. 684 685The tick callback function MUST NOT throw an `Exception`. 686The return value of the tick callback function will be ignored and has 687no effect, so for performance reasons you're recommended to not return 688any excessive data structures. 689 690If you want to access any variables within your callback function, you 691can bind arbitrary data to a callback closure like this: 692 693```php 694function hello($name, LoopInterface $loop) 695{ 696 $loop->futureTick(function () use ($name) { 697 echo "hello $name\n"; 698 }); 699} 700 701hello('Tester', $loop); 702``` 703 704Unlike timers, tick callbacks are guaranteed to be executed in the order 705they are enqueued. 706Also, once a callback is enqueued, there's no way to cancel this operation. 707 708This is often used to break down bigger tasks into smaller steps (a form 709of cooperative multitasking). 710 711```php 712$loop->futureTick(function () { 713 echo 'b'; 714}); 715$loop->futureTick(function () { 716 echo 'c'; 717}); 718echo 'a'; 719``` 720 721See also [example #3](examples). 722 723#### addSignal() 724 725The `addSignal(int $signal, callable $listener): void` method can be used to 726register a listener to be notified when a signal has been caught by this process. 727 728This is useful to catch user interrupt signals or shutdown signals from 729tools like `supervisor` or `systemd`. 730 731The second parameter MUST be a listener callback function that accepts 732the signal as its only parameter. 733If you don't use the signal inside your listener callback function 734you MAY use a function which has no parameters at all. 735 736The listener callback function MUST NOT throw an `Exception`. 737The return value of the listener callback function will be ignored and has 738no effect, so for performance reasons you're recommended to not return 739any excessive data structures. 740 741```php 742$loop->addSignal(SIGINT, function (int $signal) { 743 echo 'Caught user interrupt signal' . PHP_EOL; 744}); 745``` 746 747See also [example #4](examples). 748 749Signaling is only available on Unix-like platforms, Windows isn't 750supported due to operating system limitations. 751This method may throw a `BadMethodCallException` if signals aren't 752supported on this platform, for example when required extensions are 753missing. 754 755**Note: A listener can only be added once to the same signal, any 756attempts to add it more than once will be ignored.** 757 758#### removeSignal() 759 760The `removeSignal(int $signal, callable $listener): void` method can be used to 761remove a previously added signal listener. 762 763```php 764$loop->removeSignal(SIGINT, $listener); 765``` 766 767Any 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 776The `addReadStream(resource $stream, callable $callback): void` method can be used to 777register a listener to be notified when a stream is ready to read. 778 779The first parameter MUST be a valid stream resource that supports 780checking whether it is ready to read by this loop implementation. 781A single stream resource MUST NOT be added more than once. 782Instead, either call [`removeReadStream()`](#removereadstream) first or 783react to this event with a single listener and then dispatch from this 784listener. This method MAY throw an `Exception` if the given resource type 785is not supported by this loop implementation. 786 787The second parameter MUST be a listener callback function that accepts 788the stream resource as its only parameter. 789If you don't use the stream resource inside your listener callback function 790you MAY use a function which has no parameters at all. 791 792The listener callback function MUST NOT throw an `Exception`. 793The return value of the listener callback function will be ignored and has 794no effect, so for performance reasons you're recommended to not return 795any excessive data structures. 796 797If you want to access any variables within your callback function, you 798can 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 806See also [example #11](examples). 807 808You can invoke [`removeReadStream()`](#removereadstream) to remove the 809read event listener for this stream. 810 811The execution order of listeners when multiple streams become ready at 812the same time is not guaranteed. 813 814Some event loop implementations are known to only trigger the listener if 815the stream *becomes* readable (edge-triggered) and may not trigger if the 816stream has already been readable from the beginning. 817This also implies that a stream may not be recognized as readable when data 818is still left in PHP's internal stream buffers. 819As such, it's recommended to use `stream_set_read_buffer($stream, 0);` 820to 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 829The `addWriteStream(resource $stream, callable $callback): void` method can be used to 830register a listener to be notified when a stream is ready to write. 831 832The first parameter MUST be a valid stream resource that supports 833checking whether it is ready to write by this loop implementation. 834A single stream resource MUST NOT be added more than once. 835Instead, either call [`removeWriteStream()`](#removewritestream) first or 836react to this event with a single listener and then dispatch from this 837listener. This method MAY throw an `Exception` if the given resource type 838is not supported by this loop implementation. 839 840The second parameter MUST be a listener callback function that accepts 841the stream resource as its only parameter. 842If you don't use the stream resource inside your listener callback function 843you MAY use a function which has no parameters at all. 844 845The listener callback function MUST NOT throw an `Exception`. 846The return value of the listener callback function will be ignored and has 847no effect, so for performance reasons you're recommended to not return 848any excessive data structures. 849 850If you want to access any variables within your callback function, you 851can 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 859See also [example #12](examples). 860 861You can invoke [`removeWriteStream()`](#removewritestream) to remove the 862write event listener for this stream. 863 864The execution order of listeners when multiple streams become ready at 865the same time is not guaranteed. 866 867#### removeReadStream() 868 869The `removeReadStream(resource $stream): void` method can be used to 870remove the read event listener for the given stream. 871 872Removing a stream from the loop that has already been removed or trying 873to remove a stream that was never added or is invalid has no effect. 874 875#### removeWriteStream() 876 877The `removeWriteStream(resource $stream): void` method can be used to 878remove the write event listener for the given stream. 879 880Removing a stream from the loop that has already been removed or trying 881to remove a stream that was never added or is invalid has no effect. 882 883## Install 884 885The recommended way to install this library is [through Composer](https://getcomposer.org/). 886[New to Composer?](https://getcomposer.org/doc/00-intro.md) 887 888This project follows [SemVer](https://semver.org/). 889This will install the latest supported version: 890 891```bash 892composer require react/event-loop:^1.5 893``` 894 895See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. 896 897This project aims to run on any platform and thus does not require any PHP 898extensions and supports running on legacy PHP 5.3 through current PHP 8+ and 899HHVM. 900It's *highly recommended to use the latest supported PHP version* for this project. 901 902Installing any of the event loop extensions is suggested, but entirely optional. 903See also [event loop implementations](#loop-implementations) for more details. 904 905## Tests 906 907To run the test suite, you first need to clone this repo and then install all 908dependencies [through Composer](https://getcomposer.org/): 909 910```bash 911composer install 912``` 913 914To run the test suite, go to the project root and run: 915 916```bash 917vendor/bin/phpunit 918``` 919 920## License 921 922MIT, 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.