Skip to content

Commit

Permalink
[WIP][3.x] Introduce PHPStan
Browse files Browse the repository at this point in the history
Full commit message will follow
  • Loading branch information
WyriHaximus committed Feb 14, 2024
1 parent 229fb29 commit ae8efe4
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 18 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"php": ">=5.3.0"
},
"require-dev": {
"phpstan/phpstan": "^1",
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
},
"suggest": {
Expand Down
10 changes: 10 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
parameters:
level: max

paths:
- src/
- tests/

# ignoreErrors:
# - '#type specified#'
# - '#expects resource, resource\|false given#'
16 changes: 13 additions & 3 deletions src/ExtEvLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class ExtEvLoop implements LoopInterface
private $futureTickQueue;

/**
* @var SplObjectStorage
* @var SplObjectStorage<TimerInterface, \EvTimer>
*/
private $timers;

Expand Down Expand Up @@ -192,13 +192,21 @@ 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
&& !$this->timers->count()
&& $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) {
Expand All @@ -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]);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/ExtEventLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
16 changes: 16 additions & 0 deletions src/ExtUvLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions src/Loop.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down Expand Up @@ -198,6 +208,8 @@ public static function futureTick($listener)
self::get();
}

assert(self::$instance instanceof LoopInterface);

self::$instance->futureTick($listener);
}

Expand All @@ -216,6 +228,8 @@ public static function addSignal($signal, $listener)
self::get();
}

assert(self::$instance instanceof LoopInterface);

self::$instance->addSignal($signal, $listener);
}

Expand Down Expand Up @@ -247,6 +261,8 @@ public static function run()
self::get();
}

assert(self::$instance instanceof LoopInterface);

self::$instance->run();
}

Expand Down
3 changes: 3 additions & 0 deletions src/SignalsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public function call($signal)
}
}

/**
* @phpstan-impure
*/
public function count($signal)
{
if (!isset($this->signals[$signal])) {
Expand Down
15 changes: 12 additions & 3 deletions src/StreamSelectLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ final class StreamSelectLoop implements LoopInterface
private $running;
private $pcntl = false;
private $pcntlPoll = false;
/**
* @var SignalsHandler
*/
private $signals;

public function __construct()
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
9 changes: 0 additions & 9 deletions tests/ExtEventLoopTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
10 changes: 7 additions & 3 deletions tests/StreamSelectLoopTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public function testStreamSelectReportsWarningForStreamWithFilter()
$error = null;
$previous = set_error_handler(function ($_, $errstr) use (&$error) {
$error = $errstr;
return true;
});

try {
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
Expand Down
5 changes: 5 additions & 0 deletions tests/bin/12-undefined.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
5 changes: 5 additions & 0 deletions tests/bin/22-stop-uncaught.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit ae8efe4

Please sign in to comment.