forked from reactphp/async
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
1 parent
037cbf2
commit 422c056
Showing
11 changed files
with
239 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 function (int $a): int { return $a; }, 42)); | ||
assertType('React\Promise\PromiseInterface<int>', coroutine(static function (int $a, int $b): int { return $a + $b; }, 10, 32)); | ||
assertType('React\Promise\PromiseInterface<int>', coroutine(static function (int $a, int $b, int $c): int { return $a + $b + $c; }, 10, 22, 10)); | ||
assertType('React\Promise\PromiseInterface<int>', coroutine(static function (int $a, int $b, int $c, int $d): int { return $a + $b + $c + $d; }, 10, 22, 5, 5)); | ||
assertType('React\Promise\PromiseInterface<int>', coroutine(static function (int $a, int $b, int $c, int $d, int $e): int { return $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)); | ||
// }))); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 function (): PromiseInterface { return resolve(true); }, | ||
static function (): PromiseInterface { return resolve(time()); }, | ||
static function (): PromiseInterface { return resolve(microtime(true)); }, | ||
])); | ||
|
||
assertType('React\Promise\PromiseInterface<array<bool|float|int>>', parallel([ | ||
static function (): bool { return true; }, | ||
static function (): int { return time(); }, | ||
static function (): float { return microtime(true); }, | ||
])); | ||
|
||
assertType('array<bool|float|int>', await(parallel([ | ||
static function (): PromiseInterface { return resolve(true); }, | ||
static function (): PromiseInterface { return resolve(time()); }, | ||
static function (): PromiseInterface { return resolve(microtime(true)); }, | ||
]))); | ||
|
||
assertType('array<bool|float|int>', await(parallel([ | ||
static function (): bool { return true; }, | ||
static function (): int { return time(); }, | ||
static function (): float { return microtime(true); }, | ||
]))); |
Oops, something went wrong.