Skip to content

Commit db6a2a8

Browse files
committed
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
1 parent 616601d commit db6a2a8

File tree

8 files changed

+72
-29
lines changed

8 files changed

+72
-29
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
.travis.yml export-ignore
88
tests/ export-ignore
99
phpunit.xml export-ignore
10+
static-analysis/ export-ignore

.travis.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
language: php
22

33
php:
4-
- '7.1'
5-
- '7.2'
64
- '7.3'
75
- '7.4'
6+
- '8.0'
87

98
matrix:
109
fast_finish: true

composer.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
}
2323
},
2424
"require": {
25-
"php": ">=7.1",
25+
"php": "^7.3 || ^8.0",
2626
"ext-json": "*"
2727
},
2828
"require-dev": {
29-
"phpunit/phpunit": "^7",
29+
"phpunit/phpunit": "^9.5",
3030
"squizlabs/php_codesniffer": "1.*",
31-
"vimeo/psalm": "^3.8"
31+
"vimeo/psalm": "^4.5.1"
3232
}
3333
}

phpunit.xml

+6-13
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<!--
3-
phpunit -c phpunit.xml
4-
-->
5-
<phpunit backupGlobals="false"
6-
backupStaticAttributes="false"
7-
colors="true"
8-
convertErrorsToExceptions="true"
9-
convertNoticesToExceptions="true"
10-
convertWarningsToExceptions="true"
11-
processIsolation="false"
12-
stopOnFailure="false"
13-
syntaxCheck="false"
14-
bootstrap="./tests/bootstrap.php">
2+
<phpunit
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
5+
colors="true"
6+
bootstrap="./tests/bootstrap.php"
7+
>
158
<testsuites>
169
<testsuite name="PHP Enum Test Suite">
1710
<directory suffix=".php">./tests</directory>

psalm.xml

+15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
>
99
<projectFiles>
1010
<directory name="src" />
11+
<directory name="static-analysis" />
1112
<ignoreFiles>
1213
<directory name="vendor" />
1314
<directory name="src/PHPUnit" />
@@ -16,5 +17,19 @@
1617

1718
<issueHandlers>
1819
<MixedAssignment errorLevel="info" />
20+
21+
<ImpureStaticProperty>
22+
<!-- self::$... usages in Enum are used to populate an internal cache, and cause no side-effects -->
23+
<errorLevel type="suppress">
24+
<file name="src/Enum.php"/>
25+
</errorLevel>
26+
</ImpureStaticProperty>
27+
28+
<ImpureVariable>
29+
<!-- $this usages in Enum point themselves to an immutable instance -->
30+
<errorLevel type="suppress">
31+
<file name="src/Enum.php"/>
32+
</errorLevel>
33+
</ImpureVariable>
1934
</issueHandlers>
2035
</psalm>

src/Enum.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*
1818
* @psalm-template T
1919
* @psalm-immutable
20+
* @psalm-consistent-constructor
2021
*/
2122
abstract class Enum implements \JsonSerializable
2223
{
@@ -51,7 +52,7 @@ abstract class Enum implements \JsonSerializable
5152
* @psalm-pure
5253
* @param mixed $value
5354
*
54-
* @psalm-param static<T>|T $value
55+
* @psalm-param T $value
5556
* @throws \UnexpectedValueException if incompatible type is given.
5657
*/
5758
public function __construct($value)
@@ -162,7 +163,9 @@ public static function toArray()
162163
$class = static::class;
163164

164165
if (!isset(static::$cache[$class])) {
166+
/** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
165167
$reflection = new \ReflectionClass($class);
168+
/** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
166169
static::$cache[$class] = $reflection->getConstants();
167170
}
168171

@@ -219,6 +222,8 @@ public static function search($value)
219222
*
220223
* @return static
221224
* @throws \BadMethodCallException
225+
*
226+
* @psalm-pure
222227
*/
223228
public static function __callStatic($name, $arguments)
224229
{

static-analysis/EnumIsPure.php

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MyCLabs\Tests\Enum\StaticAnalysis;
6+
7+
use MyCLabs\Enum\Enum;
8+
9+
/**
10+
* @method static PureEnum A()
11+
* @method static PureEnum C()
12+
*
13+
* @psalm-immutable
14+
* @psalm-template T of 'A'|'B'
15+
* @template-extends Enum<T>
16+
*/
17+
final class PureEnum extends Enum
18+
{
19+
const A = 'A';
20+
const C = 'C';
21+
}
22+
23+
/** @psalm-pure */
24+
function enumFetchViaMagicMethodIsPure(): PureEnum
25+
{
26+
return PureEnum::A();
27+
}
28+
29+
/** @psalm-pure */
30+
function enumFetchViaExplicitMagicCallIsPure(): PureEnum
31+
{
32+
return PureEnum::__callStatic('A', []);
33+
}

tests/EnumTest.php

+7-10
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,12 @@ public function testGetKey()
3838
$this->assertNotEquals('BA', $value->getKey());
3939
}
4040

41-
/**
42-
* @dataProvider invalidValueProvider
43-
* @expectedException \UnexpectedValueException
44-
* @expectedExceptionMessage is not part of the enum MyCLabs\Tests\Enum\EnumFixture
45-
*/
41+
/** @dataProvider invalidValueProvider */
4642
public function testCreatingEnumWithInvalidValue($value)
4743
{
44+
$this->expectException(\UnexpectedValueException::class);
45+
$this->expectExceptionMessage('is not part of the enum ' . EnumFixture::class);
46+
4847
new EnumFixture($value);
4948
}
5049

@@ -146,13 +145,11 @@ public function testStaticAccess()
146145
$this->assertNotSame(EnumFixture::NUMBER(), EnumFixture::NUMBER());
147146
}
148147

149-
/**
150-
* @expectedException \BadMethodCallException
151-
* @expectedExceptionMessage No static method or enum constant 'UNKNOWN' in class
152-
* UnitTest\MyCLabs\Enum\Enum\EnumFixture
153-
*/
154148
public function testBadStaticAccess()
155149
{
150+
$this->expectException(\BadMethodCallException::class);
151+
$this->expectExceptionMessage('No static method or enum constant \'UNKNOWN\' in class ' . EnumFixture::class);
152+
156153
EnumFixture::UNKNOWN();
157154
}
158155

0 commit comments

Comments
 (0)