Skip to content

Commit c4e6145

Browse files
committed
Improve type definitions and update to PHPStan level max
1 parent fd5e886 commit c4e6145

39 files changed

+317
-326
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ To run the test suite, go to the project root and run:
602602
vendor/bin/phpunit
603603
```
604604

605-
On top of this, we use PHPStan on level 3 to ensure type safety across the project:
605+
On top of this, we use PHPStan on max level to ensure type safety across the project:
606606

607607
```bash
608608
vendor/bin/phpstan

phpstan.neon.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
parameters:
2-
level: 3
2+
level: max
33

44
paths:
55
- src/

src/Deferred.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
final class Deferred
66
{
7+
/** @var Promise */
78
private $promise;
9+
10+
/** @var callable */
811
private $resolveCallback;
12+
13+
/** @var callable */
914
private $rejectCallback;
1015

1116
public function __construct(callable $canceller = null)
@@ -21,6 +26,9 @@ public function promise(): PromiseInterface
2126
return $this->promise;
2227
}
2328

29+
/**
30+
* @param mixed $value
31+
*/
2432
public function resolve($value): void
2533
{
2634
($this->resolveCallback)($value);

src/Exception/CompositeException.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
*/
1212
class CompositeException extends \Exception
1313
{
14+
/** @var \Throwable[] */
1415
private $throwables;
1516

16-
public function __construct(array $throwables, $message = '', $code = 0, $previous = null)
17+
/** @param \Throwable[] $throwables */
18+
public function __construct(array $throwables, string $message = '', int $code = 0, ?\Throwable $previous = null)
1719
{
1820
parent::__construct($message, $code, $previous);
1921

src/Internal/CancellationQueue.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
*/
88
final class CancellationQueue
99
{
10+
/** @var bool */
1011
private $started = false;
12+
13+
/** @var object[] */
1114
private $queue = [];
1215

1316
public function __invoke(): void
@@ -20,6 +23,9 @@ public function __invoke(): void
2023
$this->drain();
2124
}
2225

26+
/**
27+
* @param mixed $cancellable
28+
*/
2329
public function enqueue($cancellable): void
2430
{
2531
if (!\is_object($cancellable) || !\method_exists($cancellable, 'then') || !\method_exists($cancellable, 'cancel')) {
@@ -37,6 +43,7 @@ private function drain(): void
3743
{
3844
for ($i = \key($this->queue); isset($this->queue[$i]); $i++) {
3945
$cancellable = $this->queue[$i];
46+
assert(\method_exists($cancellable, 'cancel'));
4047

4148
$exception = null;
4249

src/Internal/FulfilledPromise.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@
1010
*/
1111
final class FulfilledPromise implements PromiseInterface
1212
{
13+
/** @var mixed */
1314
private $value;
1415

16+
/**
17+
* @param mixed $value
18+
* @throws \InvalidArgumentException
19+
*/
1520
public function __construct($value = null)
1621
{
1722
if ($value instanceof PromiseInterface) {

src/Internal/RejectedPromise.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
*/
1212
final class RejectedPromise implements PromiseInterface
1313
{
14+
/** @var \Throwable */
1415
private $reason;
1516

17+
/**
18+
* @param \Throwable $reason
19+
*/
1620
public function __construct(\Throwable $reason)
1721
{
1822
$this->reason = $reason;

src/Promise.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66

77
final class Promise implements PromiseInterface
88
{
9+
/** @var ?callable */
910
private $canceller;
11+
12+
/** @var ?PromiseInterface */
1013
private $result;
1114

15+
/** @var callable[] */
1216
private $handlers = [];
1317

18+
/** @var int */
1419
private $requiredCancelRequests = 0;
1520

1621
public function __construct(callable $resolver, callable $canceller = null)
@@ -46,6 +51,7 @@ public function then(callable $onFulfilled = null, callable $onRejected = null):
4651
return new static(
4752
$this->resolver($onFulfilled, $onRejected),
4853
static function () use (&$parent) {
54+
assert($parent instanceof self);
4955
--$parent->requiredCancelRequests;
5056

5157
if ($parent->requiredCancelRequests <= 0) {
@@ -187,7 +193,7 @@ private function settle(PromiseInterface $result): void
187193
}
188194
}
189195

190-
private function unwrap($promise): PromiseInterface
196+
private function unwrap(PromiseInterface $promise): PromiseInterface
191197
{
192198
while ($promise instanceof self && null !== $promise->result) {
193199
$promise = $promise->result;
@@ -213,6 +219,7 @@ private function call(callable $cb): void
213219
} elseif (\is_object($callback) && !$callback instanceof \Closure) {
214220
$ref = new \ReflectionMethod($callback, '__invoke');
215221
} else {
222+
assert($callback instanceof \Closure || \is_string($callback));
216223
$ref = new \ReflectionFunction($callback);
217224
}
218225
$args = $ref->getNumberOfParameters();

src/functions.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ function reject(\Throwable $reason): PromiseInterface
6868
* will be an array containing the resolution values of each of the items in
6969
* `$promisesOrValues`.
7070
*
71-
* @param iterable $promisesOrValues
71+
* @param iterable<mixed> $promisesOrValues
7272
* @return PromiseInterface
7373
*/
7474
function all(iterable $promisesOrValues): PromiseInterface
@@ -77,6 +77,7 @@ function all(iterable $promisesOrValues): PromiseInterface
7777

7878
return new Promise(function ($resolve, $reject) use ($promisesOrValues, $cancellationQueue): void {
7979
$toResolve = 0;
80+
/** @var bool */
8081
$continue = true;
8182
$values = [];
8283

@@ -118,7 +119,7 @@ function (\Throwable $reason) use (&$continue, $reject): void {
118119
* The returned promise will become **infinitely pending** if `$promisesOrValues`
119120
* contains 0 items.
120121
*
121-
* @param iterable $promisesOrValues
122+
* @param iterable<mixed> $promisesOrValues
122123
* @return PromiseInterface
123124
*/
124125
function race(iterable $promisesOrValues): PromiseInterface
@@ -153,7 +154,7 @@ function race(iterable $promisesOrValues): PromiseInterface
153154
* The returned promise will also reject with a `React\Promise\Exception\LengthException`
154155
* if `$promisesOrValues` contains 0 items.
155156
*
156-
* @param iterable $promisesOrValues
157+
* @param iterable<mixed> $promisesOrValues
157158
* @return PromiseInterface
158159
*/
159160
function any(iterable $promisesOrValues): PromiseInterface
@@ -215,6 +216,7 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
215216
} elseif (\is_object($callback) && !$callback instanceof \Closure) {
216217
$callbackReflection = new \ReflectionMethod($callback, '__invoke');
217218
} else {
219+
assert($callback instanceof \Closure || \is_string($callback));
218220
$callbackReflection = new \ReflectionFunction($callback);
219221
}
220222

@@ -257,16 +259,16 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
257259
if ($type instanceof \ReflectionIntersectionType) {
258260
foreach ($type->getTypes() as $typeToMatch) {
259261
assert($typeToMatch instanceof \ReflectionNamedType);
260-
if (!($matches = ($typeToMatch->isBuiltin() && \gettype($reason) === $typeToMatch->getName())
261-
|| (new \ReflectionClass($typeToMatch->getName()))->isInstance($reason))) {
262+
$name = $typeToMatch->getName();
263+
if (!($matches = (!$typeToMatch->isBuiltin() && $reason instanceof $name))) {
262264
break;
263265
}
264266
}
265267
assert(isset($matches));
266268
} else {
267269
assert($type instanceof \ReflectionNamedType);
268-
$matches = ($type->isBuiltin() && \gettype($reason) === $type->getName())
269-
|| (new \ReflectionClass($type->getName()))->isInstance($reason);
270+
$name = $type->getName();
271+
$matches = !$type->isBuiltin() && $reason instanceof $name;
270272
}
271273

272274
// If we look for a single match (union), we can return early on match

tests/DeferredTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class DeferredTest extends TestCase
88
{
99
use PromiseTest\FullTestTrait;
1010

11-
public function getPromiseTestAdapter(callable $canceller = null)
11+
public function getPromiseTestAdapter(callable $canceller = null): CallbackPromiseAdapter
1212
{
1313
$d = new Deferred($canceller);
1414

@@ -21,7 +21,7 @@ public function getPromiseTestAdapter(callable $canceller = null)
2121
}
2222

2323
/** @test */
24-
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerRejectsWithException()
24+
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerRejectsWithException(): void
2525
{
2626
gc_collect_cycles();
2727
$deferred = new Deferred(function ($resolve, $reject) {
@@ -34,7 +34,7 @@ public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerRejectsWithEx
3434
}
3535

3636
/** @test */
37-
public function shouldRejectWithoutCreatingGarbageCyclesIfParentCancellerRejectsWithException()
37+
public function shouldRejectWithoutCreatingGarbageCyclesIfParentCancellerRejectsWithException(): void
3838
{
3939
gc_collect_cycles();
4040
gc_collect_cycles(); // clear twice to avoid leftovers in PHP 7.4 with ext-xdebug and code coverage turned on
@@ -49,7 +49,7 @@ public function shouldRejectWithoutCreatingGarbageCyclesIfParentCancellerRejects
4949
}
5050

5151
/** @test */
52-
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenceAndExplicitlyRejectWithException()
52+
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenceAndExplicitlyRejectWithException(): void
5353
{
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

tests/FunctionAllTest.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class FunctionAllTest extends TestCase
88
{
99
/** @test */
10-
public function shouldResolveEmptyInput()
10+
public function shouldResolveEmptyInput(): void
1111
{
1212
$mock = $this->createCallableMock();
1313
$mock
@@ -20,7 +20,7 @@ public function shouldResolveEmptyInput()
2020
}
2121

2222
/** @test */
23-
public function shouldResolveValuesArray()
23+
public function shouldResolveValuesArray(): void
2424
{
2525
$mock = $this->createCallableMock();
2626
$mock
@@ -33,7 +33,7 @@ public function shouldResolveValuesArray()
3333
}
3434

3535
/** @test */
36-
public function shouldResolvePromisesArray()
36+
public function shouldResolvePromisesArray(): void
3737
{
3838
$mock = $this->createCallableMock();
3939
$mock
@@ -46,7 +46,7 @@ public function shouldResolvePromisesArray()
4646
}
4747

4848
/** @test */
49-
public function shouldResolveSparseArrayInput()
49+
public function shouldResolveSparseArrayInput(): void
5050
{
5151
$mock = $this->createCallableMock();
5252
$mock
@@ -59,7 +59,7 @@ public function shouldResolveSparseArrayInput()
5959
}
6060

6161
/** @test */
62-
public function shouldResolveValuesGenerator()
62+
public function shouldResolveValuesGenerator(): void
6363
{
6464
$mock = $this->createCallableMock();
6565
$mock
@@ -77,7 +77,7 @@ public function shouldResolveValuesGenerator()
7777
}
7878

7979
/** @test */
80-
public function shouldResolveValuesGeneratorEmpty()
80+
public function shouldResolveValuesGeneratorEmpty(): void
8181
{
8282
$mock = $this->createCallableMock();
8383
$mock
@@ -86,7 +86,7 @@ public function shouldResolveValuesGeneratorEmpty()
8686
->with(self::identicalTo([]));
8787

8888
$gen = (function () {
89-
if (false) {
89+
if (false) { // @phpstan-ignore-line
9090
yield;
9191
}
9292
})();
@@ -95,7 +95,7 @@ public function shouldResolveValuesGeneratorEmpty()
9595
}
9696

9797
/** @test */
98-
public function shouldRejectIfAnyInputPromiseRejects()
98+
public function shouldRejectIfAnyInputPromiseRejects(): void
9999
{
100100
$exception2 = new Exception();
101101
$exception3 = new Exception();
@@ -111,7 +111,7 @@ public function shouldRejectIfAnyInputPromiseRejects()
111111
}
112112

113113
/** @test */
114-
public function shouldRejectInfiteGeneratorOrRejectedPromises()
114+
public function shouldRejectInfiteGeneratorOrRejectedPromises(): void
115115
{
116116
$mock = $this->createCallableMock();
117117
$mock
@@ -129,7 +129,7 @@ public function shouldRejectInfiteGeneratorOrRejectedPromises()
129129
}
130130

131131
/** @test */
132-
public function shouldPreserveTheOrderOfArrayWhenResolvingAsyncPromises()
132+
public function shouldPreserveTheOrderOfArrayWhenResolvingAsyncPromises(): void
133133
{
134134
$mock = $this->createCallableMock();
135135
$mock

0 commit comments

Comments
 (0)