Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

ReactPHP's core reactor event loop that libraries can use for evented I/O.

License

NotificationsYou must be signed in to change notification settings

reactphp/event-loop

Repository files navigation

CI statusinstalls on Packagist

ReactPHP's core reactor event loop that libraries can use for evented I/O.

Development version: This branch contains the code for the upcoming v3release. For the code of the current stable v1 release, check out the1.x branch.

The upcoming v3 release will be the way forward for this package. However,we will still actively support v1 for those not yet on the latest version.See alsoinstallation instructions for more details.

In order for async based libraries to be interoperable, they need to use thesame event loop. This component provides a commonLoopInterface that anylibrary can target. This allows them to be used in the same loop, with onesinglerun() call that is controlled by the user.

Table of contents

Quickstart example

Here is an async HTTP server built with just the event loop.

<?phpuseReact\EventLoop\Loop;require__DIR__ .'/vendor/autoload.php';$server =stream_socket_server('tcp://127.0.0.1:8080');stream_set_blocking($server,false);Loop::addReadStream($server,function ($server) {$conn =stream_socket_accept($server);$data ="HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";    Loop::addWriteStream($conn,function ($conn)use (&$data) {$written =fwrite($conn,$data);if ($written ===strlen($data)) {fclose($conn);            Loop::removeWriteStream($conn);        }else {$data =substr($data,$written);        }    });});Loop::addPeriodicTimer(5,function () {$memory =memory_get_usage() /1024;$formatted =number_format($memory,3).'K';echo"Current memory usage:{$formatted}\n";});

See also theexamples.

Usage

Typical applications would use theLoop class to use the defaultevent loop like this:

useReact\EventLoop\Loop;$timer = Loop::addPeriodicTimer(0.1,function () {echo'Tick' .PHP_EOL;});Loop::addTimer(1.0,function ()use ($timer) {    Loop::cancelTimer($timer);echo'Done' .PHP_EOL;});

As an alternative, you can also explicitly create an event loop instance at thebeginning, reuse it throughout your program and finally run it at the end of theprogram like this:

$loop =React\EventLoop\Loop::get();$timer =$loop->addPeriodicTimer(0.1,function () {echo'Tick' .PHP_EOL;});$loop->addTimer(1.0,function ()use ($loop,$timer) {$loop->cancelTimer($timer);echo'Done' .PHP_EOL;});$loop->run();

While the former is more concise, the latter is more explicit.In both cases, the program would perform the exact same steps.

  1. The event loop instance is created at the beginning of the program. This isimplicitly done the first time you call theLoop class(or by manually instantiating any of theloop implementations).
  2. The event loop is used directly or passed as an instance to library andapplication code. In this example, a periodic timer is registered with theevent loop which simply outputsTick every fraction of a second until anothertimer stops the periodic timer after a second.
  3. The event loop is run at the end of the program. This is automatically donewhen using theLoop class or explicitly with a singlerun()call at the end of the program.

As ofv1.2.0, we highly recommend using theLoop class.The explicit loop instructions are still valid and may still be useful in someapplications, especially for a transition period towards the more concise style.

Loop

TheLoop class exists as a convenient global accessor for the event loop.

Loop methods

TheLoop class provides all methods that exist on theLoopInterfaceas static methods:

If you're working with the event loop in your application code, it's ofteneasiest to directly interface with the static methods defined on theLoop classlike this:

useReact\EventLoop\Loop;$timer = Loop::addPeriodicTimer(0.1,function () {echo'Tick' .PHP_EOL;});Loop::addTimer(1.0,function ()use ($timer) {    Loop::cancelTimer($timer);echo'Done' .PHP_EOL;});

On the other hand, if you're familiar with object-oriented programming (OOP) anddependency injection (DI), you may want to inject an event loop instance andinvoke instance methods on theLoopInterface like this:

useReact\EventLoop\Loop;useReact\EventLoop\LoopInterface;class Greeter{private$loop;publicfunction__construct(LoopInterface$loop)    {$this->loop =$loop;    }publicfunctiongreet(string$name)    {$this->loop->addTimer(1.0,function ()use ($name) {echo'Hello' .$name .'!' .PHP_EOL;        });    }}$greeter =newGreeter(Loop::get());$greeter->greet('Alice');$greeter->greet('Bob');

Each static method call will be forwarded as-is to the underlying event loopinstance by using theLoop::get() call internally.SeeLoopInterface for more details about available methods.

