Skip to content

Commit

Permalink
[3.x] Add template annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
simPod authored and WyriHaximus committed Jun 26, 2023
1 parent d66fa66 commit 866662c
Show file tree
Hide file tree
Showing 21 changed files with 269 additions and 98 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
name: PHPStan (PHP ${{ matrix.php }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
php:
- 8.2
Expand All @@ -52,3 +53,6 @@ jobs:
coverage: none
- run: composer install
- run: vendor/bin/phpstan
if: ${{ matrix.php >= 7.2 }}
- run: vendor/bin/phpstan --configuration="phpstan.legacy.neon.dist"
if: ${{ matrix.php < 7.2 }}
7 changes: 7 additions & 0 deletions phpstan.legacy.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
parameters:
ignoreErrors:
- '#Template type T is declared as covariant, but occurs in contravariant position in parameter result of method React\\Promise\\Promise::settle\(\).#'
- '#Template type T is declared as covariant, but occurs in contravariant position in parameter promise of method React\\Promise\\Promise::unwrap\(\).#'

includes:
- phpstan.neon.dist
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ parameters:
paths:
- src/
- tests/
- types/
10 changes: 9 additions & 1 deletion src/Deferred.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

namespace React\Promise;

/**
* @template-covariant T
*/
final class Deferred
{
/** @var Promise */
/**
* @var PromiseInterface<T>
*/
private $promise;

/** @var callable */
Expand All @@ -21,6 +26,9 @@ public function __construct(callable $canceller = null)
}, $canceller);
}

