Skip to content

Commit fba3870

Browse files
authored
Generics + better doc types PHPStan/psalm (#138)
* introduce phpstan analizing and document generics * phpstan 0.12.7 * rebase fixes + PHPStan update * fixes
1 parent 5c048f2 commit fba3870

30 files changed

+609
-332
lines changed

.travis.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,14 @@ script:
5353
php -d 'zend.assertions=1' vendor/bin/phpunit --verbose;
5454
fi
5555

56+
# run phpstan
57+
- php vendor/bin/phpstan analyse --level max src/ tests/
58+
59+
# run psalm
60+
- vendor/bin/psalm
61+
5662
# run benchmarks to make sure they are working fine
5763
- php vendor/bin/phpbench run --no-interaction --revs=1 --retry-threshold=100
58-
- vendor/bin/psalm
5964

6065
after_script:
6166
- if [ "${CODE_COVERAGE}" == "1" ]; then

composer.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
"ext-reflection": "*"
2222
},
2323
"require-dev": {
24-
"phpunit/phpunit": "^7.0",
25-
"phpbench/phpbench": "^0.16.1",
26-
"vimeo/psalm": "^3.10"
24+
"phpbench/phpbench": "^0.16.10",
25+
"phpstan/phpstan": "^0.12.19",
26+
"phpunit/phpunit": "^7.5.20",
27+
"vimeo/psalm": "^3.11.2"
2728
},
2829
"autoload": {
2930
"psr-4": {
@@ -33,6 +34,7 @@
3334
"autoload-dev": {
3435
"psr-4": {
3536
"MabeEnumTest\\": "tests/MabeEnumTest/",
37+
"MabeEnumStaticAnalysis\\": "tests/MabeEnumStaticAnalysis/",
3638
"MabeEnumBench\\": "bench/"
3739
}
3840
},

phpstan.neon.dist

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
parameters:
2+
ignoreErrors:
3+
# EnumSet internally handles a bitset to be either an integer or a string
4+
- message: '#Binary operation "[\|\&\^]" between int\|string and int\|string results in an error#'
5+
path: %currentWorkingDirectory%/src/EnumSet.php
6+
7+
# EnumSerializableTrait
8+
- message: '#Access to private property \$[a-z]+ of parent class MabeEnum\\Enum#'
9+
path: %currentWorkingDirectory%/src/EnumSerializableTrait.php
10+
- message: '#Access to an undefined static property MabeEnumTest\\TestAsset\\SerializableEnum::\$instances#'
11+
path: %currentWorkingDirectory%/src/EnumSerializableTrait.php
12+
13+
# Tests
14+
- message: '#Parameter \#\d \$[a-z]* of static method MabeEnum\\Enum::[^ ]* expects [^ ]*, .+ given#'
15+
path: %currentWorkingDirectory%/tests/
16+
- message: '#Parameter \#\d \$[a-z]* of method MabeEnum\\EnumSet<[^ ]*>::[^ ]* expects [^ ]*, .+ given#'
17+
path: %currentWorkingDirectory%/tests/
18+
- message: '#Parameter \#\d \$[a-z]* of class MabeEnum\\Enum(Set|Map) constructor expects class-string<T of MabeEnum\\Enum>, string given#'
19+
path: %currentWorkingDirectory%/tests/

phpunit.xml.dist

-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@
77
stopOnFailure="false"
88
processIsolation="false"
99
backupGlobals="false"
10-
syntaxCheck="true"
1110
beStrictAboutTestsThatDoNotTestAnything="true"
1211
beStrictAboutOutputDuringTests="true"
13-
beStrictAboutTestSize="true"
1412
beStrictAboutCoversAnnotation="true"
1513
>
1614
<testsuite name="php-enum Test-Suite">

src/Enum.php

+56-25
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ abstract class Enum
2222
/**
2323
* The selected enumerator value
2424
*
25-
* @var null|bool|int|float|string|array
25+
* @var null|bool|int|float|string|array<mixed>
2626
*/
2727
private $value;
2828

@@ -36,29 +36,29 @@ abstract class Enum
3636
/**
3737
* A map of enumerator names and values by enumeration class
3838
*
39-
* @var array ["$class" => ["$name" => $value, ...], ...]
39+
* @var array<class-string<Enum>, array<string, null|bool|int|float|string|array<mixed>>>
4040
*/
4141
private static $constants = [];
4242

4343
/**
4444
* A List of available enumerator names by enumeration class
4545
*
46-
* @var array ["$class" => ["$name0", ...], ...]
46+
* @var array<class-string<Enum>, string[]>
4747
*/
4848
private static $names = [];
4949

5050
/**
51-
* Already instantiated enumerators
51+
* A map of enumerator names and instances by enumeration class
5252
*
53-
* @var array ["$class" => ["$name" => $instance, ...], ...]
53+
* @var array<class-string<Enum>, array<string, Enum>>
5454
*/
5555
private static $instances = [];
5656

5757
/**
5858
* Constructor
5959
*
60-
* @param null|bool|int|float|string|array $value The value of the enumerator
61-
* @param int|null $ordinal The ordinal number of the enumerator
60+
* @param null|bool|int|float|string|array<mixed> $value The value of the enumerator
61+
* @param int|null $ordinal The ordinal number of the enumerator
6262
*/
6363
final private function __construct($value, $ordinal = null)
6464
{
@@ -111,7 +111,7 @@ final public function __wakeup()
111111
/**
112112
* Get the value of the enumerator
113113
*
114-
* @return null|bool|int|float|string|array
114+
* @return null|bool|int|float|string|array<mixed>
115115
*/
116116
final public function getValue()
117117
{
@@ -123,6 +123,7 @@ final public function getValue()
123123
*
124124
* @return string
125125
*
126+
* @phpstan-return string
126127
* @psalm-return non-empty-string
127128
*/
128129
final public function getName()
@@ -157,7 +158,7 @@ final public function getOrdinal()
157158
/**
158159
* Compare this enumerator against another and check if it's the same.
159160
*
160-
* @param static|null|bool|int|float|string|array $enumerator An enumerator object or value
161+
* @param static|null|bool|int|float|string|array<mixed> $enumerator An enumerator object or value
161162
* @return bool
162163
*/
163164
final public function is($enumerator)
@@ -174,7 +175,7 @@ final public function is($enumerator)
174175
/**
175176
* Get an enumerator instance of the given enumerator value or instance
176177
*
177-
* @param static|null|bool|int|float|string|array $enumerator An enumerator object or value
178+
* @param static|null|bool|int|float|string|array<mixed> $enumerator An enumerator object or value
178179
* @return static
179180
* @throws InvalidArgumentException On an unknown or invalid value
180181
* @throws LogicException On ambiguous constant values
@@ -183,7 +184,15 @@ final public function is($enumerator)
183184
*/
184185
final public static function get($enumerator)
185186
{
186-
if ($enumerator instanceof static && \get_class($enumerator) === static::class) {
187+
if ($enumerator instanceof static) {
188+
if (\get_class($enumerator) !== static::class) {
189+
throw new InvalidArgumentException(sprintf(
190+
'Invalid value of type %s for enumeration %s',
191+
\get_class($enumerator),
192+
static::class
193+
));
194+
}
195+
187196
return $enumerator;
188197
}
189198

@@ -193,7 +202,7 @@ final public static function get($enumerator)
193202
/**
194203
* Get an enumerator instance by the given value
195204
*
196-
* @param null|bool|int|float|string|array $value Enumerator value
205+
* @param null|bool|int|float|string|array<mixed> $value Enumerator value
197206
* @return static
198207
* @throws InvalidArgumentException On an unknown or invalid value
199208
* @throws LogicException On ambiguous constant values
@@ -202,6 +211,8 @@ final public static function get($enumerator)
202211
*/
203212
final public static function byValue($value)
204213
{
214+
/** @var mixed $value */
215+
205216
$constants = self::$constants[static::class] ?? static::getConstants();
206217

207218
$name = \array_search($value, $constants, true);
@@ -215,8 +226,11 @@ final public static function byValue($value)
215226
));
216227
}
217228

218-
return self::$instances[static::class][$name]
229+
/** @var static $instance */
230+
$instance = self::$instances[static::class][$name]
219231
?? self::$instances[static::class][$name] = new static($constants[$name]);
232+
233+
return $instance;
220234
}
221235

222236
/**
@@ -232,7 +246,9 @@ final public static function byValue($value)
232246
final public static function byName(string $name)
233247
{
234248
if (isset(self::$instances[static::class][$name])) {
235-
return self::$instances[static::class][$name];
249+
/** @var static $instance */
250+
$instance = self::$instances[static::class][$name];
251+
return $instance;
236252
}
237253

238254
$const = static::class . "::{$name}";
@@ -271,15 +287,20 @@ final public static function byOrdinal(int $ordinal)
271287
}
272288

273289
$name = self::$names[static::class][$ordinal];
274-
return self::$instances[static::class][$name]
290+
291+
/** @var static $instance */
292+
$instance = self::$instances[static::class][$name]
275293
?? self::$instances[static::class][$name] = new static($constants[$name], $ordinal);
294+
295+
return $instance;
276296
}
277297

