-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
These annotations will aid static analyses like PHPStan and Psalm to enhance type-safety for this project and projects depending on it These changes make the following example understandable by PHPStan: ```php final readonly class User { public function __construct( public string $name, ) } /** * \React\Promise\PromiseInterface<User> */ function getCurrentUserFromDatabase(): \React\Promise\PromiseInterface { // The following line would do the database query and fetch the result from it // but keeping it simple for the sake of the example. return \React\Promise\resolve(new User('WyriHaximus')); } // For the sake of this example we're going to assume the following code runs // in \React\Async\async call echo await(getCurrentUserFromDatabase())->name; // This echos: WyriHaximus ```
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,8 +44,9 @@ | |
* } | ||
* ``` | ||
* | ||
* @param PromiseInterface $promise | ||
* @return mixed returns whatever the promise resolves to | ||
* @template T | ||
* @param PromiseInterface<T> $promise | ||
* @return T returns whatever the promise resolves to | ||
* @throws \Exception when the promise is rejected with an `Exception` | ||
* @throws \Throwable when the promise is rejected with a `Throwable` | ||
* @throws \UnexpectedValueException when the promise is rejected with an unexpected value (Promise API v1 or v2 only) | ||
|
@@ -93,13 +94,14 @@ function ($error) use (&$exception, &$rejected, &$wait, &$loopStarted) { | |
// promise is rejected with an unexpected value (Promise API v1 or v2 only) | ||
if (!$exception instanceof \Throwable) { | ||
$exception = new \UnexpectedValueException( | ||
'Promise rejected with unexpected value of type ' . (is_object($exception) ? get_class($exception) : gettype($exception)) | ||
'Promise rejected with unexpected value of type ' . (is_object($exception) ? get_class($exception) : gettype($exception)) // @phpstan-ignore-line | ||
); | ||
} | ||
|
||
throw $exception; | ||
} | ||
|
||
/** @var T $resolved */ | ||
return $resolved; | ||
} | ||
|
||
|
@@ -296,9 +298,16 @@ function delay(float $seconds): void | |
* }); | ||
* ``` | ||
* | ||
* @param callable(mixed ...$args):(\Generator<mixed,PromiseInterface,mixed,mixed>|mixed) $function | ||
* @template T | ||
* @template TYield | ||
* @template A1 (any number of function arguments, see https://github.com/phpstan/phpstan/issues/8214) | ||
* @template A2 | ||
* @template A3 | ||
* @template A4 | ||
* @template A5 | ||
* @param callable(A1, A2, A3, A4, A5):(\Generator<mixed, PromiseInterface<TYield>, TYield, PromiseInterface<T>|T>|PromiseInterface<T>|T) $function | ||
* @param mixed ...$args Optional list of additional arguments that will be passed to the given `$function` as is | ||
* @return PromiseInterface<mixed> | ||
* @return PromiseInterface<T> | ||
* @since 3.0.0 | ||
*/ | ||
function coroutine(callable $function, ...$args): PromiseInterface | ||
|
@@ -315,7 +324,7 @@ function coroutine(callable $function, ...$args): PromiseInterface | |
|
||
$promise = null; | ||
$deferred = new Deferred(function () use (&$promise) { | ||
/** @var ?PromiseInterface $promise */ | ||
/** @var ?PromiseInterface<T> $promise */ | ||
if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { | ||
$promise->cancel(); | ||
} | ||
|
@@ -336,7 +345,6 @@ function coroutine(callable $function, ...$args): PromiseInterface | |
return; | ||
} | ||
|
||
/** @var mixed $promise */ | ||
$promise = $generator->current(); | ||
if (!$promise instanceof PromiseInterface) { | ||
$next = null; | ||
|
@@ -346,6 +354,7 @@ function coroutine(callable $function, ...$args): PromiseInterface | |
return; | ||
} | ||
|
||
/** @var PromiseInterface<TYield> $promise */ | ||
assert($next instanceof \Closure); | ||
$promise->then(function ($value) use ($generator, $next) { | ||
$generator->send($value); | ||
|
@@ -364,12 +373,13 @@ function coroutine(callable $function, ...$args): PromiseInterface | |
} | ||
|
||
/** | ||
* @param iterable<callable():PromiseInterface<mixed>> $tasks | ||
* @return PromiseInterface<array<mixed>> | ||
* @template T | ||
* @param iterable<callable():(PromiseInterface<T>|T)> $tasks | ||
* @return PromiseInterface<array<T>> | ||
*/ | ||
function parallel(iterable $tasks): PromiseInterface | ||
{ | ||
/** @var array<int,PromiseInterface> $pending */ | ||
/** @var array<int,PromiseInterface<T>> $pending */ | ||
$pending = []; | ||
$deferred = new Deferred(function () use (&$pending) { | ||
foreach ($pending as $promise) { | ||
|
@@ -424,14 +434,15 @@ function parallel(iterable $tasks): PromiseInterface | |
} | ||
|
||
/** | ||
* @param iterable<callable():PromiseInterface<mixed>> $tasks | ||
* @return PromiseInterface<array<mixed>> | ||
* @template T | ||
* @param iterable<callable():(PromiseInterface<T>|T)> $tasks | ||
* @return PromiseInterface<array<T>> | ||
*/ | ||
function series(iterable $tasks): PromiseInterface | ||
{ | ||
$pending = null; | ||
$deferred = new Deferred(function () use (&$pending) { | ||
/** @var ?PromiseInterface $pending */ | ||
/** @var ?PromiseInterface<T> $pending */ | ||
if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) { | ||
$pending->cancel(); | ||
} | ||
|
@@ -478,14 +489,15 @@ function series(iterable $tasks): PromiseInterface | |
} | ||
|
||
/** | ||
* @param iterable<(callable():PromiseInterface<mixed>)|(callable(mixed):PromiseInterface<mixed>)> $tasks | ||
* @return PromiseInterface<mixed> | ||
* @template T | ||
* @param iterable<(callable():(PromiseInterface<T>|T))|(callable(mixed):(PromiseInterface<T>|T))> $tasks | ||
* @return PromiseInterface<($tasks is non-empty-array|\Traversable ? T : null)> | ||
*/ | ||
function waterfall(iterable $tasks): PromiseInterface | ||
Check failure on line 496 in src/functions.php GitHub Actions / PHPStan (PHP 7.1)
Check failure on line 496 in src/functions.php GitHub Actions / PHPStan (PHP 7.1)
|
||
{ | ||
$pending = null; | ||
$deferred = new Deferred(function () use (&$pending) { | ||
/** @var ?PromiseInterface $pending */ | ||
/** @var ?PromiseInterface<T> $pending */ | ||
if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) { | ||
$pending->cancel(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
use React\Promise\PromiseInterface; | ||
use function PHPStan\Testing\assertType; | ||
use function React\Async\await; | ||
use function React\Promise\resolve; | ||
|
||
assertType('bool', await(resolve(true))); | ||
|
||
final class AwaitExampleUser | ||
{ | ||
public string $name; | ||
|
||
public function __construct(string $name) { | ||
$this->name = $name; | ||
} | ||
} | ||
|
||
assertType('string', await(resolve(new AwaitExampleUser('WyriHaximus')))->name); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
|
||
use function PHPStan\Testing\assertType; | ||
use function React\Async\await; | ||
use function React\Async\coroutine; | ||
use function React\Promise\resolve; | ||
|
||
assertType('React\Promise\PromiseInterface<bool>', coroutine(static function () { | ||
return true; | ||
})); | ||
|
||
assertType('React\Promise\PromiseInterface<bool>', coroutine(static function () { | ||
return resolve(true); | ||
})); | ||
|
||
// assertType('React\Promise\PromiseInterface<bool>', coroutine(static function () { | ||
// return (yield resolve(true)); | ||
// })); | ||
|
||
assertType('React\Promise\PromiseInterface<int>', coroutine(static function () { | ||
// $bool = yield resolve(true); | ||
// assertType('bool', $bool); | ||
|
||
return time(); | ||
})); | ||
|
||
// assertType('React\Promise\PromiseInterface<bool>', coroutine(static function () { | ||
// $bool = yield resolve(true); | ||
// assertType('bool', $bool); | ||
|
||
// return $bool; | ||
// })); | ||
|
||
assertType('React\Promise\PromiseInterface<bool>', coroutine(static function () { | ||
yield resolve(time()); | ||
|
||
return true; | ||
})); | ||
|
||
assertType('React\Promise\PromiseInterface<bool>', coroutine(static function () { | ||
for ($i = 0; $i <= 10; $i++) { | ||
yield resolve($i); | ||
} | ||
|
||
return true; | ||
})); | ||
|
||
assertType('React\Promise\PromiseInterface<int>', coroutine(static fn(int $a): int => $a, 42)); | ||
Check failure on line 48 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 48 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 48 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
Check failure on line 48 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
Check failure on line 48 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.1)
|
||
assertType('React\Promise\PromiseInterface<int>', coroutine(static fn(int $a, int $b): int => $a + $b, 10, 32)); | ||
Check failure on line 49 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 49 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 49 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 49 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
Check failure on line 49 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
Check failure on line 49 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
Check failure on line 49 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.1)
Check failure on line 49 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.1)
|
||
assertType('React\Promise\PromiseInterface<int>', coroutine(static fn(int $a, int $b, int $c): int => $a + $b + $c, 10, 22, 10)); | ||
Check failure on line 50 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 50 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 50 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 50 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
Check failure on line 50 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
Check failure on line 50 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
Check failure on line 50 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.1)
Check failure on line 50 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.1)
|
||
assertType('React\Promise\PromiseInterface<int>', coroutine(static fn(int $a, int $b, int $c, int $d): int => $a + $b + $c + $d, 10, 22, 5, 5)); | ||
Check failure on line 51 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 51 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.3)
Check failure on line 51 in tests/types/coroutine.php GitHub Actions / PHPStan (PHP 7.2)
|
||
assertType('React\Promise\PromiseInterface<int>', coroutine(static fn(int $a, int $b, int $c, int $d, int $e): int => $a + $b + $c + $d + $e, 10, 12, 10, 5, 5)); | ||
|
||
assertType('bool', await(coroutine(static function () { | ||
return true; | ||
}))); | ||
|
||
// assertType('bool', await(coroutine(static function () { | ||
// return (yield resolve(true)); | ||
// }))); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
|
||
use React\Promise\PromiseInterface; | ||
use function PHPStan\Testing\assertType; | ||
use function React\Async\await; | ||
use function React\Async\parallel; | ||
use function React\Promise\resolve; | ||
|
||
assertType('React\Promise\PromiseInterface<array>', parallel([])); | ||
|
||
assertType('React\Promise\PromiseInterface<array<bool|float|int>>', parallel([ | ||
static fn (): PromiseInterface => resolve(true), | ||
static fn (): PromiseInterface => resolve(time()), | ||
static fn (): PromiseInterface => resolve(microtime(true)), | ||
])); | ||
|
||
assertType('React\Promise\PromiseInterface<array<bool|float|int>>', parallel([ | ||
static fn (): bool => true, | ||
static fn (): int => time(), | ||
static fn (): float => microtime(true), | ||
])); | ||
|
||
assertType('array<bool|float|int>', await(parallel([ | ||
static fn (): PromiseInterface => resolve(true), | ||
static fn (): PromiseInterface => resolve(time()), | ||
static fn (): PromiseInterface => resolve(microtime(true)), | ||
]))); | ||
|
||
assertType('array<bool|float|int>', await(parallel([ | ||
static fn (): bool => true, | ||
static fn (): int => time(), | ||
static fn (): float => microtime(true), | ||
]))); |