Skip to content

Commit 6185725

Browse files
committed
Improve type definitions and update to PHPStan level max
1 parent 0fdd6a4 commit 6185725

14 files changed

+173
-133
lines changed

README.md

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

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

664664
```bash
665665
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/FiberMap.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,43 @@
99
*/
1010
final class FiberMap
1111
{
12+
/** @var array<int,bool> */
1213
private static array $status = [];
13-
private static array $map = [];
1414

15+
/** @var array<int,PromiseInterface> */
16+
private static array $map = [];
17+
18+
/** @param \Fiber<mixed,mixed,mixed,mixed> $fiber */
1519
public static function register(\Fiber $fiber): void
1620
{
1721
self::$status[\spl_object_id($fiber)] = false;
18-
self::$map[\spl_object_id($fiber)] = [];
1922
}
2023

24+
/** @param \Fiber<mixed,mixed,mixed,mixed> $fiber */
2125
public static function cancel(\Fiber $fiber): void
2226
{
2327
self::$status[\spl_object_id($fiber)] = true;
2428
}
2529

30+
/** @param \Fiber<mixed,mixed,mixed,mixed> $fiber */
2631
public static function setPromise(\Fiber $fiber, PromiseInterface $promise): void
2732
{
2833
self::$map[\spl_object_id($fiber)] = $promise;
2934
}
3035

36+
/** @param \Fiber<mixed,mixed,mixed,mixed> $fiber */
3137
public static function unsetPromise(\Fiber $fiber, PromiseInterface $promise): void
3238
{
3339
unset(self::$map[\spl_object_id($fiber)]);
3440
}
3541

42+
/** @param \Fiber<mixed,mixed,mixed,mixed> $fiber */
3643
public static function getPromise(\Fiber $fiber): ?PromiseInterface
3744
{
3845
return self::$map[\spl_object_id($fiber)] ?? null;
3946
}
4047

48+
/** @param \Fiber<mixed,mixed,mixed,mixed> $fiber */
4149
public static function unregister(\Fiber $fiber): void
4250
{
4351
unset(self::$status[\spl_object_id($fiber)], self::$map[\spl_object_id($fiber)]);

src/SimpleFiber.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
*/
1010
final class SimpleFiber implements FiberInterface
1111
{
12+
/** @var ?\Fiber<void,void,void,callable(): mixed> */
1213
private static ?\Fiber $scheduler = null;
14+
1315
private static ?\Closure $suspend = null;
16+
17+
/** @var ?\Fiber<mixed,mixed,mixed,mixed> */
1418
private ?\Fiber $fiber = null;
1519

1620
public function __construct()
@@ -57,13 +61,17 @@ public function suspend(): mixed
5761
self::$scheduler = new \Fiber(static fn() => Loop::run());
5862
// Run event loop to completion on shutdown.
5963
\register_shutdown_function(static function (): void {
64+
assert(self::$scheduler instanceof \Fiber);
6065
if (self::$scheduler->isSuspended()) {
6166
self::$scheduler->resume();
6267
}
6368
});
6469
}
6570

66-
return (self::$scheduler->isStarted() ? self::$scheduler->resume() : self::$scheduler->start())();
71+
$ret = (self::$scheduler->isStarted() ? self::$scheduler->resume() : self::$scheduler->start());
72+
assert(\is_callable($ret));
73+
74+
return $ret();
6775
}
6876

6977
return \Fiber::suspend();

src/functions.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@
176176
* await($promise);
177177
* ```
178178
*
179-
* @param callable(mixed ...$args):mixed $function
180-
* @return callable(mixed ...$args): PromiseInterface<mixed>
179+
* @param callable $function
180+
* @return callable(mixed ...): PromiseInterface<mixed>
181181
* @since 4.0.0
182182
* @see coroutine()
183183
*/
@@ -192,6 +192,7 @@ function async(callable $function): callable
192192
} catch (\Throwable $exception) {
193193
$reject($exception);
194194
} finally {
195+
assert($fiber instanceof \Fiber);
195196
FiberMap::unregister($fiber);
196197
}
197198
});
@@ -200,6 +201,7 @@ function async(callable $function): callable
200201

