Skip to content

[3.x] Add PHPStan to test environment with max level #77

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Improve type definitions and update to PHPStan level max
  • Loading branch information
clue committed Jun 14, 2023
commit 9b92ce5238dbacc46733b2fbc418293d3b038333
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ To run the test suite, go to the project root and run:
vendor/bin/phpunit
```

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

```bash
vendor/bin/phpstan
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 3
level: max

paths:
- src/
Expand Down
18 changes: 15 additions & 3 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ function await(PromiseInterface $promise)
$resolved = null;
$exception = null;
$rejected = false;

/** @var bool $loopStarted */
$loopStarted = false;

$promise->then(
Expand Down Expand Up @@ -294,7 +296,7 @@ function delay(float $seconds): void
* });
* ```
*
* @param callable(mixed ...$args):\Generator<mixed,PromiseInterface,mixed,mixed> $function
* @param callable(mixed ...$args):(\Generator<mixed,PromiseInterface,mixed,mixed>|mixed) $function
* @param mixed ...$args Optional list of additional arguments that will be passed to the given `$function` as is
* @return PromiseInterface<mixed>
* @since 3.0.0
Expand All @@ -313,6 +315,7 @@ function coroutine(callable $function, ...$args): PromiseInterface

$promise = null;
$deferred = new Deferred(function () use (&$promise) {
/** @var ?PromiseInterface $promise */
if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) {
$promise->cancel();
}
Expand All @@ -333,6 +336,7 @@ function coroutine(callable $function, ...$args): PromiseInterface
return;
}

/** @var mixed $promise */
$promise = $generator->current();
if (!$promise instanceof PromiseInterface) {
$next = null;
Expand All @@ -342,6 +346,7 @@ function coroutine(callable $function, ...$args): PromiseInterface
return;
}

assert($next instanceof \Closure);
$promise->then(function ($value) use ($generator, $next) {
$generator->send($value);
$next();
Expand All @@ -364,6 +369,7 @@ function coroutine(callable $function, ...$args): PromiseInterface
*/
function parallel(iterable $tasks): PromiseInterface
{
/** @var array<int,PromiseInterface> $pending */
$pending = [];
$deferred = new Deferred(function () use (&$pending) {
foreach ($pending as $promise) {
Expand Down Expand Up @@ -425,6 +431,7 @@ function series(iterable $tasks): PromiseInterface
{
$pending = null;
$deferred = new Deferred(function () use (&$pending) {
/** @var ?PromiseInterface $pending */
if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) {
$pending->cancel();
}
Expand All @@ -439,7 +446,7 @@ function series(iterable $tasks): PromiseInterface

$taskCallback = function ($result) use (&$results, &$next) {
$results[] = $result;
assert($next instanceof \Closure);
/** @var \Closure $next */
$next();
};

Expand All @@ -453,9 +460,11 @@ function series(iterable $tasks): PromiseInterface
$task = $tasks->current();
$tasks->next();
} else {
assert(\is_array($tasks));
$task = \array_shift($tasks);
}

assert(\is_callable($task));
$promise = \call_user_func($task);
assert($promise instanceof PromiseInterface);
$pending = $promise;
Expand All @@ -469,13 +478,14 @@ function series(iterable $tasks): PromiseInterface
}

/**
* @param iterable<callable(mixed=):PromiseInterface<mixed>> $tasks
* @param iterable<(callable():PromiseInterface<mixed>)|(callable(mixed):PromiseInterface<mixed>)> $tasks
* @return PromiseInterface<mixed>
*/
function waterfall(iterable $tasks): PromiseInterface
{
$pending = null;
$deferred = new Deferred(function () use (&$pending) {
/** @var ?PromiseInterface $pending */
if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) {
$pending->cancel();
}
Expand All @@ -498,9 +508,11 @@ function waterfall(iterable $tasks): PromiseInterface
$task = $tasks->current();
$tasks->next();
} else {
assert(\is_array($tasks));
$task = \array_shift($tasks);
}

assert(\is_callable($task));
$promise = \call_user_func_array($task, func_get_args());
assert($promise instanceof PromiseInterface);
$pending = $promise;
Expand Down
28 changes: 14 additions & 14 deletions tests/AwaitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class AwaitTest extends TestCase
{
public function testAwaitThrowsExceptionWhenPromiseIsRejectedWithException()
public function testAwaitThrowsExceptionWhenPromiseIsRejectedWithException(): void
{
$promise = new Promise(function () {
throw new \Exception('test');
Expand All @@ -19,7 +19,7 @@ public function testAwaitThrowsExceptionWhenPromiseIsRejectedWithException()
React\Async\await($promise);
}

public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWithFalse()
public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWithFalse(): void
{
if (!interface_exists('React\Promise\CancellablePromiseInterface')) {
$this->markTestSkipped('Promises must be rejected with a \Throwable instance since Promise v3');
Expand All @@ -34,7 +34,7 @@ public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWith
React\Async\await($promise);
}

public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWithNull()
public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWithNull(): void
{
if (!interface_exists('React\Promise\CancellablePromiseInterface')) {
$this->markTestSkipped('Promises must be rejected with a \Throwable instance since Promise v3');
Expand All @@ -49,7 +49,7 @@ public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWith
React\Async\await($promise);
}

public function testAwaitThrowsErrorWhenPromiseIsRejectedWithError()
public function testAwaitThrowsErrorWhenPromiseIsRejectedWithError(): void
{
$promise = new Promise(function ($_, $reject) {
throw new \Error('Test', 42);
Expand All @@ -61,7 +61,7 @@ public function testAwaitThrowsErrorWhenPromiseIsRejectedWithError()
React\Async\await($promise);
}

public function testAwaitReturnsValueWhenPromiseIsFullfilled()
public function testAwaitReturnsValueWhenPromiseIsFullfilled(): void
{
$promise = new Promise(function ($resolve) {
$resolve(42);
Expand All @@ -70,7 +70,7 @@ public function testAwaitReturnsValueWhenPromiseIsFullfilled()
$this->assertEquals(42, React\Async\await($promise));
}

public function testAwaitReturnsValueWhenPromiseIsFulfilledEvenWhenOtherTimerStopsLoop()
public function testAwaitReturnsValueWhenPromiseIsFulfilledEvenWhenOtherTimerStopsLoop(): void
{
$promise = new Promise(function ($resolve) {
Loop::addTimer(0.02, function () use ($resolve) {
Expand All @@ -84,7 +84,7 @@ public function testAwaitReturnsValueWhenPromiseIsFulfilledEvenWhenOtherTimerSto
$this->assertEquals(2, React\Async\await($promise));
}

public function testAwaitWithAlreadyFulfilledPromiseWillReturnWithoutRunningLoop()
public function testAwaitWithAlreadyFulfilledPromiseWillReturnWithoutRunningLoop(): void
{
$now = true;

Expand All @@ -100,7 +100,7 @@ public function testAwaitWithAlreadyFulfilledPromiseWillReturnWithoutRunningLoop
$this->assertTrue($now);
}

public function testAwaitWithAlreadyFulfilledPromiseWillReturnWithoutStoppingLoop()
public function testAwaitWithAlreadyFulfilledPromiseWillReturnWithoutStoppingLoop(): void
{
$ticks = 0;

Expand Down Expand Up @@ -128,7 +128,7 @@ public function testAwaitWithAlreadyFulfilledPromiseWillReturnWithoutStoppingLoo
$this->assertEquals(2, $ticks);
}

public function testAwaitWithPendingPromiseThatWillResolveWillStopLoopBeforeLastTimerFinishes()
public function testAwaitWithPendingPromiseThatWillResolveWillStopLoopBeforeLastTimerFinishes(): void
{
$promise = new Promise(function ($resolve) {
Loop::addTimer(0.02, function () use ($resolve) {
Expand Down Expand Up @@ -159,7 +159,7 @@ public function testAwaitWithPendingPromiseThatWillResolveWillStopLoopBeforeLast
$this->assertEquals(1, $ticks);
}

public function testAwaitWithAlreadyRejectedPromiseWillReturnWithoutStoppingLoop()
public function testAwaitWithAlreadyRejectedPromiseWillReturnWithoutStoppingLoop(): void
{
$ticks = 0;

Expand Down Expand Up @@ -191,7 +191,7 @@ public function testAwaitWithAlreadyRejectedPromiseWillReturnWithoutStoppingLoop
$this->assertEquals(2, $ticks);
}

public function testAwaitWithPendingPromiseThatWillRejectWillStopLoopBeforeLastTimerFinishes()
public function testAwaitWithPendingPromiseThatWillRejectWillStopLoopBeforeLastTimerFinishes(): void
{
$promise = new Promise(function ($_, $reject) {
Loop::addTimer(0.02, function () use (&$reject) {
Expand Down Expand Up @@ -227,7 +227,7 @@ public function testAwaitWithPendingPromiseThatWillRejectWillStopLoopBeforeLastT
$this->assertEquals(1, $ticks);
}

public function testAwaitShouldNotCreateAnyGarbageReferencesForResolvedPromise()
public function testAwaitShouldNotCreateAnyGarbageReferencesForResolvedPromise(): void
{
if (class_exists('React\Promise\When')) {
$this->markTestSkipped('Not supported on legacy Promise v1 API');
Expand All @@ -244,7 +244,7 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForResolvedPromise()
$this->assertEquals(0, gc_collect_cycles());
}

public function testAwaitShouldNotCreateAnyGarbageReferencesForRejectedPromise()
public function testAwaitShouldNotCreateAnyGarbageReferencesForRejectedPromise(): void
{
if (class_exists('React\Promise\When')) {
$this->markTestSkipped('Not supported on legacy Promise v1 API');
Expand All @@ -265,7 +265,7 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForRejectedPromise()
$this->assertEquals(0, gc_collect_cycles());
}

public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithNullValue()
public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithNullValue(): void
{
if (!interface_exists('React\Promise\CancellablePromiseInterface')) {
$this->markTestSkipped('Promises must be rejected with a \Throwable instance since Promise v3');
Expand Down
Loading
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