Loop autorun

When using theLoop class, it will automatically execute the loop at the end ofthe program. This means the following example will schedule a timer and willautomatically execute the program until the timer event fires:

useReact\EventLoop\Loop;Loop::addTimer(1.0,function () {echo'Hello' .PHP_EOL;});

As ofv1.2.0, we highly recommend using theLoop class this way and omitting anyexplicitrun() calls. For BC reasons, the explicitrun()method is still valid and may still be useful in some applications, especiallyfor a transition period towards the more concise style.

If you don't want theLoop to run automatically, you can either explicitlyrun() orstop() it. This can be useful if you're usinga global exception handler like this:

useReact\EventLoop\Loop;Loop::addTimer(10.0,function () {echo'Never happens';});set_exception_handler(function (Throwable$e) {echo'Error:' .$e->getMessage() .PHP_EOL;    Loop::stop();});thrownewRuntimeException('Demo');

get()

Theget(): LoopInterface method can be used toget the currently active event loop instance.

This method will always return the same event loop instance throughout thelifetime of your application.

useReact\EventLoop\Loop;useReact\EventLoop\LoopInterface;$loop = Loop::get();assert($loopinstanceof LoopInterface);assert($loop === Loop::get());

This is particularly useful if you're using object-oriented programming (OOP)and dependency injection (DI). In this case, you may want to inject an eventloop instance and invoke instance methods on theLoopInterface like this:

useReact\EventLoop\Loop;useReact\EventLoop\LoopInterface;class Greeter{private$loop;publicfunction__construct(LoopInterface$loop)    {$this->loop =$loop;    }publicfunctiongreet(string$name)    {$this->loop->addTimer(1.0,function ()use ($name) {echo'Hello' .$name .'!' .PHP_EOL;        });    }}$greeter =newGreeter(Loop::get());$greeter->greet('Alice');$greeter->greet('Bob');

SeeLoopInterface for more details about available methods.

Loop implementations

In addition to theLoopInterface, there are a number ofevent loop implementations provided.

All of the event loops support these features:

  • File descriptor polling
  • One-off timers
  • Periodic timers
  • Deferred execution on future loop tick

For most consumers of this package, the underlying event loop implementation isan implementation detail.You should use theLoop class to automatically create a new instance.

Advanced! If you explicitly need a certain event loop implementation, you canmanually instantiate one of the following classes.Note that you may have to install the required PHP extensions for the respectiveevent loop implementation first or they will throw aBadMethodCallException on creation.

StreamSelectLoop

Astream_select() based event loop.

This uses thestream_select()function and is the only implementation that works out of the box with PHP.

This event loop works out of the box on any PHP version.This means that no installation is required and this library works on allplatforms and supported PHP versions.Accordingly, theLoop class will use this event loop by default ifyou do not install any of the event loop extensions listed below.

Under the hood, it does a simpleselect system call.This system call is limited to the maximum file descriptor number ofFD_SETSIZE (platform dependent, commonly 1024) and scales withO(m)(m being the maximum file descriptor number passed).This means that you may run into issues when handling thousands of streamsconcurrently and you may want to look into using one of the alternativeevent loop implementations listed below in this case.If your use case is among the many common use cases that involve handling onlydozens or a few hundred streams at once, then this event loop implementationperforms really well.

