From cbc17ca6443b50359df89bc8f7d1c33d99f41a60 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sat, 8 Nov 2025 23:31:02 +0100 Subject: [PATCH 1/4] [1.x] Run CI jobs on PHP8.5 --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc858562..fa78b8cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ jobs: strategy: matrix: php: + - 8.5 - 8.4 - 8.3 - 8.2 @@ -49,6 +50,7 @@ jobs: strategy: matrix: php: + - 8.5 - 8.4 - 8.3 - 8.2 @@ -113,6 +115,7 @@ jobs: strategy: matrix: php: + - 8.5 - 8.4 - 8.3 - 8.2 From aa908f9bfca47b812b5f403543f0aa8f6db35f75 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sat, 8 Nov 2025 23:35:01 +0100 Subject: [PATCH 2/4] [1.x] Fix ext-uv int into float overflow logic for PHP8.5 This code introduced in #196 relied on behavior that has been changed in PHP8.5 by: https://wiki.php.net/rfc/warnings-php-8-5#casting_out_of_range_floats_to_int --- src/ExtUvLoop.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ExtUvLoop.php b/src/ExtUvLoop.php index 4434720d..1938f357 100644 --- a/src/ExtUvLoop.php +++ b/src/ExtUvLoop.php @@ -329,9 +329,17 @@ private function convertFloatSecondsToMilliseconds($interval) } $maxValue = (int) (\PHP_INT_MAX / 1000); - $intInterval = (int) $interval; + $intervalOverflow = false; + if (PHP_VERSION_ID > 80499 && $interval >= \PHP_INT_MAX + 1) { + $intervalOverflow = true; + } else { + $intInterval = (int) $interval; + if (($intInterval <= 0 && $interval > 1) || $intInterval >= $maxValue) { + $intervalOverflow = true; + } + } - if (($intInterval <= 0 && $interval > 1) || $intInterval >= $maxValue) { + if ($intervalOverflow) { throw new \InvalidArgumentException( "Interval overflow, value must be lower than '{$maxValue}', but '{$interval}' passed." ); From d23802238e357114b47ae4b1e635119a57a38015 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 10 Nov 2025 15:57:45 +0100 Subject: [PATCH 3/4] [1.x] Fix reflection setAccessible deprecation warnings In PHP8.1 ReflectionProperty::setAccessible was made a no-op through https://wiki.php.net/rfc/make-reflection-setaccessible-no-op in PHP8.5 it is now throwing a deprecation warning by: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_reflectionsetaccessible --- tests/LoopTest.php | 52 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/tests/LoopTest.php b/tests/LoopTest.php index 42f85244..25b6e855 100644 --- a/tests/LoopTest.php +++ b/tests/LoopTest.php @@ -64,7 +64,9 @@ public function testStaticAddReadStreamCallsAddReadStreamOnLoopInstance() public function testStaticAddReadStreamWithNoDefaultLoopCallsAddReadStreamOnNewLoopInstance() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $stream = stream_socket_server('127.0.0.1:0'); @@ -90,7 +92,9 @@ public function testStaticAddWriteStreamCallsAddWriteStreamOnLoopInstance() public function testStaticAddWriteStreamWithNoDefaultLoopCallsAddWriteStreamOnNewLoopInstance() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $stream = stream_socket_server('127.0.0.1:0'); @@ -115,7 +119,9 @@ public function testStaticRemoveReadStreamCallsRemoveReadStreamOnLoopInstance() public function testStaticRemoveReadStreamWithNoDefaultLoopIsNoOp() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $stream = tmpfile(); @@ -139,7 +145,9 @@ public function testStaticRemoveWriteStreamCallsRemoveWriteStreamOnLoopInstance( public function testStaticRemoveWriteStreamWithNoDefaultLoopIsNoOp() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $stream = tmpfile(); @@ -167,7 +175,9 @@ public function testStaticAddTimerCallsAddTimerOnLoopInstanceAndReturnsTimerInst public function testStaticAddTimerWithNoDefaultLoopCallsAddTimerOnNewLoopInstance() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $interval = 1.0; @@ -197,7 +207,9 @@ public function testStaticAddPeriodicTimerCallsAddPeriodicTimerOnLoopInstanceAnd public function testStaticAddPeriodicTimerWithNoDefaultLoopCallsAddPeriodicTimerOnNewLoopInstance() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $interval = 1.0; @@ -224,7 +236,9 @@ public function testStaticCancelTimerCallsCancelTimerOnLoopInstance() public function testStaticCancelTimerWithNoDefaultLoopIsNoOp() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock(); @@ -248,7 +262,9 @@ public function testStaticFutureTickCallsFutureTickOnLoopInstance() public function testStaticFutureTickWithNoDefaultLoopCallsFutureTickOnNewLoopInstance() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $listener = function () { }; @@ -277,7 +293,9 @@ public function testStaticAddSignalWithNoDefaultLoopCallsAddSignalOnNewLoopInsta } $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $signal = 1; @@ -307,7 +325,9 @@ public function testStaticRemoveSignalCallsRemoveSignalOnLoopInstance() public function testStaticRemoveSignalWithNoDefaultLoopIsNoOp() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); $signal = 1; @@ -330,7 +350,9 @@ public function testStaticRunCallsRunOnLoopInstance() public function testStaticRunWithNoDefaultLoopCallsRunsOnNewLoopInstance() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); Loop::run(); @@ -351,7 +373,9 @@ public function testStaticStopCallsStopOnLoopInstance() public function testStaticStopCallWithNoDefaultLoopIsNoOp() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); Loop::stop(); @@ -366,7 +390,9 @@ public function testStaticStopCallWithNoDefaultLoopIsNoOp() public function unsetLoopFromLoopAccessor() { $ref = new \ReflectionProperty('React\EventLoop\Loop', 'instance'); - $ref->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $ref->setAccessible(true); + } $ref->setValue(null, null); } } From 81bd56309aa198aff3a92d717032474fcc98f3d4 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 10 Nov 2025 15:58:21 +0100 Subject: [PATCH 4/4] [1.x] Fix SPL SplObjectStorage deprecations In PHP8.5 the SplObjectStorage::contains, SplObjectStorage::attach, and SplObjectStorage::detach methods have been deprecated through https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_splobjectstoragecontains_splobjectstorageattach_and_splobjectstoragedetach --- src/ExtEvLoop.php | 8 ++++---- src/ExtEventLoop.php | 8 ++++---- src/ExtUvLoop.php | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ExtEvLoop.php b/src/ExtEvLoop.php index a3fcec68..7689ff57 100644 --- a/src/ExtEvLoop.php +++ b/src/ExtEvLoop.php @@ -143,13 +143,13 @@ public function addTimer($interval, $callback) $callback = function () use ($timer, $timers, $that) { \call_user_func($timer->getCallback(), $timer); - if ($timers->contains($timer)) { + if ($timers->offsetExists($timer)) { $that->cancelTimer($timer); } }; $event = $this->loop->timer($timer->getInterval(), 0.0, $callback); - $this->timers->attach($timer, $event); + $this->timers->offsetSet($timer, $event); return $timer; } @@ -163,7 +163,7 @@ public function addPeriodicTimer($interval, $callback) }; $event = $this->loop->timer($timer->getInterval(), $timer->getInterval(), $callback); - $this->timers->attach($timer, $event); + $this->timers->offsetSet($timer, $event); return $timer; } @@ -176,7 +176,7 @@ public function cancelTimer(TimerInterface $timer) $event = $this->timers[$timer]; $event->stop(); - $this->timers->detach($timer); + $this->timers->offsetUnset($timer); } public function futureTick($listener) diff --git a/src/ExtEventLoop.php b/src/ExtEventLoop.php index b162a402..2f26d746 100644 --- a/src/ExtEventLoop.php +++ b/src/ExtEventLoop.php @@ -64,7 +64,7 @@ public function __destruct() { // explicitly clear all references to Event objects to prevent SEGFAULTs on Windows foreach ($this->timerEvents as $timer) { - $this->timerEvents->detach($timer); + $this->timerEvents->offsetUnset($timer); } $this->readEvents = array(); @@ -157,9 +157,9 @@ public function addPeriodicTimer($interval, $callback) public function cancelTimer(TimerInterface $timer) { - if ($this->timerEvents->contains($timer)) { + if ($this->timerEvents->offsetExists($timer)) { $this->timerEvents[$timer]->free(); - $this->timerEvents->detach($timer); + $this->timerEvents->offsetUnset($timer); } } @@ -243,7 +243,7 @@ private function createTimerCallback() $this->timerCallback = function ($_, $__, $timer) use ($timers) { \call_user_func($timer->getCallback(), $timer); - if (!$timer->isPeriodic() && $timers->contains($timer)) { + if (!$timer->isPeriodic() && $timers->offsetExists($timer)) { $this->cancelTimer($timer); } }; diff --git a/src/ExtUvLoop.php b/src/ExtUvLoop.php index 1938f357..29aa86b4 100644 --- a/src/ExtUvLoop.php +++ b/src/ExtUvLoop.php @@ -119,13 +119,13 @@ public function addTimer($interval, $callback) $callback = function () use ($timer, $timers, $that) { \call_user_func($timer->getCallback(), $timer); - if ($timers->contains($timer)) { + if ($timers->offsetExists($timer)) { $that->cancelTimer($timer); } }; $event = \uv_timer_init($this->uv); - $this->timers->attach($timer, $event); + $this->timers->offsetSet($timer, $event); \uv_timer_start( $event, $this->convertFloatSecondsToMilliseconds($interval), @@ -149,7 +149,7 @@ public function addPeriodicTimer($interval, $callback) $interval = $this->convertFloatSecondsToMilliseconds($interval); $event = \uv_timer_init($this->uv); - $this->timers->attach($timer, $event); + $this->timers->offsetSet($timer, $event); \uv_timer_start( $event, $interval, @@ -167,7 +167,7 @@ public function cancelTimer(TimerInterface $timer) { if (isset($this->timers[$timer])) { @\uv_timer_stop($this->timers[$timer]); - $this->timers->detach($timer); + $this->timers->offsetUnset($timer); } }