278298
/**
279299
* Get a list of enumerator instances ordered by ordinal number
280300
*
281301
* @return static[]
282302
*
303+
* @phpstan-return array<int, static>
283304
* @psalm-return list<static>
284305
* @psalm-pure
285306
*/
@@ -288,14 +309,18 @@ final public static function getEnumerators()
288309
if (!isset(self::$names[static::class])) {
289310
static::getConstants();
290311
}
291-
return \array_map([static::class, 'byName'], self::$names[static::class]);
312+
313+
/** @var callable $byNameFn */
314+
$byNameFn = [static::class, 'byName'];
315+
return \array_map($byNameFn, self::$names[static::class]);
292316
}
293317

294318
/**
295319
* Get a list of enumerator values ordered by ordinal number
296320
*
297-
* @return mixed[]
321+
* @return (null|bool|int|float|string|array)[]
298322
*
323+
* @phpstan-return array<int, null|bool|int|float|string|array>
299324
* @psalm-return list<null|bool|int|float|string|array>
300325
* @psalm-pure
301326
*/
@@ -309,6 +334,7 @@ final public static function getValues()
309334
*
310335
* @return string[]
311336
*
337+
* @phpstan-return array<int, string>
312338
* @psalm-return list<non-empty-string>
313339
* @psalm-pure
314340
*/
@@ -325,6 +351,7 @@ final public static function getNames()
325351
*
326352
* @return int[]
327353
*
354+
* @phpstan-return array<int, int>
328355
* @psalm-return list<int>
329356
* @psalm-pure
330357
*/
@@ -337,9 +364,10 @@ final public static function getOrdinals()
337364
/**
338365
* Get all available constants of the called class
339366
*
340-
* @return mixed[]
367+
* @return (null|bool|int|float|string|array)[]
341368
* @throws LogicException On ambiguous constant values
342369
*
370+
* @phpstan-return array<string, null|bool|int|float|string|array>
343371
* @psalm-return array<non-empty-string, null|bool|int|float|string|array>
344372
* @psalm-pure
345373
*/
@@ -375,7 +403,7 @@ final public static function getConstants()
375403

