diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4bf2b84..fb6353bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,3 +120,36 @@ jobs: uses: docker://hhvm/hhvm:3.30-lts-latest with: args: hhvm vendor/bin/phpunit + + PHPStan: + name: PHPStan (PHP ${{ matrix.php }}) + runs-on: ubuntu-22.04 + strategy: + matrix: + php: + - 8.3 + - 8.2 + - 8.1 + - 8.0 + - 7.4 + - 7.3 + - 7.2 + - 7.1 + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + ini-file: development + ini-values: disable_functions='' # do not disable PCNTL functions on PHP < 8.1 + extensions: sockets, pcntl ${{ matrix.php >= 5.6 && ', event' || '' }} ${{ matrix.php >= 5.4 && ', ev' || '' }} + env: + fail-fast: true # fail step if any extension can not be installed + - name: Install ext-uv on PHP 7+ + run: | + sudo apt-get update -q && sudo apt-get install libuv1-dev + echo "yes" | sudo pecl install ${{ matrix.php >= 8.0 && 'uv-0.3.0' || 'uv-0.2.4' }} + php -m | grep -q uv || echo "extension=uv.so" >> "$(php -r 'echo php_ini_loaded_file();')" + - run: composer install + - run: vendor/bin/phpstan diff --git a/composer.json b/composer.json index 25a41fe1..c840d529 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "php": ">=5.3.0" }, "require-dev": { + "phpstan/phpstan": "^1", "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "suggest": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..fd9e9ea5 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,10 @@ +parameters: + level: max + + paths: + - src/ + - tests/ + +# ignoreErrors: +# - '#type specified#' +# - '#expects resource, resource\|false given#' diff --git a/src/ExtEvLoop.php b/src/ExtEvLoop.php index a3fcec68..ca601cc2 100644 --- a/src/ExtEvLoop.php +++ b/src/ExtEvLoop.php @@ -34,7 +34,7 @@ class ExtEvLoop implements LoopInterface private $futureTickQueue; /** - * @var SplObjectStorage + * @var SplObjectStorage */ private $timers; @@ -192,6 +192,10 @@ public function run() $this->futureTickQueue->tick(); $hasPendingCallbacks = !$this->futureTickQueue->isEmpty(); + /** + * @link https://github.com/phpstan/phpstan/issues/10566 + * @phpstan-ignore-next-line + */ $wasJustStopped = !$this->running; $nothingLeftToDo = !$this->readStreams && !$this->writeStreams @@ -199,6 +203,10 @@ public function run() && $this->signals->isEmpty(); $flags = Ev::RUN_ONCE; + /** + * @link https://github.com/phpstan/phpstan/issues/10566 + * @phpstan-ignore-next-line + */ if ($wasJustStopped || $hasPendingCallbacks) { $flags |= Ev::RUN_NOWAIT; } elseif ($nothingLeftToDo) { @@ -222,11 +230,13 @@ public function __destruct() } foreach ($this->readStreams as $key => $stream) { - $this->removeReadStream($key); + $this->readStreams[$key]->stop(); + unset($this->readStreams[$key]); } foreach ($this->writeStreams as $key => $stream) { - $this->removeWriteStream($key); + $this->readStreams[$key]->stop(); + unset($this->readStreams[$key]); } } diff --git a/src/ExtEventLoop.php b/src/ExtEventLoop.php index b162a402..3e720628 100644 --- a/src/ExtEventLoop.php +++ b/src/ExtEventLoop.php @@ -196,6 +196,10 @@ public function run() $this->futureTickQueue->tick(); $flags = EventBase::LOOP_ONCE; + /** + * @link https://github.com/phpstan/phpstan/issues/10566 + * @phpstan-ignore-next-line + */ if (!$this->running || !$this->futureTickQueue->isEmpty()) { $flags |= EventBase::LOOP_NONBLOCK; } elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count() && $this->signals->isEmpty()) { diff --git a/src/ExtUvLoop.php b/src/ExtUvLoop.php index 4434720d..3a20265d 100644 --- a/src/ExtUvLoop.php +++ b/src/ExtUvLoop.php @@ -213,6 +213,10 @@ public function run() $this->futureTickQueue->tick(); $hasPendingCallbacks = !$this->futureTickQueue->isEmpty(); + /** + * @link https://github.com/phpstan/phpstan/issues/10566 + * @phpstan-ignore-next-line + */ $wasJustStopped = !$this->running; $nothingLeftToDo = !$this->readStreams && !$this->writeStreams @@ -223,12 +227,20 @@ public function run() // otherwise use UV::RUN_NOWAIT. // @link http://docs.libuv.org/en/v1.x/loop.html#c.uv_run $flags = \UV::RUN_ONCE; + /** + * @link https://github.com/phpstan/phpstan/issues/10566 + * @phpstan-ignore-next-line + */ if ($wasJustStopped || $hasPendingCallbacks) { $flags = \UV::RUN_NOWAIT; } elseif ($nothingLeftToDo) { break; } + /** + * @link https://github.com/JetBrains/phpstorm-stubs/pull/1614 + * @phpstan-ignore-next-line + */ \uv_run($this->uv, $flags); } } @@ -261,6 +273,10 @@ private function removeStream($stream) if (!isset($this->readStreams[(int) $stream]) && !isset($this->writeStreams[(int) $stream])) { \uv_poll_stop($this->streamEvents[(int) $stream]); + /** + * @link https://github.com/JetBrains/phpstorm-stubs/pull/1615 + * @phpstan-ignore-next-line + */ \uv_close($this->streamEvents[(int) $stream]); unset($this->streamEvents[(int) $stream]); return; diff --git a/src/Loop.php b/src/Loop.php index 10976eea..111f4bb7 100644 --- a/src/Loop.php +++ b/src/Loop.php @@ -87,6 +87,7 @@ public static function addReadStream($stream, $listener) if (self::$instance === null) { self::get(); } + assert(self::$instance instanceof LoopInterface); self::$instance->addReadStream($stream, $listener); } @@ -105,6 +106,9 @@ public static function addWriteStream($stream, $listener) if (self::$instance === null) { self::get(); } + + assert(self::$instance instanceof LoopInterface); + self::$instance->addWriteStream($stream, $listener); } @@ -150,6 +154,9 @@ public static function addTimer($interval, $callback) if (self::$instance === null) { self::get(); } + + assert(self::$instance instanceof LoopInterface); + return self::$instance->addTimer($interval, $callback); } @@ -167,6 +174,9 @@ public static function addPeriodicTimer($interval, $callback) if (self::$instance === null) { self::get(); } + + assert(self::$instance instanceof LoopInterface); + return self::$instance->addPeriodicTimer($interval, $callback); } @@ -198,6 +208,8 @@ public static function futureTick($listener) self::get(); } + assert(self::$instance instanceof LoopInterface); + self::$instance->futureTick($listener); } @@ -216,6 +228,8 @@ public static function addSignal($signal, $listener) self::get(); } + assert(self::$instance instanceof LoopInterface); + self::$instance->addSignal($signal, $listener); } @@ -247,6 +261,8 @@ public static function run() self::get(); } + assert(self::$instance instanceof LoopInterface); + self::$instance->run(); } diff --git a/src/SignalsHandler.php b/src/SignalsHandler.php index 10d125df..3538fc8c 100644 --- a/src/SignalsHandler.php +++ b/src/SignalsHandler.php @@ -47,6 +47,9 @@ public function call($signal) } } + /** + * @phpstan-impure + */ public function count($signal) { if (!isset($this->signals[$signal])) { diff --git a/src/StreamSelectLoop.php b/src/StreamSelectLoop.php index 943a81aa..5933d1ad 100644 --- a/src/StreamSelectLoop.php +++ b/src/StreamSelectLoop.php @@ -63,6 +63,9 @@ final class StreamSelectLoop implements LoopInterface private $running; private $pcntl = false; private $pcntlPoll = false; + /** + * @var SignalsHandler + */ private $signals; public function __construct() @@ -183,7 +186,13 @@ public function run() $this->timers->tick(); - // Future-tick queue has pending callbacks ... + /** + * Future-tick queue has pending callbacks ... + * + * + * @link https://github.com/phpstan/phpstan/issues/10566 + * @phpstan-ignore-next-line + */ if (!$this->running || !$this->futureTickQueue->isEmpty()) { $timeout = 0; @@ -286,7 +295,7 @@ private function streamSelect(array &$read, array &$write, $timeout) } } - /** @var ?callable $previous */ + /** @var ?(callable(int, string, string, int): bool) $previous */ $previous = \set_error_handler(function ($errno, $errstr) use (&$previous) { // suppress warnings that occur when `stream_select()` is interrupted by a signal // PHP defines `EINTR` through `ext-sockets` or `ext-pcntl`, otherwise use common default (Linux & Mac) @@ -305,7 +314,7 @@ private function streamSelect(array &$read, array &$write, $timeout) } catch (\Throwable $e) { // @codeCoverageIgnoreStart \restore_error_handler(); throw $e; - } catch (\Exception $e) { + } catch (\Exception $e) { /** @phpstan-ignore-line */ \restore_error_handler(); throw $e; } // @codeCoverageIgnoreEnd diff --git a/tests/ExtEventLoopTest.php b/tests/ExtEventLoopTest.php index af4caa13..2d107642 100644 --- a/tests/ExtEventLoopTest.php +++ b/tests/ExtEventLoopTest.php @@ -62,15 +62,6 @@ public function createStream() return $stream; } - public function writeToStream($stream, $content) - { - if ('Linux' !== PHP_OS) { - return parent::writeToStream($stream, $content); - } - - fwrite($stream, $content); - } - /** * @group epoll-readable-error */ diff --git a/tests/StreamSelectLoopTest.php b/tests/StreamSelectLoopTest.php index 7e2435a8..d4cf072c 100644 --- a/tests/StreamSelectLoopTest.php +++ b/tests/StreamSelectLoopTest.php @@ -61,6 +61,7 @@ public function testStreamSelectReportsWarningForStreamWithFilter() $error = null; $previous = set_error_handler(function ($_, $errstr) use (&$error) { $error = $errstr; + return true; }); try { @@ -73,7 +74,9 @@ public function testStreamSelectReportsWarningForStreamWithFilter() $this->assertNotNull($error); - $now = set_error_handler(function () { }); + $now = set_error_handler(function () { + return true; + }); restore_error_handler(); $this->assertEquals($previous, $now); } @@ -114,7 +117,9 @@ public function testStreamSelectThrowsWhenCustomErrorHandlerThrowsForStreamWithF $this->assertInstanceOf('RuntimeException', $e); - $now = set_error_handler(function () { }); + $now = set_error_handler(function () { + return true; + }); restore_error_handler(); $this->assertEquals($previous, $now); } @@ -176,7 +181,6 @@ public function testSignalInterruptWithStream($signal) $loop = $this->loop; list($writeStream, $readStream) = $this->createSocketPair(); $loop->addReadStream($readStream, function ($stream) use ($loop) { - /** @var $loop LoopInterface */ $read = fgets($stream); if ($read === "end loop\n") { $loop->stop(); diff --git a/tests/bin/12-undefined.php b/tests/bin/12-undefined.php index c45cc0f4..339ea7f3 100644 --- a/tests/bin/12-undefined.php +++ b/tests/bin/12-undefined.php @@ -9,4 +9,9 @@ echo 'never'; }); +/** + * We're ignore this line because the test using this file relies on the error caused by it. + * + * @phpstan-ignore-next-line + */ $undefined->foo('bar'); diff --git a/tests/bin/22-stop-uncaught.php b/tests/bin/22-stop-uncaught.php index 5b6142ed..a2654f12 100644 --- a/tests/bin/22-stop-uncaught.php +++ b/tests/bin/22-stop-uncaught.php @@ -9,6 +9,11 @@ echo 'never'; }); +/** + * Ignoring the next line until we raise the minimum PHP version to 7.1 + * + * @phpstan-ignore-next-line + */ set_exception_handler(function (Exception $e) { echo 'Uncaught error occured' . PHP_EOL; Loop::stop(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy