friendship ended with social-app. php is my new best friend
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
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.