From db6a2a87422dcde7a13d023d61178be238badeea Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 12 Feb 2021 16:01:52 +0100 Subject: [PATCH] Fix #134 by marking `Enum::__callStatic` as `@psalm-pure` To achieve proper static analysis coverage: * `vimeo/psalm` has been updated * a new `static-analysis` dir has been added (to test static analysis properties **only**) * impure `$this` usages in `Enum` have been ignored in static analysis checks * PHP version support has been upgraded from `>=7.1` to `^7.3 || ^8.0` * PHPUnit version has been upgraded to its latest and greatest Fixes #134 --- .gitattributes | 1 + .travis.yml | 3 +-- composer.json | 6 +++--- phpunit.xml | 19 ++++++------------- psalm.xml | 15 +++++++++++++++ src/Enum.php | 7 ++++++- static-analysis/EnumIsPure.php | 33 +++++++++++++++++++++++++++++++++ tests/EnumTest.php | 17 +++++++---------- 8 files changed, 72 insertions(+), 29 deletions(-) create mode 100644 static-analysis/EnumIsPure.php diff --git a/.gitattributes b/.gitattributes index cd7364e..c821984 100755 --- a/.gitattributes +++ b/.gitattributes @@ -7,3 +7,4 @@ .travis.yml export-ignore tests/ export-ignore phpunit.xml export-ignore +static-analysis/ export-ignore diff --git a/.travis.yml b/.travis.yml index c6ac198..92affbf 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,9 @@ language: php php: - - '7.1' - - '7.2' - '7.3' - '7.4' + - '8.0' matrix: fast_finish: true diff --git a/composer.json b/composer.json index 6861a5c..73caf14 100644 --- a/composer.json +++ b/composer.json @@ -22,12 +22,12 @@ } }, "require": { - "php": ">=7.1", + "php": "^7.3 || ^8.0", "ext-json": "*" }, "require-dev": { - "phpunit/phpunit": "^7", + "phpunit/phpunit": "^9.5", "squizlabs/php_codesniffer": "1.*", - "vimeo/psalm": "^3.8" + "vimeo/psalm": "^4.5.1" } } diff --git a/phpunit.xml b/phpunit.xml index 67a61e1..bd714f1 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,17 +1,10 @@ - - + ./tests diff --git a/psalm.xml b/psalm.xml index b07e929..ff06b66 100644 --- a/psalm.xml +++ b/psalm.xml @@ -8,6 +8,7 @@ > + @@ -16,5 +17,19 @@ + + + + + + + + + + + + + + diff --git a/src/Enum.php b/src/Enum.php index 92a42a4..dc9fd51 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -17,6 +17,7 @@ * * @psalm-template T * @psalm-immutable + * @psalm-consistent-constructor */ abstract class Enum implements \JsonSerializable { @@ -51,7 +52,7 @@ abstract class Enum implements \JsonSerializable * @psalm-pure * @param mixed $value * - * @psalm-param static|T $value + * @psalm-param T $value * @throws \UnexpectedValueException if incompatible type is given. */ public function __construct($value) @@ -162,7 +163,9 @@ public static function toArray() $class = static::class; if (!isset(static::$cache[$class])) { + /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */ $reflection = new \ReflectionClass($class); + /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */ static::$cache[$class] = $reflection->getConstants(); } @@ -219,6 +222,8 @@ public static function search($value) * * @return static * @throws \BadMethodCallException + * + * @psalm-pure */ public static function __callStatic($name, $arguments) { diff --git a/static-analysis/EnumIsPure.php b/static-analysis/EnumIsPure.php new file mode 100644 index 0000000..5875fd8 --- /dev/null +++ b/static-analysis/EnumIsPure.php @@ -0,0 +1,33 @@ + + */ +final class PureEnum extends Enum +{ + const A = 'A'; + const C = 'C'; +} + +/** @psalm-pure */ +function enumFetchViaMagicMethodIsPure(): PureEnum +{ + return PureEnum::A(); +} + +/** @psalm-pure */ +function enumFetchViaExplicitMagicCallIsPure(): PureEnum +{ + return PureEnum::__callStatic('A', []); +} diff --git a/tests/EnumTest.php b/tests/EnumTest.php index cb08d29..90caa48 100755 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -38,13 +38,12 @@ public function testGetKey() $this->assertNotEquals('BA', $value->getKey()); } - /** - * @dataProvider invalidValueProvider - * @expectedException \UnexpectedValueException - * @expectedExceptionMessage is not part of the enum MyCLabs\Tests\Enum\EnumFixture - */ + /** @dataProvider invalidValueProvider */ public function testCreatingEnumWithInvalidValue($value) { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage('is not part of the enum ' . EnumFixture::class); + new EnumFixture($value); } @@ -146,13 +145,11 @@ public function testStaticAccess() $this->assertNotSame(EnumFixture::NUMBER(), EnumFixture::NUMBER()); } - /** - * @expectedException \BadMethodCallException - * @expectedExceptionMessage No static method or enum constant 'UNKNOWN' in class - * UnitTest\MyCLabs\Enum\Enum\EnumFixture - */ public function testBadStaticAccess() { + $this->expectException(\BadMethodCallException::class); + $this->expectExceptionMessage('No static method or enum constant \'UNKNOWN\' in class ' . EnumFixture::class); + EnumFixture::UNKNOWN(); }