/**
* @return PromiseInterface<T>
*/
public function promise(): PromiseInterface
{
return $this->promise;
Expand Down
18 changes: 16 additions & 2 deletions src/Internal/FulfilledPromise.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@

/**
* @internal
*
* @template-implements PromiseInterface<T>
* @template-covariant T
*/
final class FulfilledPromise implements PromiseInterface
{
/** @var mixed */
/** @var T */
private $value;

/**
* @param mixed $value
* @param T $value
* @throws \InvalidArgumentException
*/
public function __construct($value = null)
Expand All @@ -26,6 +29,9 @@ public function __construct($value = null)
$this->value = $value;
}

/**
* @inheritdoc
*/
public function then(callable $onFulfilled = null, callable $onRejected = null): PromiseInterface
{
if (null === $onFulfilled) {
Expand All @@ -39,11 +45,17 @@ public function then(callable $onFulfilled = null, callable $onRejected = null):
}
}

/**
* @inheritdoc
*/
public function catch(callable $onRejected): PromiseInterface
{
return $this;
}

/**
* @inheritdoc
*/
public function finally(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->then(function ($value) use ($onFulfilledOrRejected): PromiseInterface {
Expand All @@ -60,6 +72,7 @@ public function cancel(): void
/**
* @deprecated 3.0.0 Use `catch()` instead
* @see self::catch()
* @inheritdoc
*/
public function otherwise(callable $onRejected): PromiseInterface
{
Expand All @@ -69,6 +82,7 @@ public function otherwise(callable $onRejected): PromiseInterface
/**
* @deprecated 3.0.0 Use `finally()` instead
* @see self::finally()
* @inheritdoc
*/
public function always(callable $onFulfilledOrRejected): PromiseInterface
{
Expand Down
14 changes: 14 additions & 0 deletions src/Internal/RejectedPromise.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

/**
* @internal
*
* @template-implements PromiseInterface<T>
* @template-covariant T
*/
final class RejectedPromise implements PromiseInterface
{
Expand All @@ -22,6 +25,9 @@ public function __construct(\Throwable $reason)
$this->reason = $reason;
}

/**
* @inheritdoc
*/
public function then(callable $onFulfilled = null, callable $onRejected = null): PromiseInterface
{
if (null === $onRejected) {
Expand All @@ -35,6 +41,9 @@ public function then(callable $onFulfilled = null, callable $onRejected = null):
}
}

/**
* @inheritdoc
*/
public function catch(callable $onRejected): PromiseInterface
{
if (!_checkTypehint($onRejected, $this->reason)) {
Expand All @@ -44,6 +53,9 @@ public function catch(callable $onRejected): PromiseInterface
return $this->then(null, $onRejected);
}

/**
* @inheritdoc
*/
public function finally(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->then(null, function (\Throwable $reason) use ($onFulfilledOrRejected): PromiseInterface {
Expand All @@ -60,6 +72,7 @@ public function cancel(): void
/**
* @deprecated 3.0.0 Use `catch()` instead
* @see self::catch()
* @inheritdoc
*/
public function otherwise(callable $onRejected): PromiseInterface
{
Expand All @@ -69,6 +82,7 @@ public function otherwise(callable $onRejected): PromiseInterface
/**
* @deprecated 3.0.0 Use `always()` instead
* @see self::always()
* @inheritdoc
*/
public function always(callable $onFulfilledOrRejected): PromiseInterface
{
Expand Down
37 changes: 30 additions & 7 deletions src/Promise.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
namespace React\Promise;

use React\Promise\Internal\RejectedPromise;
use Throwable;

/**
* @template-implements PromiseInterface<T>
* @template-covariant T
*/
final class Promise implements PromiseInterface
{
/** @var ?callable */
private $canceller;

/** @var ?PromiseInterface */
/** @var PromiseInterface<T> */
private $result;

/** @var callable[] */
Expand All @@ -30,6 +35,9 @@ public function __construct(callable $resolver, callable $canceller = null)
$this->call($cb);
}

/**
* @inheritdoc
*/
public function then(callable $onFulfilled = null, callable $onRejected = null): PromiseInterface
{
if (null !== $this->result) {
Expand Down Expand Up @@ -63,6 +71,9 @@ static function () use (&$parent) {
);
}

/**
* @inheritdoc
*/
public function catch(callable $onRejected): PromiseInterface
{
return $this->then(null, static function ($reason) use ($onRejected) {
Expand All @@ -74,14 +85,17 @@ public function catch(callable $onRejected): PromiseInterface
});
}

/**
* @inheritdoc
*/
public function finally(callable $onFulfilledOrRejected): PromiseInterface
{
return $this->then(static function ($value) use ($onFulfilledOrRejected) {
return resolve($onFulfilledOrRejected())->then(function () use ($value) {
return resolve($onFulfilledOrRejected())->then(function ($_) use ($value) {
return $value;
});
}, static function ($reason) use ($onFulfilledOrRejected) {
return resolve($onFulfilledOrRejected())->then(function () use ($reason) {
return resolve($onFulfilledOrRejected())->then(function ($_) use ($reason) {
return new RejectedPromise($reason);
});
});
Expand Down Expand Up @@ -125,6 +139,7 @@ public function cancel(): void
/**
* @deprecated 3.0.0 Use `catch()` instead
* @see self::catch()
* @inheritdoc
*/
public function otherwise(callable $onRejected): PromiseInterface
{
Expand All @@ -134,6 +149,7 @@ public function otherwise(callable $onRejected): PromiseInterface
/**
* @deprecated 3.0.0 Use `finally()` instead
* @see self::finally()
* @inheritdoc
*/
public function always(callable $onFulfilledOrRejected): PromiseInterface
{
Expand All @@ -157,7 +173,7 @@ private function resolver(callable $onFulfilled = null, callable $onRejected = n
};
}

private function reject(\Throwable $reason): void
private function reject(Throwable $reason): void
{
if (null !== $this->result) {
return;
Expand All @@ -166,6 +182,9 @@ private function reject(\Throwable $reason): void
$this->settle(reject($reason));
}

/**
* @param PromiseInterface<T>|PromiseInterface<null> $result
*/
private function settle(PromiseInterface $result): void
{
$result = $this->unwrap($result);
Expand Down Expand Up @@ -193,13 +212,17 @@ private function settle(PromiseInterface $result): void
}
}

/**
* @param PromiseInterface<T>|PromiseInterface<null> $promise
* @return PromiseInterface<T>
*/
private function unwrap(PromiseInterface $promise): PromiseInterface
{
while ($promise instanceof self && null !== $promise->result) {
$promise = $promise->result;
}

return $promise;
return $promise; /** @phpstan-ignore-line */
}

private function call(callable $cb): void
Expand Down Expand Up @@ -245,15 +268,15 @@ static function ($value) use (&$target) {
$target = null;
}
},
static function (\Throwable $reason) use (&$target) {
static function (Throwable $reason) use (&$target) {
if ($target !== null) {
$target->reject($reason);
$target = null;
}
}
);
}
} catch (\Throwable $e) {
} catch (Throwable $e) {
$target = null;
$this->reject($e);
}
Expand Down
21 changes: 12 additions & 9 deletions src/PromiseInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace React\Promise;

/**
* @template-covariant T
*/
interface PromiseInterface
{
/**
Expand All @@ -28,9 +31,9 @@ interface PromiseInterface
* 2. `$onFulfilled` and `$onRejected` will never be called more
* than once.
*
* @param callable|null $onFulfilled
* @param callable|null $onRejected
* @return PromiseInterface
* @template TFulfilled as PromiseInterface<T>|T
* @param (callable(T): TFulfilled)|null $onFulfilled
* @return PromiseInterface<T>
*/
public function then(?callable $onFulfilled = null, ?callable $onRejected = null): PromiseInterface;

Expand All @@ -44,8 +47,7 @@ public function then(?callable $onFulfilled = null, ?callable $onRejected = null
* Additionally, you can type hint the `$reason` argument of `$onRejected` to catch
* only specific errors.
*
* @param callable $onRejected
* @return PromiseInterface
* @return PromiseInterface<T>
*/
public function catch(callable $onRejected): PromiseInterface;

Expand Down Expand Up @@ -91,8 +93,9 @@ public function catch(callable $onRejected): PromiseInterface;
* ->finally('cleanup');
* ```
*
* @param callable $onFulfilledOrRejected
* @return PromiseInterface
* @template TReturn of PromiseInterface<T>|T
* @param callable(T=): TReturn $onFulfilledOrRejected
* @return PromiseInterface<T>
*/
public function finally(callable $onFulfilledOrRejected): PromiseInterface;

Expand All @@ -118,7 +121,7 @@ public function cancel(): void;
* ```
*
* @param callable $onRejected
* @return PromiseInterface
* @return PromiseInterface<T>
* @deprecated 3.0.0 Use catch() instead
* @see self::catch()
*/
Expand All @@ -135,7 +138,7 @@ public function otherwise(callable $onRejected): PromiseInterface;
* ```
*
* @param callable $onFulfilledOrRejected
* @return PromiseInterface
* @return PromiseInterface<T>
* @deprecated 3.0.0 Use finally() instead
* @see self::finally()
*/
Expand Down
Loading

0 comments on commit 866662c

Please sign in to comment.