Skip to content

Commit

Permalink
PHPStan 2.0 (#17)
Browse files Browse the repository at this point in the history
* PHPStan 2.0

* Remove PHP 7.1 from github actions

* remove old version mabe enum support

* drop support for old phpunit & fix github actions
  • Loading branch information
olekans authored Nov 28, 2024
1 parent 63d391c commit 79ea7e3
Show file tree
Hide file tree
Showing 14 changed files with 85 additions and 115 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
php-version:
- "7.4"
- "8.0"
- "8.1"
- "8.2"
- "8.3"
- "8.4"

steps:
- name: "Checkout"
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/tests-code-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@ jobs:
fail-fast: false
matrix:
php-version:
- "7.2"
- "7.3"
- "7.4"
- "8.0"
- "8.1"

steps:
- name: "Checkout"
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ jobs:
fail-fast: false
matrix:
php-version:
- "7.1"
- "8.1"
- "8.2"
- "8.3"
- "8.4"

steps:
- name: "Checkout"
Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
"keywords": ["enum", "phpstan"],
"license": "BSD-3-Clause",
"require": {
"php": "^7.1 | ^8.0",
"marc-mabe/php-enum": "^1.1 || ^2.0 || ^3.0 || ^4.0",
"phpstan/phpstan": "^1.0"
"php": "^7.4 | ^8.0",
"marc-mabe/php-enum": "^4.0",
"phpstan/phpstan": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5 | ^8.5 | 9.4"
"phpunit/phpunit": "^9.0"
},
"autoload": {
"psr-4": {
Expand Down
28 changes: 17 additions & 11 deletions src/EnumDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,36 @@ class EnumDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExt
*
* @var array<string, callable>
*/
private $objectMethods = [];
private array $objectMethods = [];

/**
* Map supported static method to a callable function detecting return type
*
* @var array<string, callable>
*/
private $staticMethods = [];
private array $staticMethods = [];

/**
* Buffer of all types of enumeration values
* @phpstan-var array<class-string<Enum>, Type[]>
*/
private $enumValueTypesBuffer = [];
private array $enumValueTypesBuffer = [];

/**
* Buffer of all types of enumeration ordinals
* @phpstan-var array<class-string<Enum>, Type[]>
*/
private $enumOrdinalTypesBuffer = [];
private array $enumOrdinalTypesBuffer = [];

public function __construct()
{
$this->objectMethods['getvalue'] = function (string $class) {
return $this->detectGetValueReturnType($class);
};

if (method_exists(Enum::class, 'getvalues')) {
$this->staticMethods['getvalues'] = function (string $class) {
return $this->detectGetValuesReturnType($class);
};
}
$this->staticMethods['getvalues'] = function (string $class) {
return $this->detectGetValuesReturnType($class);
};

// static methods can be called like object methods
$this->objectMethods = array_merge($this->objectMethods, $this->staticMethods);
Expand Down Expand Up @@ -87,15 +85,23 @@ public function getTypeFromStaticMethodCall(
// The call class is not a name
// E.g. an expression on $enumClass::getValues()
if (!$callClass instanceof Name) {
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
return ParametersAcceptorSelector::selectFromArgs(
$scope,
$staticCall->getArgs(),
$methodReflection->getVariants()
)->getReturnType();
}

$callClassName = $callClass->toString();

// Can't detect possible types on static::*()
// as it depends on defined enumerators of unknown inherited classes
if ($callClassName === 'static') {
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
return ParametersAcceptorSelector::selectFromArgs(
$scope,
$staticCall->getArgs(),
$methodReflection->getVariants()
)->getReturnType();
}

if ($callClassName === 'self') {
Expand Down
12 changes: 3 additions & 9 deletions src/EnumMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,9 @@

class EnumMethodReflection implements MethodReflection
{
/**
* @var ClassReflection
*/
private $classReflection;

/**
* @var string
*/
private $name;
private ClassReflection $classReflection;

private string $name;

public function __construct(ClassReflection $classReflection, string $name)
{
Expand Down
1 change: 0 additions & 1 deletion src/EnumMethodsClassReflectionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public function hasMethod(ClassReflection $classReflection, string $methodName):
return false;
}

/** @var string|Enum $class */
$class = $classReflection->getName();
return array_key_exists($methodName, $class::getConstants());
}
Expand Down
15 changes: 4 additions & 11 deletions tests/integration/IntegrationTest.php
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
<?php declare(strict_types = 1);
<?php declare(strict_types=1);

namespace MabeEnum\PHPStan\tests\integration;

use MabeEnum\Enum;
use PHPStan\Testing\LevelsTestCase;

final class IntegrationTest extends LevelsTestCase
{

/**
* @return string[][]
*/
public function dataTopics(): array
public static function dataTopics(): array
{
$dataTopics = [
return [
['EnumMethodsClassReflection'],
['EnumGetValueReturnType'],
['EnumGetValuesReturnType'],
];

if (method_exists(Enum::class, 'getValues')) {
$dataTopics[] = ['EnumGetValuesReturnType'];
}

return $dataTopics;
}

public function getDataPath(): string
Expand Down
15 changes: 15 additions & 0 deletions tests/integration/data/EnumGetValuesReturnType-3.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
[
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::staticBaseExprMethodFail() should return array<int, null> but returns array<int, array<int|string, mixed>|bool|float|int|string|null>.",
"line": 21,
"ignorable": true
},
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\Example::staticMethodFail() should return array<int, null> but returns array<int, float|int|string>.",
"line": 40,
Expand All @@ -14,9 +19,19 @@
"line": 70,
"ignorable": true
},
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyEnum::staticGetValuesFail() should return array<int, null> but returns array<int, array<int|string, mixed>|bool|float|int|string|null>.",
"line": 76,
"ignorable": true
},
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritSelfGetValuesFail() should return array<int, null> but returns array<int, float|int|string>.",
"line": 93,
"ignorable": true
},
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumGetValuesReturnType\\MyInheritedEnum::inheritStaticGetValuesFail() should return array<int, null> but returns array<int, array<int|string, mixed>|bool|float|int|string|null>.",
"line": 99,
"ignorable": true
}
]
17 changes: 0 additions & 17 deletions tests/integration/data/EnumGetValuesReturnType-7.json

This file was deleted.

7 changes: 7 additions & 0 deletions tests/integration/data/EnumMethodsClassReflection-10.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"message": "Method MabeEnum\\PHPStan\\tests\\integration\\data\\EnumMethodsClassReflection\\Example::fail() should return MabeEnum\\PHPStan\\tests\\integration\\data\\EnumMethodsClassReflection\\MyEnum but returns mixed.",
"line": 16,
"ignorable": true
}
]
13 changes: 2 additions & 11 deletions tests/unit/EnumDynamicReturnTypeExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@

class EnumDynamicReturnTypeExtensionTest extends PHPStanTestCase
{
/**
* @var EnumDynamicReturnTypeExtension
*/
protected $extension;
protected EnumDynamicReturnTypeExtension $extension;

public function setUp(): void
{
Expand Down Expand Up @@ -72,13 +69,7 @@ public function testIsMethodSupportedShouldReturnFalse(): void

public function staticMethodsProvider(): array
{
$staticMethods = [];

if (method_exists(Enum::class, 'getValues')) {
$staticMethods[] = ['getValues'];
}

return $staticMethods;
return [['getValues']];
}

public function objectMethodsProvider(): array
Expand Down
60 changes: 22 additions & 38 deletions tests/unit/EnumMethodReflectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,15 @@
use MabeEnum\PHPStan\tests\assets\DocCommentEnum;
use MabeEnum\PHPStan\tests\assets\VisibilityEnum;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Testing\PHPStanTestCase;
use PHPStan\Type\VerbosityLevel;
use PHPStan\Analyser\Scope;

class EnumMethodReflectionTest extends PHPStanTestCase
{
/**
* @var \PHPStan\Reflection\ReflectionProvider
*/
protected $reflectionProvider;

/**
* @var EnumMethodsClassReflectionExtension
*/
protected $reflectionExtension;
protected ReflectionProvider $reflectionProvider;
protected EnumMethodsClassReflectionExtension $reflectionExtension;

public function setUp(): void
{
Expand All @@ -30,59 +25,68 @@ public function setUp(): void

public function testGetName(): void
{
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR');

$this->assertSame('STR', $methodReflection->getName());
}

public function testGetDeclaringClass(): void
{
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR');

$this->assertSame($classReflection, $methodReflection->getDeclaringClass());
}

public function testShouldBeStatic(): void
{
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR');

$this->assertTrue($methodReflection->isStatic());
}

public function testShouldNotBePrivate(): void
{
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR');

$this->assertFalse($methodReflection->isPrivate());
}

public function testShouldBePublic(): void
{
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR');

$this->assertTrue($methodReflection->isPublic());
}

public function testGetPrototype(): void
{
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR');

$this->assertSame($methodReflection, $methodReflection->getPrototype());
}

public function testGetVariants(): void
{
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$classReflection = $this->reflectionProvider->getClass(VisibilityEnum::class);
$methodReflection = $this->reflectionExtension->getMethod($classReflection, 'STR');
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());

$this->assertSame(VisibilityEnum::class, $parametersAcceptor->getReturnType()->describe(VerbosityLevel::value()));
$scope = $this->createMock(Scope::class);
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
$scope,
[],
$methodReflection->getVariants()
);

$this->assertSame(
VisibilityEnum::class,
$parametersAcceptor->getReturnType()->describe(VerbosityLevel::value())
);
}

public function testGetDocComment(): void
Expand Down Expand Up @@ -152,24 +156,4 @@ public function testHasSideEffects(): void

$this->assertTrue($methodReflection->hasSideEffects()->no());
}

public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void
{
if (method_exists(parent::class, 'assertMatchesRegularExpression')) {
parent::assertMatchesRegularExpression($pattern, $string, $message);
return;
}

self::assertRegExp($pattern, $string, $message);
}

public static function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void
{
if (method_exists(parent::class, 'assertDoesNotMatchRegularExpression')) {
parent::assertDoesNotMatchRegularExpression($pattern, $string, $message);
return;
}

self::assertNotRegExp($pattern, $string, $message);
}
}
Loading

0 comments on commit 79ea7e3

Please sign in to comment.