201202
$fiber->start();
202203
}, function () use (&$fiber): void {
204+
assert($fiber instanceof \Fiber);
203205
FiberMap::cancel($fiber);
204206
$promise = FiberMap::getPromise($fiber);
205207
if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) {
@@ -287,6 +289,7 @@ function (mixed $value) use (&$resolved, &$resolvedValue, &$fiber, $lowLevelFibe
287289
FiberMap::unsetPromise($lowLevelFiber, $promise);
288290
}
289291

292+
/** @var ?\Fiber<mixed,mixed,mixed,mixed> $fiber */
290293
if ($fiber === null) {
291294
$resolved = true;
292295
$resolvedValue = $value;
@@ -309,6 +312,7 @@ function (mixed $throwable) use (&$rejected, &$rejectedThrowable, &$fiber, $lowL
309312
// what a lovely piece of code!
310313
$r = new \ReflectionProperty('Exception', 'trace');
311314
$trace = $r->getValue($throwable);
315+
assert(\is_array($trace));
312316

313317
// Exception trace arguments only available when zend.exception_ignore_args is not set
314318
// @codeCoverageIgnoreStart
@@ -340,6 +344,7 @@ function (mixed $throwable) use (&$rejected, &$rejectedThrowable, &$fiber, $lowL
340344
}
341345

342346
if ($rejected) {
347+
assert($rejectedThrowable instanceof \Throwable);
343348
throw $rejectedThrowable;
344349
}
345350

@@ -587,7 +592,7 @@ function delay(float $seconds): void
587592
* });
588593
* ```
589594
*
590-
* @param callable(mixed ...$args):\Generator<mixed,PromiseInterface,mixed,mixed> $function
595+
* @param callable(mixed ...$args):(\Generator<mixed,PromiseInterface,mixed,mixed>|mixed) $function
591596
* @param mixed ...$args Optional list of additional arguments that will be passed to the given `$function` as is
592597
* @return PromiseInterface<mixed>
593598
* @since 3.0.0
@@ -606,6 +611,7 @@ function coroutine(callable $function, mixed ...$args): PromiseInterface
606611

607612
$promise = null;
608613
$deferred = new Deferred(function () use (&$promise) {
614+
/** @var ?PromiseInterface $promise */
609615
if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) {
610616
$promise->cancel();
611617
}
@@ -626,6 +632,7 @@ function coroutine(callable $function, mixed ...$args): PromiseInterface
626632
return;
627633
}
628634

635+
/** @var mixed $promise */
629636
$promise = $generator->current();
630637
if (!$promise instanceof PromiseInterface) {
631638
$next = null;
@@ -635,6 +642,7 @@ function coroutine(callable $function, mixed ...$args): PromiseInterface
635642
return;
636643
}
637644

645+
assert($next instanceof \Closure);
638646
$promise->then(function ($value) use ($generator, $next) {
639647
$generator->send($value);
640648
$next();
@@ -657,6 +665,7 @@ function coroutine(callable $function, mixed ...$args): PromiseInterface
657665
*/
658666
function parallel(iterable $tasks): PromiseInterface
659667
{
668+
/** @var array<int,PromiseInterface> $pending */
660669
$pending = [];
661670
$deferred = new Deferred(function () use (&$pending) {
662671
foreach ($pending as $promise) {
@@ -718,6 +727,7 @@ function series(iterable $tasks): PromiseInterface
718727
{
719728
$pending = null;
720729
$deferred = new Deferred(function () use (&$pending) {
730+
/** @var ?PromiseInterface $pending */
721731
if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) {
722732
$pending->cancel();
723733
}
@@ -732,7 +742,7 @@ function series(iterable $tasks): PromiseInterface
732742

733743
$taskCallback = function ($result) use (&$results, &$next) {
734744
$results[] = $result;
735-
assert($next instanceof \Closure);
745+
/** @var \Closure $next */
736746
$next();
737747
};
738748

@@ -746,9 +756,11 @@ function series(iterable $tasks): PromiseInterface
746756
$task = $tasks->current();
747757
$tasks->next();
748758
} else {
759+
assert(\is_array($tasks));
749760
$task = \array_shift($tasks);
750761
}
751762

763+
assert(\is_callable($task));
752764
$promise = \call_user_func($task);
753765
assert($promise instanceof PromiseInterface);
754766
$pending = $promise;
@@ -762,13 +774,14 @@ function series(iterable $tasks): PromiseInterface
762774
}
763775

764776
/**
765-
* @param iterable<callable(mixed=):PromiseInterface<mixed>> $tasks
777+
* @param iterable<(callable():PromiseInterface<mixed>)|(callable(mixed):PromiseInterface<mixed>)> $tasks
766778
* @return PromiseInterface<mixed>
767779
*/
768780
function waterfall(iterable $tasks): PromiseInterface
769781
{
770782
$pending = null;
771783
$deferred = new Deferred(function () use (&$pending) {
784+
/** @var ?PromiseInterface $pending */
772785
if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) {
773786
$pending->cancel();
774787
}
@@ -791,9 +804,11 @@ function waterfall(iterable $tasks): PromiseInterface
791804
$task = $tasks->current();
792805
$tasks->next();
793806
} else {
807+
assert(\is_array($tasks));
794808
$task = \array_shift($tasks);
795809
}
796810

811+
assert(\is_callable($task));
797812
$promise = \call_user_func_array($task, func_get_args());
798813
assert($promise instanceof PromiseInterface);
799814
$pending = $promise;

tests/AsyncTest.php

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
class AsyncTest extends TestCase
1616
{
17-
public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsValue()
17+
public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsValue(): void
1818
{
1919
$promise = async(function () {
2020
return 42;
@@ -28,7 +28,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsV
2828
$this->assertEquals(42, $value);
2929
}
3030

31-
public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsPromiseThatFulfillsWithValue()
31+
public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsPromiseThatFulfillsWithValue(): void
3232
{
3333
$promise = async(function () {
3434
return resolve(42);
@@ -42,7 +42,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsP
4242
$this->assertEquals(42, $value);
4343
}
4444

45-
public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrows()
45+
public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrows(): void
4646
{
4747
$promise = async(function () {
4848
throw new \RuntimeException('Foo', 42);
@@ -59,7 +59,7 @@ public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrow
5959
$this->assertEquals(42, $exception->getCode());
6060
}
6161

62-
public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackReturnsPromiseThatRejectsWithException()
62+
public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackReturnsPromiseThatRejectsWithException(): void
6363
{
6464
$promise = async(function () {
6565
return reject(new \RuntimeException('Foo', 42));
@@ -76,7 +76,7 @@ public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackRetur
7676
$this->assertEquals(42, $exception->getCode());
7777
}
7878

79-
public function testAsyncReturnsPendingPromiseWhenCallbackReturnsPendingPromise()
79+
public function testAsyncReturnsPendingPromiseWhenCallbackReturnsPendingPromise(): void
8080
{
8181
$promise = async(function () {
8282
return new Promise(function () { });
@@ -85,7 +85,7 @@ public function testAsyncReturnsPendingPromiseWhenCallbackReturnsPendingPromise(
8585
$promise->then($this->expectCallableNever(), $this->expectCallableNever());
8686
}
8787

88-
public function testAsyncWithAwaitReturnsReturnsPromiseFulfilledWithValueImmediatelyWhenPromiseIsFulfilled()
88+
public function testAsyncWithAwaitReturnsReturnsPromiseFulfilledWithValueImmediatelyWhenPromiseIsFulfilled(): void
8989
{
9090
$deferred = new Deferred();
9191

@@ -105,7 +105,7 @@ public function testAsyncWithAwaitReturnsReturnsPromiseFulfilledWithValueImmedia
105105
$this->assertEquals(42, $return);
106106
}
107107

108-
public function testAsyncWithAwaitReturnsPromiseRejectedWithExceptionImmediatelyWhenPromiseIsRejected()
108+
public function testAsyncWithAwaitReturnsPromiseRejectedWithExceptionImmediatelyWhenPromiseIsRejected(): void
109109
{
110110
$deferred = new Deferred();
111111

@@ -122,13 +122,13 @@ public function testAsyncWithAwaitReturnsPromiseRejectedWithExceptionImmediately
122122

123123
$deferred->reject(new \RuntimeException('Test', 42));
124124

125+
/** @var \RuntimeException $exception */
125126
$this->assertInstanceof(\RuntimeException::class, $exception);
126-
assert($exception instanceof \RuntimeException);
127127
$this->assertEquals('Test', $exception->getMessage());
128128
$this->assertEquals(42, $exception->getCode());
129129
}
130130

131-
public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsAfterAwaitingPromise()
131+
public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsAfterAwaitingPromise(): void
132132
{
133133
$promise = async(function () {
134134
$promise = new Promise(function ($resolve) {
@@ -143,7 +143,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsA
143143
$this->assertEquals(42, $value);
144144
}
145145

146-
public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrowsAfterAwaitingPromise()
146+
public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrowsAfterAwaitingPromise(): void
147147
{
148148
$promise = async(function () {
149149
$promise = new Promise(function ($_, $reject) {
@@ -159,7 +159,7 @@ public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrow
159159
await($promise);
160160
}
161161

162-
public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsAfterAwaitingTwoConcurrentPromises()
162+
public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsAfterAwaitingTwoConcurrentPromises(): void
163163
{
164164
$promise1 = async(function () {
165165
$promise = new Promise(function ($resolve) {
@@ -174,6 +174,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsA
174174
Loop::addTimer(0.11, fn () => $resolve($theAnswerToLifeTheUniverseAndEverything));
175175
});
176176

177+
/** @var int */
177178
return await($promise);
178179
})(42);
179180

@@ -186,7 +187,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsA
186187
$this->assertLessThan(0.12, $time);
187188
}
188189

189-
public function testCancelAsyncWillReturnRejectedPromiseWhenCancellingPendingPromiseRejects()
190+
public function testCancelAsyncWillReturnRejectedPromiseWhenCancellingPendingPromiseRejects(): void
190191
{
191192
$promise = async(function () {
192193
await(new Promise(function () { }, function () {
@@ -200,7 +201,7 @@ public function testCancelAsyncWillReturnRejectedPromiseWhenCancellingPendingPro
200201
$promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Operation cancelled')));
201202
}
202203

203-
public function testCancelAsyncWillReturnFulfilledPromiseWhenCancellingPendingPromiseRejectsInsideCatchThatReturnsValue()
204+
public function testCancelAsyncWillReturnFulfilledPromiseWhenCancellingPendingPromiseRejectsInsideCatchThatReturnsValue(): void
204205
{
205206
$promise = async(function () {
206207
try {
@@ -218,7 +219,7 @@ public function testCancelAsyncWillReturnFulfilledPromiseWhenCancellingPendingPr
218219
$promise->then($this->expectCallableOnceWith(42));
219220
}
220221

221-
public function testCancelAsycWillReturnPendigPromiseWhenCancellingFirstPromiseRejectsInsideCatchThatAwaitsSecondPromise()
222+
public function testCancelAsycWillReturnPendigPromiseWhenCancellingFirstPromiseRejectsInsideCatchThatAwaitsSecondPromise(): void
222223
{
223224
$promise = async(function () {
224225
try {
@@ -238,7 +239,7 @@ public function testCancelAsycWillReturnPendigPromiseWhenCancellingFirstPromiseR
238239
$promise->then($this->expectCallableNever(), $this->expectCallableNever());
239240
}
240241

241-
public function testCancelAsyncWillCancelNestedAwait()
242+
public function testCancelAsyncWillCancelNestedAwait(): void
242243
{
243244
self::expectOutputString('abc');
244245
$this->expectException(\RuntimeException::class);

0 commit comments

Comments
 (0)
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