From f4cc8aee6996af29577a842393ab4e25d6e9e563 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 21 Jun 2022 08:43:38 +0200 Subject: [PATCH] Add basic template annotations This adds basic type safety annotations for static analyzers like PHPStan and Psalm. This will cover around 80% of the use cases and a follow-up PR for all supported versions will be proposed later to get it to a 100% of close to a 100%. By adding these annotations methods returning a promise can hint their resolving type by adding `@return PromiseInterface` when they for example resolve to a boolean. By doing that Psalm and PHPStan will understand that the following bit of code will not become an issue because the method's contract promised a boolean through the promise: ```php $promise->then(static function (bool $isEnabled) {}); ``` However, the following will yield errors: ```php $promise->then(static function (string $isEnabled) {}); ``` This PR is a requirement for https://github.com/reactphp/async/pull/40 --- .gitattributes | 2 ++ .github/workflows/ci.yml | 15 +++++++++++++++ phpstan.neon.dist | 4 ++++ src/PromiseInterface.php | 8 ++++++-- src/functions.php | 5 +++-- types/PromiseInterface.php | 9 +++++++++ 6 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 phpstan.neon.dist create mode 100644 types/PromiseInterface.php diff --git a/.gitattributes b/.gitattributes index 21be40ca..94a1c394 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,8 @@ /.gitattributes export-ignore /.github/ export-ignore /.gitignore export-ignore +/phpstan.neon.dist export-ignore /phpunit.xml.dist export-ignore /phpunit.xml.legacy export-ignore /tests/ export-ignore +/types/ export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e20fa07c..749a5e87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,3 +45,18 @@ jobs: - run: composer self-update --2.2 # downgrade Composer for HHVM - run: hhvm $(which composer) install - run: hhvm vendor/bin/phpunit + + PHPStan: + name: PHPStan + runs-on: ubuntu-20.04 + strategy: + matrix: + php: + - 8.1 + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + - run: composer require phpstan/phpstan + - run: vendor/bin/phpstan diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..609d438c --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,4 @@ +parameters: + paths: + - types + level: max diff --git a/src/PromiseInterface.php b/src/PromiseInterface.php index edcb0077..f52f5443 100644 --- a/src/PromiseInterface.php +++ b/src/PromiseInterface.php @@ -2,6 +2,9 @@ namespace React\Promise; +/** + * @template T + */ interface PromiseInterface { /** @@ -32,10 +35,11 @@ interface PromiseInterface * than once. * 3. `$onProgress` (deprecated) may be called multiple times. * - * @param callable|null $onFulfilled + * @template TReturn of mixed + * @param callable(T): TReturn $onFulfilled * @param callable|null $onRejected * @param callable|null $onProgress This argument is deprecated and should not be used anymore. - * @return PromiseInterface + * @return (TReturn is PromiseInterface ? TReturn : PromiseInterface) */ public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); } diff --git a/src/functions.php b/src/functions.php index 429f0e73..469fedc4 100644 --- a/src/functions.php +++ b/src/functions.php @@ -13,8 +13,9 @@ * * If `$promiseOrValue` is a promise, it will be returned as is. * - * @param mixed $promiseOrValue - * @return PromiseInterface + * @template T + * @param T $promiseOrValue + * @return PromiseInterface */ function resolve($promiseOrValue = null) { diff --git a/types/PromiseInterface.php b/types/PromiseInterface.php new file mode 100644 index 00000000..5fd5e8d8 --- /dev/null +++ b/types/PromiseInterface.php @@ -0,0 +1,9 @@ + $bool; + +assertType('React\Promise\PromiseInterface', resolve(true)); +assertType('React\Promise\PromiseInterface', resolve(true)->then($passThroughBoolFn));