From d152ae2f8b88dda3dbd0c618ad4c3385465f4f4d Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 21 Jun 2022 08:42:13 +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/React/Promise/PromiseInterface.php | 6 ++++++ types/PromiseInterface.php | 9 +++++++++ 5 files changed, 36 insertions(+) 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 987c25ef..016d6d5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,3 +44,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/React/Promise/PromiseInterface.php b/src/React/Promise/PromiseInterface.php index 9d23be2e..cfee86bf 100644 --- a/src/React/Promise/PromiseInterface.php +++ b/src/React/Promise/PromiseInterface.php @@ -2,7 +2,13 @@ namespace React\Promise; +/** @template T */ interface PromiseInterface { + /** + * @template TReturn of mixed + * @param callable(T): TReturn $fulfilledHandler + * @return (TReturn is PromiseInterface ? TReturn : PromiseInterface) + */ public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = 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));