Skip to content

Commit fd5e886

Browse files
committed
Add PHPStan to test environment
1 parent 6019855 commit fd5e886

18 files changed

+144
-73
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/.gitattributes export-ignore
22
/.github/ export-ignore
33
/.gitignore export-ignore
4+
/phpstan.neon.dist export-ignore
45
/phpunit.xml.dist export-ignore
56
/phpunit.xml.legacy export-ignore
67
/tests/ export-ignore

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,25 @@ jobs:
3030
if: ${{ matrix.php >= 7.3 }}
3131
- run: vendor/bin/phpunit --coverage-text -c phpunit.xml.legacy
3232
if: ${{ matrix.php < 7.3 }}
33+
34+
PHPStan:
35+
name: PHPStan (PHP ${{ matrix.php }})
36+
runs-on: ubuntu-22.04
37+
strategy:
38+
matrix:
39+
php:
40+
- 8.2
41+
- 8.1
42+
- 8.0
43+
- 7.4
44+
- 7.3
45+
- 7.2
46+
- 7.1
47+
steps:
48+
- uses: actions/checkout@v3
49+
- uses: shivammathur/setup-php@v2
50+
with:
51+
php-version: ${{ matrix.php }}
52+
coverage: none
53+
- run: composer install
54+
- run: vendor/bin/phpstan

README.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ Table of Contents
4848
* [Rejection forwarding](#rejection-forwarding)
4949
* [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding)
5050
5. [Install](#install)
51-
6. [Credits](#credits)
52-
7. [License](#license)
51+
6. [Tests](#tests)
52+
7. [Credits](#credits)
53+
8. [License](#license)
5354

5455
Introduction
5556
------------
@@ -586,6 +587,27 @@ PHP versions like this:
586587
composer require "react/promise:^3@dev || ^2 || ^1"
587588
```
588589

590+
## Tests
591+
592+
To run the test suite, you first need to clone this repo and then install all
593+
dependencies [through Composer](https://getcomposer.org/):
594+
595+
```bash
596+
composer install
597+
```
598+
599+
To run the test suite, go to the project root and run:
600+
601+
```bash
602+
vendor/bin/phpunit
603+
```
604+
605+
On top of this, we use PHPStan on level 3 to ensure type safety across the project:
606+
607+
```bash
608+
vendor/bin/phpstan
609+
```
610+
589611
Credits
590612
-------
591613

composer.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,27 @@
2828
"php": ">=7.1.0"
2929
},
3030
"require-dev": {
31+
"phpstan/phpstan": "1.10.20 || 1.4.10",
3132
"phpunit/phpunit": "^9.5 || ^7.5"
3233
},
3334
"autoload": {
3435
"psr-4": {
3536
"React\\Promise\\": "src/"
3637
},
37-
"files": ["src/functions_include.php"]
38+
"files": [
39+
"src/functions_include.php"
40+
]
3841
},
3942
"autoload-dev": {
4043
"psr-4": {
4144
"React\\Promise\\": [
4245
"tests/fixtures/",
4346
"tests/"
4447
]
45-
}
48+
},
49+
"files": [
50+
"tests/Fiber.php"
51+
]
4652
},
4753
"keywords": [
4854
"promise",

phpstan.neon.dist

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
parameters:
2+
level: 3
3+
4+
paths:
5+
- src/
6+
- tests/

src/functions.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,15 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
256256

257257
if ($type instanceof \ReflectionIntersectionType) {
258258
foreach ($type->getTypes() as $typeToMatch) {
259+
assert($typeToMatch instanceof \ReflectionNamedType);
259260
if (!($matches = ($typeToMatch->isBuiltin() && \gettype($reason) === $typeToMatch->getName())
260261
|| (new \ReflectionClass($typeToMatch->getName()))->isInstance($reason))) {
261262
break;
262263
}
263264
}
265+
assert(isset($matches));
264266
} else {
267+
assert($type instanceof \ReflectionNamedType);
265268
$matches = ($type->isBuiltin() && \gettype($reason) === $type->getName())
266269
|| (new \ReflectionClass($type->getName()))->isInstance($reason);
267270
}

tests/DeferredTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenc
5454
gc_collect_cycles();
5555
gc_collect_cycles(); // clear twice to avoid leftovers in PHP 7.4 with ext-xdebug and code coverage turned on
5656

57-
$deferred = new Deferred(function () use (&$deferred) { });
57+
$deferred = new Deferred(function () use (&$deferred) {
58+
assert($deferred instanceof Deferred);
59+
});
5860
$deferred->reject(new \Exception('foo'));
5961
unset($deferred);
6062

tests/Fiber.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
if (!class_exists(Fiber::class)) {
4+
/**
5+
* Fiber stub to make PHPStan happy on PHP < 8.1
6+
*
7+
* @link https://www.php.net/manual/en/class.fiber.php
8+
* @copyright Copyright (c) 2023 Christian Lück, taken from https://github.com/clue/framework-x with permission
9+
*/
10+
class Fiber
11+
{
12+
public static function suspend(mixed $value): void
13+
{
14+
// NOOP
15+
}
16+
17+
public function __construct(callable $callback)
18+
{
19+
assert(is_callable($callback));
20+
}
21+
22+
public function start(): int
23+
{
24+
return 42;
25+
}
26+
}
27+
}

tests/FunctionAnyTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,6 @@ public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfill
219219

220220
$promise2 = new Promise(function () {}, $this->expectCallableNever());
221221

222-
any([$deferred->promise(), $promise2], 1)->cancel();
222+
any([$deferred->promise(), $promise2])->cancel();
223223
}
224224
}

tests/FunctionCheckTypehintTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public function shouldAcceptStaticClassCallbackWithIntersectionTypehint()
122122
public function shouldAcceptInvokableObjectCallbackWithDNFTypehint()
123123
{
124124
self::assertFalse(_checkTypehint(new CallbackWithDNFTypehintClass(), new \RuntimeException()));
125-
self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new ArrayAccessibleException()));
125+
self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new IterableException()));
126126
self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new CountableException()));
127127
}
128128

@@ -134,7 +134,7 @@ public function shouldAcceptObjectMethodCallbackWithDNFTypehint()
134134
{
135135
self::assertFalse(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new \RuntimeException()));
136136
self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new CountableException()));
137-
self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new ArrayAccessibleException()));
137+
self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new IterableException()));
138138
}
139139

140140
/**
@@ -145,7 +145,7 @@ public function shouldAcceptStaticClassCallbackWithDNFTypehint()
145145
{
146146
self::assertFalse(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new \RuntimeException()));
147147
self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new CountableException()));
148-
self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new ArrayAccessibleException()));
148+
self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new IterableException()));
149149
}
150150

151151
/** @test */

0 commit comments

Comments
 (0)