If you want to use signal handling (see alsoaddSignal() below),this event loop implementation requiresext-pcntl.This extension is only available for Unix-like platforms and does not supportWindows.It is commonly installed as part of many PHP distributions.If this extension is missing (or you're running on Windows), signal handling isnot supported and throws aBadMethodCallException instead.

This event loop is known to rely on wall-clock time to schedule future timerswhen using any version before PHP 7.3, because a monotonic time source isonly available as of PHP 7.3 (hrtime()).While this does not affect many common use cases, this is an importantdistinction for programs that rely on a high time precision or on systemsthat are subject to discontinuous time adjustments (time jumps).This means that if you schedule a timer to trigger in 30s on PHP < 7.3 andthen adjust your system time forward by 20s, the timer may trigger in 10s.See alsoaddTimer() for more details.

ExtEventLoop

Anext-event based event loop.

This uses theevent PECL extension,that provides an interface tolibevent library.libevent itself supports a number of system-specific backends (epoll, kqueue).

This loop is known to work with PHP 7.1 through PHP 8+.

ExtEvLoop

Anext-ev based event loop.

This loop uses theev PECL extension,that provides an interface tolibev library.libev itself supports a number of system-specific backends (epoll, kqueue).

This loop is known to work with PHP 7.1 through PHP 8+.

ExtUvLoop

Anext-uv based event loop.

This loop uses theuv PECL extension,that provides an interface tolibuv library.libuv itself supports a number of system-specific backends (epoll, kqueue).

This loop is known to work with PHP 7.1 through PHP 8+.

LoopInterface

run()

Therun(): void method can be used torun the event loop until there are no more tasks to perform.

For many applications, this method is the only directly visibleinvocation on the event loop.As a rule of thumb, it is usually recommended to attach everything to thesame loop instance and then run the loop once at the bottom end of theapplication.

$loop->run();

This method will keep the loop running until there are no more tasksto perform. In other words: This method will block until the lasttimer, stream and/or signal has been removed.

Likewise, it is imperative to ensure the application actually invokesthis method once. Adding listeners to the loop and missing to actuallyrun it will result in the application exiting without actually waitingfor any of the attached listeners.

This method MUST NOT be called while the loop is already running.This method MAY be called more than once after it has explicitly beenstop()ped or after it automatically stopped because itpreviously did no longer have anything to do.

stop()

Thestop(): void method can be used toinstruct a running event loop to stop.

This method is considered advanced usage and should be used with care.As a rule of thumb, it is usually recommended to let the loop stoponly automatically when it no longer has anything to do.

This method can be used to explicitly instruct the event loop to stop:

$loop->addTimer(3.0,function ()use ($loop) {$loop->stop();});

Calling this method on a loop instance that is not currently running oron a loop instance that has already been stopped has no effect.

addTimer()

TheaddTimer(float $interval, callable $callback): TimerInterface method can be used toenqueue a callback to be invoked once after the given interval.

The second parameter MUST be a timer callback function that acceptsthe timer instance as its only parameter.If you don't use the timer instance inside your timer callback functionyou MAY use a function which has no parameters at all.

The timer callback function MUST NOT throw anException.The return value of the timer callback function will be ignored and hasno effect, so for performance reasons you're recommended to not returnany excessive data structures.

This method returns a timer instance. The same timer instance will also bepassed into the timer callback function as described above.You can invokecancelTimer to cancel a pending timer.UnlikeaddPeriodicTimer(), this method will ensurethe callback will be invoked only once after the given interval.

$loop->addTimer(0.8,function () {echo'world!' .PHP_EOL;});$loop->addTimer(0.3,function () {echo'hello';});

See alsoexample #1.

If you want to access any variables within your callback function, youcan bind arbitrary data to a callback closure like this:

functionhello($name,LoopInterface$loop){$loop->addTimer(1.0,function ()use ($name) {echo"hello$name\n";    });}hello('Tester',$loop);

This interface does not enforce any particular timer resolution, sospecial care may have to be taken if you rely on very high precision withmillisecond accuracy or below. Event loop implementations SHOULD work ona best effort basis and SHOULD provide at least millisecond accuracyunless otherwise noted. Many existing event loop implementations areknown to provide microsecond accuracy, but it's generally not recommendedto rely on this high precision.

Similarly, the execution order of timers scheduled to execute at thesame time (within its possible accuracy) is not guaranteed.

This interface suggests that event loop implementations SHOULD use amonotonic time source if available. Given that a monotonic time source isonly available as of PHP 7.3 by default, event loop implementations MAYfall back to using wall-clock time.While this does not affect many common use cases, this is an importantdistinction for programs that rely on a high time precision or on systemsthat are subject to discontinuous time adjustments (time jumps).This means that if you schedule a timer to trigger in 30s and then adjustyour system time forward by 20s, the timer SHOULD still trigger in 30s.See alsoevent loop implementations for more details.

addPeriodicTimer()

TheaddPeriodicTimer(float $interval, callable $callback): TimerInterface method can be used toenqueue a callback to be invoked repeatedly after the given interval.

The second parameter MUST be a timer callback function that acceptsthe timer instance as its only parameter.If you don't use the timer instance inside your timer callback functionyou MAY use a function which has no parameters at all.

The timer callback function MUST NOT throw anException.The return value of the timer callback function will be ignored and hasno effect, so for performance reasons you're recommended to not returnany excessive data structures.

This method returns a timer instance. The same timer instance will also bepassed into the timer callback function as described above.UnlikeaddTimer(), this method will ensure the callbackwill be invoked infinitely after the given interval or until you invokecancelTimer.

$timer =$loop->addPeriodicTimer(0.1,function () {echo'tick!' .PHP_EOL;});$loop->addTimer(1.0,function ()use ($loop,$timer) {$loop->cancelTimer($timer);echo'Done' .PHP_EOL;});

See alsoexample #2.

If you want to limit the number of executions, you can bindarbitrary data to a callback closure like this:

functionhello($name,LoopInterface$loop){$n =3;$loop->addPeriodicTimer(1.0,function ($timer)use ($name,$loop, &$n) {if ($n >0) {            --$n;echo"hello$name\n";        }else {$loop->cancelTimer($timer);        }    });}hello('Tester',$loop);

This interface does not enforce any particular timer resolution, sospecial care may have to be taken if you rely on very high precision withmillisecond accuracy or below. Event loop implementations SHOULD work ona best effort basis and SHOULD provide at least millisecond accuracyunless otherwise noted. Many existing event loop implementations areknown to provide microsecond accuracy, but it's generally not recommendedto rely on this high precision.

Similarly, the execution order of timers scheduled to execute at thesame time (within its possible accuracy) is not guaranteed.

This interface suggests that event loop implementations SHOULD use amonotonic time source if available. Given that a monotonic time source isonly available as of PHP 7.3 by default, event loop implementations MAYfall back to using wall-clock time.While this does not affect many common use cases, this is an importantdistinction for programs that rely on a high time precision or on systemsthat are subject to discontinuous time adjustments (time jumps).This means that if you schedule a timer to trigger in 30s and then adjustyour system time forward by 20s, the timer SHOULD still trigger in 30s.See alsoevent loop implementations for more details.

Additionally, periodic timers may be subject to timer drift due tore-scheduling after each invocation. As such, it's generally notrecommended to rely on this for high precision intervals with millisecondaccuracy or below.

cancelTimer()

ThecancelTimer(TimerInterface $timer): void method can be used tocancel a pending timer.

See alsoaddPeriodicTimer() andexample #2.

Calling this method on a timer instance that has not been added to thisloop instance or on a timer that has already been cancelled has no effect.

futureTick()

ThefutureTick(callable $listener): void method can be used toschedule a callback to be invoked on a future tick of the event loop.

This works very much similar to timers with an interval of zero seconds,but does not require the overhead of scheduling a timer queue.

The tick callback function MUST be able to accept zero parameters.

The tick callback function MUST NOT throw anException.The return value of the tick callback function will be ignored and hasno effect, so for performance reasons you're recommended to not returnany excessive data structures.

If you want to access any variables within your callback function, youcan bind arbitrary data to a callback closure like this:

functionhello($name,LoopInterface$loop){$loop->futureTick(function ()use ($name) {echo"hello$name\n";    });}hello('Tester',$loop);

Unlike timers, tick callbacks are guaranteed to be executed in the orderthey are enqueued.Also, once a callback is enqueued, there's no way to cancel this operation.

This is often used to break down bigger tasks into smaller steps (a formof cooperative multitasking).

$loop->futureTick(function () {echo'b';});$loop->futureTick(function () {echo'c';});echo'a';

See alsoexample #3.

addSignal()

TheaddSignal(int $signal, callable $listener): void method can be used toregister a listener to be notified when a signal has been caught by this process.

This is useful to catch user interrupt signals or shutdown signals fromtools likesupervisor orsystemd.

The second parameter MUST be a listener callback function that acceptsthe signal as its only parameter.If you don't use the signal inside your listener callback functionyou MAY use a function which has no parameters at all.

The listener callback function MUST NOT throw anException.The return value of the listener callback function will be ignored and hasno effect, so for performance reasons you're recommended to not returnany excessive data structures.

$loop->addSignal(SIGINT,function (int$signal) {echo'Caught user interrupt signal' .PHP_EOL;});

See alsoexample #4.

Signaling is only available on Unix-like platforms, Windows isn'tsupported due to operating system limitations.This method may throw aBadMethodCallException if signals aren'tsupported on this platform, for example when required extensions aremissing.

Note: A listener can only be added once to the same signal, anyattempts to add it more than once will be ignored.

removeSignal()

TheremoveSignal(int $signal, callable $listener): void method can be used toremove a previously added signal listener.

$loop->removeSignal(SIGINT,$listener);

Any attempts to remove listeners that aren't registered will be ignored.

addReadStream()

Advanced! Note that this low-level API is considered advanced usage.Most use cases should probably use the higher-levelreadable Stream APIinstead.

TheaddReadStream(resource $stream, callable $callback): void method can be used toregister a listener to be notified when a stream is ready to read.

The first parameter MUST be a valid stream resource that supportschecking whether it is ready to read by this loop implementation.A single stream resource MUST NOT be added more than once.Instead, either callremoveReadStream() first orreact to this event with a single listener and then dispatch from thislistener. This method MAY throw anException if the given resource typeis not supported by this loop implementation.

The second parameter MUST be a listener callback function that acceptsthe stream resource as its only parameter.If you don't use the stream resource inside your listener callback functionyou MAY use a function which has no parameters at all.

The listener callback function MUST NOT throw anException.The return value of the listener callback function will be ignored and hasno effect, so for performance reasons you're recommended to not returnany excessive data structures.

If you want to access any variables within your callback function, youcan bind arbitrary data to a callback closure like this:

$loop->addReadStream($stream,function ($stream)use ($name) {echo$name .' said:' .fread($stream);});

See alsoexample #11.

You can invokeremoveReadStream() to remove theread event listener for this stream.

The execution order of listeners when multiple streams become ready atthe same time is not guaranteed.

Some event loop implementations are known to only trigger the listener ifthe streambecomes readable (edge-triggered) and may not trigger if thestream has already been readable from the beginning.This also implies that a stream may not be recognized as readable when datais still left in PHP's internal stream buffers.As such, it's recommended to usestream_set_read_buffer($stream, 0);to disable PHP's internal read buffer in this case.

addWriteStream()

Advanced! Note that this low-level API is considered advanced usage.Most use cases should probably use the higher-levelwritable Stream APIinstead.

TheaddWriteStream(resource $stream, callable $callback): void method can be used toregister a listener to be notified when a stream is ready to write.

The first parameter MUST be a valid stream resource that supportschecking whether it is ready to write by this loop implementation.A single stream resource MUST NOT be added more than once.Instead, either callremoveWriteStream() first orreact to this event with a single listener and then dispatch from thislistener. This method MAY throw anException if the given resource typeis not supported by this loop implementation.

The second parameter MUST be a listener callback function that acceptsthe stream resource as its only parameter.If you don't use the stream resource inside your listener callback functionyou MAY use a function which has no parameters at all.

The listener callback function MUST NOT throw anException.The return value of the listener callback function will be ignored and hasno effect, so for performance reasons you're recommended to not returnany excessive data structures.

If you want to access any variables within your callback function, youcan bind arbitrary data to a callback closure like this:

$loop->addWriteStream($stream,function ($stream)use ($name) {fwrite($stream,'Hello' .$name);});

See alsoexample #12.

You can invokeremoveWriteStream() to remove thewrite event listener for this stream.

The execution order of listeners when multiple streams become ready atthe same time is not guaranteed.

removeReadStream()

TheremoveReadStream(resource $stream): void method can be used toremove the read event listener for the given stream.

Removing a stream from the loop that has already been removed or tryingto remove a stream that was never added or is invalid has no effect.

removeWriteStream()

TheremoveWriteStream(resource $stream): void method can be used toremove the write event listener for the given stream.

Removing a stream from the loop that has already been removed or tryingto remove a stream that was never added or is invalid has no effect.

Install

The recommended way to install this library isthrough Composer.New to Composer?

Once released, this project will followSemVer.At the moment, this will install the latest development version:

composer require react/event-loop:^3@dev

See also theCHANGELOG for details about version upgrades.

This project aims to run on any platform and thus does not require any PHPextensions and supports running on PHP 7.1 through current PHP 8+.It'shighly recommended to use the latest supported PHP version for this project.

Installing any of the event loop extensions is suggested, but entirely optional.See alsoevent loop implementations for more details.

Tests

To run the test suite, you first need to clone this repo and then install alldependenciesthrough Composer:

composer install

To run the test suite, go to the project root and run:

vendor/bin/phpunit

License

MIT, seeLICENSE file.

More

About

ReactPHP's core reactor event loop that libraries can use for evented I/O.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

  •  
  •  
  •  

Languages


[8]ページ先頭

©2009-2025 Movatter.jp