376404
/**
377405
* Test that the given constants does not contain ambiguous values
378-
* @param array $constants
406+
* @param array<string, null|bool|int|float|string|array<mixed>> $constants
379407
* @return bool
380408
*/
381409
private static function noAmbiguousValues($constants)
@@ -393,21 +421,24 @@ private static function noAmbiguousValues($constants)
393421
/**
394422
* Test if the given enumerator is part of this enumeration
395423
*
396-
* @param static|null|bool|int|float|string|array $enumerator
424+
* @param static|null|bool|int|float|string|array<mixed> $enumerator
397425
* @return bool
398426
*
399427
* @psalm-pure
400428
*/
401429
final public static function has($enumerator)
402430
{
403-
return ($enumerator instanceof static && \get_class($enumerator) === static::class)
404-
|| static::hasValue($enumerator);
431+
if ($enumerator instanceof static) {
432+
return \get_class($enumerator) === static::class;
433+
}
434+
435+
return static::hasValue($enumerator);
405436
}
406437

407438
/**
408439
* Test if the given enumerator value is part of this enumeration
409440
*
410-
* @param null|bool|int|float|string|array $value
441+
* @param null|bool|int|float|string|array<mixed> $value
411442
* @return bool
412443
*
413444
* @psalm-pure
@@ -436,8 +467,8 @@ final public static function hasName(string $name)
436467
* This will be called automatically on calling a method
437468
* with the same name of a defined enumerator.
438469
*
439-
* @param string $method The name of the enumerator (called as method)
440-
* @param array $args There should be no arguments
470+
* @param string $method The name of the enumerator (called as method)
471+
* @param array<mixed> $args There should be no arguments
441472
* @return static
442473
* @throws InvalidArgumentException On an invalid or unknown name
443474
* @throws LogicException On ambiguous constant values

0 commit comments

Comments
 (0)