diff --git a/.travis.yml b/.travis.yml index b51dcb9b..7af713a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: php php: +# - 5.3 # requires old distro, see below - 5.4 - 5.5 - 5.6 @@ -13,6 +14,9 @@ php: dist: trusty matrix: + include: + - php: 5.3 + dist: precise allow_failures: - php: hhvm diff --git a/README.md b/README.md index 7d7e0f38..7a30ec15 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ A `stream_select()` based event loop. This uses the [`stream_select()`](http://php.net/manual/en/function.stream-select.php) function and is the only implementation which works out of the box with PHP. -This event loop works out of the box on PHP 5.4 through PHP 7+ and HHVM. +This event loop works out of the box on PHP 5.3 through PHP 7+ and HHVM. This means that no installation is required and this library works on all platforms and supported PHP versions. Accordingly, the [`Factory`](#factory) will use this event loop by default if @@ -574,7 +574,7 @@ $ composer require react/event-loop ``` This project aims to run on any platform and thus does not require any PHP -extensions and supports running on legacy PHP 5.4 through current PHP 7+ and +extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM. It's *highly recommended to use PHP 7+* for this project. diff --git a/composer.json b/composer.json index ad14cf4b..05afe65a 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "keywords": ["event-loop", "asynchronous"], "license": "MIT", "require": { - "php": ">=5.4.0" + "php": ">=5.3.0" }, "require-dev": { "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4" diff --git a/src/ExtEventLoop.php b/src/ExtEventLoop.php index 53bbc974..4146fc18 100644 --- a/src/ExtEventLoop.php +++ b/src/ExtEventLoop.php @@ -26,15 +26,15 @@ final class ExtEventLoop implements LoopInterface private $timerCallback; private $timerEvents; private $streamCallback; - private $readEvents = []; - private $writeEvents = []; - private $readListeners = []; - private $writeListeners = []; - private $readRefs = []; - private $writeRefs = []; + private $readEvents = array(); + private $writeEvents = array(); + private $readListeners = array(); + private $writeListeners = array(); + private $readRefs = array(); + private $writeRefs = array(); private $running; private $signals; - private $signalEvents = []; + private $signalEvents = array(); public function __construct(EventBaseConfig $config = null) { @@ -215,10 +215,11 @@ private function scheduleTimer(TimerInterface $timer) */ private function createTimerCallback() { - $this->timerCallback = function ($_, $__, $timer) { + $timers = $this->timerEvents; + $this->timerCallback = function ($_, $__, $timer) use ($timers) { call_user_func($timer->getCallback(), $timer); - if (!$timer->isPeriodic() && $this->timerEvents->contains($timer)) { + if (!$timer->isPeriodic() && $timers->contains($timer)) { $this->cancelTimer($timer); } }; @@ -233,15 +234,17 @@ private function createTimerCallback() */ private function createStreamCallback() { - $this->streamCallback = function ($stream, $flags) { + $read =& $this->readListeners; + $write =& $this->writeListeners; + $this->streamCallback = function ($stream, $flags) use (&$read, &$write) { $key = (int) $stream; - if (Event::READ === (Event::READ & $flags) && isset($this->readListeners[$key])) { - call_user_func($this->readListeners[$key], $stream); + if (Event::READ === (Event::READ & $flags) && isset($read[$key])) { + call_user_func($read[$key], $stream); } - if (Event::WRITE === (Event::WRITE & $flags) && isset($this->writeListeners[$key])) { - call_user_func($this->writeListeners[$key], $stream); + if (Event::WRITE === (Event::WRITE & $flags) && isset($write[$key])) { + call_user_func($write[$key], $stream); } }; } diff --git a/src/ExtLibevLoop.php b/src/ExtLibevLoop.php index 587bb044..9c679895 100644 --- a/src/ExtLibevLoop.php +++ b/src/ExtLibevLoop.php @@ -28,11 +28,11 @@ final class ExtLibevLoop implements LoopInterface private $loop; private $futureTickQueue; private $timerEvents; - private $readEvents = []; - private $writeEvents = []; + private $readEvents = array(); + private $writeEvents = array(); private $running; private $signals; - private $signalEvents = []; + private $signalEvents = array(); public function __construct() { @@ -100,11 +100,13 @@ public function addTimer($interval, $callback) { $timer = new Timer( $interval, $callback, false); - $callback = function () use ($timer) { + $that = $this; + $timers = $this->timerEvents; + $callback = function () use ($timer, $timers, $that) { call_user_func($timer->getCallback(), $timer); - if ($this->timerEvents->contains($timer)) { - $this->cancelTimer($timer); + if ($timers->contains($timer)) { + $that->cancelTimer($timer); } }; @@ -148,8 +150,9 @@ public function addSignal($signal, $listener) $this->signals->add($signal, $listener); if (!isset($this->signalEvents[$signal])) { - $this->signalEvents[$signal] = new SignalEvent(function () use ($signal) { - $this->signals->call($signal); + $signals = $this->signals; + $this->signalEvents[$signal] = new SignalEvent(function () use ($signals, $signal) { + $signals->call($signal); }, $signal); $this->loop->add($this->signalEvents[$signal]); } diff --git a/src/ExtLibeventLoop.php b/src/ExtLibeventLoop.php index da181a28..5bc265e2 100644 --- a/src/ExtLibeventLoop.php +++ b/src/ExtLibeventLoop.php @@ -42,13 +42,13 @@ final class ExtLibeventLoop implements LoopInterface private $timerCallback; private $timerEvents; private $streamCallback; - private $readEvents = []; - private $writeEvents = []; - private $readListeners = []; - private $writeListeners = []; + private $readEvents = array(); + private $writeEvents = array(); + private $readListeners = array(); + private $writeListeners = array(); private $running; private $signals; - private $signalEvents = []; + private $signalEvents = array(); public function __construct() { @@ -228,24 +228,26 @@ private function scheduleTimer(TimerInterface $timer) */ private function createTimerCallback() { - $this->timerCallback = function ($_, $__, $timer) { + $that = $this; + $timers = $this->timerEvents; + $this->timerCallback = function ($_, $__, $timer) use ($timers, $that) { call_user_func($timer->getCallback(), $timer); // Timer already cancelled ... - if (!$this->timerEvents->contains($timer)) { + if (!$timers->contains($timer)) { return; } // Reschedule periodic timers ... if ($timer->isPeriodic()) { event_add( - $this->timerEvents[$timer], - $timer->getInterval() * self::MICROSECONDS_PER_SECOND + $timers[$timer], + $timer->getInterval() * ExtLibeventLoop::MICROSECONDS_PER_SECOND ); // Clean-up one shot timers ... } else { - $this->cancelTimer($timer); + $that->cancelTimer($timer); } }; } @@ -259,15 +261,17 @@ private function createTimerCallback() */ private function createStreamCallback() { - $this->streamCallback = function ($stream, $flags) { + $read =& $this->readListeners; + $write =& $this->writeListeners; + $this->streamCallback = function ($stream, $flags) use (&$read, &$write) { $key = (int) $stream; - if (EV_READ === (EV_READ & $flags) && isset($this->readListeners[$key])) { - call_user_func($this->readListeners[$key], $stream); + if (EV_READ === (EV_READ & $flags) && isset($read[$key])) { + call_user_func($read[$key], $stream); } - if (EV_WRITE === (EV_WRITE & $flags) && isset($this->writeListeners[$key])) { - call_user_func($this->writeListeners[$key], $stream); + if (EV_WRITE === (EV_WRITE & $flags) && isset($write[$key])) { + call_user_func($write[$key], $stream); } }; } diff --git a/src/SignalsHandler.php b/src/SignalsHandler.php index 2614e03c..523e1ca1 100644 --- a/src/SignalsHandler.php +++ b/src/SignalsHandler.php @@ -7,12 +7,12 @@ */ final class SignalsHandler { - private $signals = []; + private $signals = array(); public function add($signal, $listener) { if (!isset($this->signals[$signal])) { - $this->signals[$signal] = []; + $this->signals[$signal] = array(); } if (in_array($listener, $this->signals[$signal])) { diff --git a/src/StreamSelectLoop.php b/src/StreamSelectLoop.php index 55bb1b8a..e82e9e47 100644 --- a/src/StreamSelectLoop.php +++ b/src/StreamSelectLoop.php @@ -56,10 +56,10 @@ final class StreamSelectLoop implements LoopInterface private $futureTickQueue; private $timers; - private $readStreams = []; - private $readListeners = []; - private $writeStreams = []; - private $writeListeners = []; + private $readStreams = array(); + private $readListeners = array(); + private $writeStreams = array(); + private $writeListeners = array(); private $running; private $pcntl = false; private $signals; diff --git a/tests/AbstractLoopTest.php b/tests/AbstractLoopTest.php index 6cac6eb1..2a30b48c 100644 --- a/tests/AbstractLoopTest.php +++ b/tests/AbstractLoopTest.php @@ -82,14 +82,16 @@ private function subAddReadStreamReceivesDataFromStreamReference() fwrite($input, 'hello'); fclose($input); - $this->loop->addReadStream($output, function ($output) { + $loop = $this->loop; + $received =& $this->received; + $loop->addReadStream($output, function ($output) use ($loop, &$received) { $chunk = fread($output, 1024); if ($chunk === '') { - $this->received .= 'X'; - $this->loop->removeReadStream($output); + $received .= 'X'; + $loop->removeReadStream($output); fclose($output); } else { - $this->received .= '[' . $chunk . ']'; + $received .= '[' . $chunk . ']'; } }); } @@ -194,9 +196,10 @@ public function testRemoveReadAndWriteStreamFromLoopOnceResourceClosesEndsLoop() $this->loop->addWriteStream($stream, function () { }); // remove stream when the stream is readable (closes) - $this->loop->addReadStream($stream, function ($stream) { - $this->loop->removeReadStream($stream); - $this->loop->removeWriteStream($stream); + $loop = $this->loop; + $loop->addReadStream($stream, function ($stream) use ($loop) { + $loop->removeReadStream($stream); + $loop->removeWriteStream($stream); fclose($stream); }); @@ -216,14 +219,15 @@ public function testRemoveReadAndWriteStreamFromLoopOnceResourceClosesOnEndOfFil $this->loop->addWriteStream($stream, function () { }); // remove stream when the stream is readable (closes) - $this->loop->addReadStream($stream, function ($stream) { + $loop = $this->loop; + $loop->addReadStream($stream, function ($stream) use ($loop) { $data = fread($stream, 1024); if ($data !== '') { return; } - $this->loop->removeReadStream($stream); - $this->loop->removeWriteStream($stream); + $loop->removeReadStream($stream); + $loop->removeWriteStream($stream); fclose($stream); }); @@ -246,9 +250,10 @@ public function testRemoveReadAndWriteStreamFromLoopWithClosingResourceEndsLoop( $this->loop->addWriteStream($stream, function () { }); // remove stream when the stream is readable (closes) - $this->loop->addReadStream($stream, function ($stream) { - $this->loop->removeReadStream($stream); - $this->loop->removeWriteStream($stream); + $loop = $this->loop; + $loop->addReadStream($stream, function ($stream) use ($loop) { + $loop->removeReadStream($stream); + $loop->removeWriteStream($stream); fclose($stream); }); @@ -304,16 +309,18 @@ public function stopShouldStopRunningLoop() public function testStopShouldPreventRunFromBlocking() { + $that = $this; $this->loop->addTimer( 1, - function () { - $this->fail('Timer was executed.'); + function () use ($that) { + $that->fail('Timer was executed.'); } ); + $loop = $this->loop; $this->loop->futureTick( - function () { - $this->loop->stop(); + function () use ($loop) { + $loop->stop(); } ); @@ -338,9 +345,10 @@ public function testIgnoreRemovedCallback() }); // this callback would have to be called as well, but the first stream already removed us - $loop->addReadStream($input2, function () use (& $called) { + $that = $this; + $loop->addReadStream($input2, function () use (& $called, $that) { if ($called) { - $this->fail('Callback 2 must not be called after callback 1 was called'); + $that->fail('Callback 2 must not be called after callback 1 was called'); } }); @@ -354,9 +362,10 @@ public function testIgnoreRemovedCallback() public function testFutureTickEventGeneratedByFutureTick() { + $loop = $this->loop; $this->loop->futureTick( - function () { - $this->loop->futureTick( + function () use ($loop) { + $loop->futureTick( function () { echo 'future-tick' . PHP_EOL; } @@ -412,18 +421,19 @@ public function testRecursiveFutureTick() { list ($stream) = $this->createSocketPair(); + $loop = $this->loop; $this->loop->addWriteStream( $stream, - function () use ($stream) { + function () use ($stream, $loop) { echo 'stream' . PHP_EOL; - $this->loop->removeWriteStream($stream); + $loop->removeWriteStream($stream); } ); $this->loop->futureTick( - function () { + function () use ($loop) { echo 'future-tick-1' . PHP_EOL; - $this->loop->futureTick( + $loop->futureTick( function () { echo 'future-tick-2' . PHP_EOL; } @@ -440,11 +450,12 @@ public function testRunWaitsForFutureTickEvents() { list ($stream) = $this->createSocketPair(); + $loop = $this->loop; $this->loop->addWriteStream( $stream, - function () use ($stream) { - $this->loop->removeWriteStream($stream); - $this->loop->futureTick( + function () use ($stream, $loop) { + $loop->removeWriteStream($stream); + $loop->futureTick( function () { echo 'future-tick' . PHP_EOL; } @@ -459,10 +470,11 @@ function () { public function testFutureTickEventGeneratedByTimer() { + $loop = $this->loop; $this->loop->addTimer( 0.001, - function () { - $this->loop->futureTick( + function () use ($loop) { + $loop->futureTick( function () { echo 'future-tick' . PHP_EOL; } @@ -496,11 +508,12 @@ public function testSignal() $calledShouldNot = false; }); - $this->loop->addSignal(SIGUSR1, $func1 = function () use (&$func1, &$func2, &$called, $timer) { + $loop = $this->loop; + $this->loop->addSignal(SIGUSR1, $func1 = function () use (&$func1, &$func2, &$called, $timer, $loop) { $called = true; - $this->loop->removeSignal(SIGUSR1, $func1); - $this->loop->removeSignal(SIGUSR2, $func2); - $this->loop->cancelTimer($timer); + $loop->removeSignal(SIGUSR1, $func1); + $loop->removeSignal(SIGUSR2, $func2); + $loop->cancelTimer($timer); }); $this->loop->futureTick(function () { @@ -527,8 +540,9 @@ public function testSignalMultipleUsagesForTheSameListener() $this->loop->addTimer(0.4, function () { posix_kill(posix_getpid(), SIGUSR1); }); - $this->loop->addTimer(0.9, function () use (&$func) { - $this->loop->removeSignal(SIGUSR1, $func); + $loop = $this->loop; + $this->loop->addTimer(0.9, function () use (&$func, $loop) { + $loop->removeSignal(SIGUSR1, $func); }); $this->loop->run(); @@ -538,11 +552,12 @@ public function testSignalMultipleUsagesForTheSameListener() public function testSignalsKeepTheLoopRunning() { + $loop = $this->loop; $function = function () {}; $this->loop->addSignal(SIGUSR1, $function); - $this->loop->addTimer(1.5, function () use ($function) { - $this->loop->removeSignal(SIGUSR1, $function); - $this->loop->stop(); + $this->loop->addTimer(1.5, function () use ($function, $loop) { + $loop->removeSignal(SIGUSR1, $function); + $loop->stop(); }); $this->assertRunSlowerThan(1.5); @@ -550,10 +565,11 @@ public function testSignalsKeepTheLoopRunning() public function testSignalsKeepTheLoopRunningAndRemovingItStopsTheLoop() { + $loop = $this->loop; $function = function () {}; $this->loop->addSignal(SIGUSR1, $function); - $this->loop->addTimer(1.5, function () use ($function) { - $this->loop->removeSignal(SIGUSR1, $function); + $this->loop->addTimer(1.5, function () use ($function, $loop) { + $loop->removeSignal(SIGUSR1, $function); }); $this->assertRunFasterThan(1.6); @@ -568,9 +584,10 @@ public function testTimerIntervalCanBeFarInFuture() $timer = $this->loop->addTimer(PHP_INT_MAX, function () { }); // remove stream and timer when the stream is readable (closes) - $this->loop->addReadStream($stream, function ($stream) use ($timer) { - $this->loop->removeReadStream($stream); - $this->loop->cancelTimer($timer); + $loop = $this->loop; + $this->loop->addReadStream($stream, function ($stream) use ($timer, $loop) { + $loop->removeReadStream($stream); + $loop->cancelTimer($timer); }); $this->assertRunFasterThan($this->tickTimeout); diff --git a/tests/StreamSelectLoopTest.php b/tests/StreamSelectLoopTest.php index 87bbbe69..bd19e1cd 100644 --- a/tests/StreamSelectLoopTest.php +++ b/tests/StreamSelectLoopTest.php @@ -39,11 +39,11 @@ public function testStreamSelectTimeoutEmulation() public function signalProvider() { - return [ - ['SIGUSR1'], - ['SIGHUP'], - ['SIGTERM'], - ]; + return array( + array('SIGUSR1'), + array('SIGHUP'), + array('SIGTERM'), + ); } /** @@ -60,8 +60,9 @@ public function testSignalInterruptNoStream($signal) $check = $this->loop->addPeriodicTimer(0.01, function() { pcntl_signal_dispatch(); }); - $this->loop->addTimer(0.1, function () use ($check) { - $this->loop->cancelTimer($check); + $loop = $this->loop; + $loop->addTimer(0.1, function () use ($check, $loop) { + $loop->cancelTimer($check); }); $handled = false; @@ -92,12 +93,13 @@ public function testSignalInterruptWithStream($signal) }); // add stream to the loop + $loop = $this->loop; list($writeStream, $readStream) = $this->createSocketPair(); - $this->loop->addReadStream($readStream, function ($stream) { + $loop->addReadStream($readStream, function ($stream) use ($loop) { /** @var $loop LoopInterface */ $read = fgets($stream); if ($read === "end loop\n") { - $this->loop->stop(); + $loop->stop(); } }); $this->loop->addTimer(0.1, function() use ($writeStream) { diff --git a/travis-init.sh b/travis-init.sh index c8c8b55d..06f853fe 100755 --- a/travis-init.sh +++ b/travis-init.sh @@ -6,7 +6,9 @@ if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then # install 'event' PHP extension - echo "yes" | pecl install event + if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]]; then + echo "yes" | pecl install event + fi # install 'libevent' PHP extension (does not support php 7) if [[ "$TRAVIS_PHP_VERSION" != "7.0" &&