From ce3fc56fe98d68e56e1d37ee6c81a0dab4e78b52 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Mon, 7 Oct 2024 14:28:13 +0200 Subject: [PATCH] Upgrade to PHPStan 2.0 --- composer.json | 18 +++++++------ phpstan-baseline.neon | 27 ++++++++++++++++++- .../ServiceManagerGetMethodCallRule.php | 17 ++++-------- src/ServiceManagerLoader.php | 4 +-- ...ceLocatorGetDynamicReturnTypeExtension.php | 12 +-------- ...PluginMethodDynamicReturnTypeExtension.php | 15 +++-------- tests/LaminasIntegration/IntegrationTest.php | 4 +-- .../data/controllerPluginMethod-10.json | 7 +++++ .../data/serviceManagerDynamicReturn-4.json | 12 +++++++++ .../stdlibAbstractOptionsProperties-2.json | 2 +- .../stdlibAbstractOptionsProperties-4.json | 2 +- .../Laminas/PluginMethodReflectionTest.php | 7 +++-- 12 files changed, 72 insertions(+), 55 deletions(-) create mode 100644 tests/LaminasIntegration/data/controllerPluginMethod-10.json create mode 100644 tests/LaminasIntegration/data/serviceManagerDynamicReturn-4.json diff --git a/composer.json b/composer.json index 9cc7476..06edd69 100644 --- a/composer.json +++ b/composer.json @@ -19,25 +19,27 @@ ], "require": { "php": "~8.2.0 || ~8.3.0", - "phpstan/phpstan": "^1.12.4" + "phpstan/phpstan": "^2.0.1" }, "require-dev": { "laminas/laminas-cache": "^3.12.2", "laminas/laminas-cache-storage-adapter-memory": "^2.3.0", - "laminas/laminas-filter": "^2.37.0", - "laminas/laminas-form": "^3.20.1", + "laminas/laminas-filter": "^2.39.0", + "laminas/laminas-form": "^3.21.0", "laminas/laminas-hydrator": "^4.15.0", - "laminas/laminas-i18n": "^2.28.1", + "laminas/laminas-i18n": "^2.29.0", "laminas/laminas-inputfilter": "^2.30.1", "laminas/laminas-mail": "^2.25.1", "laminas/laminas-mvc": "^3.7.0", - "laminas/laminas-paginator": "^2.18.1", + "laminas/laminas-paginator": "^2.19.0", "laminas/laminas-validator": "^2.64.1", - "phpstan/phpstan-deprecation-rules": "^1.2.1", - "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-phpunit": "^2", "phpunit/phpunit": "^9.6.21", - "slam/php-cs-fixer-extensions": "^3.11.1" + "slam/php-cs-fixer-extensions": "^3.12.0" }, + "minimum-stability": "dev", + "prefer-stable": true, "conflict": { "laminas/laminas-cache": "<3.12", "laminas/laminas-filter": "<2.37", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6eb6b60..ec89e43 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,6 +1,31 @@ parameters: ignoreErrors: - - message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with 'LaminasPhpStan\\\\\\\\TestAsset\\\\\\\\BarService' and Laminas\\\\Stdlib\\\\DispatchableInterface will always evaluate to false\\.$#" + message: '#^Creating new ReflectionClass is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine\. Use objects retrieved from ReflectionProvider instead\.$#' + identifier: phpstanApi.runtimeReflection + count: 1 + path: src/Rules/Laminas/ServiceManagerGetMethodCallRule.php + + - + message: '#^Parameter \#1 \$config of method Laminas\\ServiceManager\\ServiceManager\:\:configure\(\) expects array\{abstract_factories\?\: array\\|Laminas\\ServiceManager\\Factory\\AbstractFactoryInterface\>, aliases\?\: array\, delegators\?\: mixed, factories\?\: mixed, initializers\?\: mixed, invokables\?\: array\, lazy_services\?\: array\{class_map\?\: array\, proxies_namespace\?\: non\-empty\-string, proxies_target_dir\?\: non\-empty\-string, write_proxy_files\?\: bool\}, services\?\: array\\|object\>, \.\.\.\}, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/ServiceManagerLoader.php + + - + message: '#^Parameter \#1 \$config of method Laminas\\ServiceManager\\ServiceManager\:\:configure\(\) expects array\{abstract_factories\?\: array\\|Laminas\\ServiceManager\\Factory\\AbstractFactoryInterface\>, aliases\?\: array\, delegators\?\: mixed, factories\?\: mixed, initializers\?\: mixed, invokables\?\: array\, lazy_services\?\: array\{class_map\?\: array\, proxies_namespace\?\: non\-empty\-string, proxies_target_dir\?\: non\-empty\-string, write_proxy_files\?\: bool\}, services\?\: array\\|object\>, \.\.\.\}, non\-empty\-array given\.$#' + identifier: argument.type + count: 1 + path: src/ServiceManagerLoader.php + + - + message: '#^Creating new ReflectionClass is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine\. Use objects retrieved from ReflectionProvider instead\.$#' + identifier: phpstanApi.runtimeReflection + count: 2 + path: src/Type/Laminas/AbstractServiceLocatorGetDynamicReturnTypeExtension.php + + - + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''LaminasPhpStan\\\\TestAsset\\\\BarService'' and Laminas\\Stdlib\\DispatchableInterface will always evaluate to false\.$#' + identifier: staticMethod.impossibleType count: 1 path: tests/Type/Laminas/ServiceManagerLoaderTest.php diff --git a/src/Rules/Laminas/ServiceManagerGetMethodCallRule.php b/src/Rules/Laminas/ServiceManagerGetMethodCallRule.php index 0227977..e450702 100644 --- a/src/Rules/Laminas/ServiceManagerGetMethodCallRule.php +++ b/src/Rules/Laminas/ServiceManagerGetMethodCallRule.php @@ -10,11 +10,11 @@ use LaminasPhpStan\ServiceManagerLoader; use LaminasPhpStan\Type\Laminas\ObjectServiceManagerType; use PhpParser\Node; -use PhpParser\Node\Arg; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use Psr\Container\ContainerInterface as PsrContainerInterface; @@ -35,11 +35,7 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @param MethodCall $node - * - * @return string[] - */ + /** @param MethodCall $node */ public function processNode(Node $node, Scope $scope): array { $args = $node->getArgs(); @@ -47,10 +43,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - $firstArg = $args[0]; - if (! $firstArg instanceof Arg) { - return []; - } + $firstArg = $args[0]; $argType = $scope->getType($firstArg->value); $constantStrings = $argType->getConstantStrings(); if (1 !== \count($constantStrings)) { @@ -92,14 +85,14 @@ public function processNode(Node $node, Scope $scope): array } } - return [\sprintf( + return [RuleErrorBuilder::message(\sprintf( 'The service "%s" was not configured in %s%s.', $serviceName, $calledOnType instanceof ObjectServiceManagerType ? $calledOnType->getServiceName() : $calledOnType->getClassName(), $classDoesNotExistNote - )]; + ))->identifier('servicemanager.servicenotconfigured')->build()]; } /** @phpstan-assert-if-true ObjectType $type */ diff --git a/src/ServiceManagerLoader.php b/src/ServiceManagerLoader.php index 0a42872..fcce7c2 100644 --- a/src/ServiceManagerLoader.php +++ b/src/ServiceManagerLoader.php @@ -67,12 +67,10 @@ public function getServiceLocator(string $serviceManagerName): ServiceLocatorInt } if (\class_exists(ServiceListenerFactory::class)) { $refProp = new ReflectionProperty(ServiceListenerFactory::class, 'defaultServiceConfig'); - $refProp->setAccessible(true); - $config = $refProp->getValue(new ServiceListenerFactory()); + $config = $refProp->getValue(new ServiceListenerFactory()); \assert(\is_array($config)); \assert(\is_array($config['factories'])); unset($config['factories']['config']); - $refProp->setAccessible(false); $serviceManager->configure($config); } foreach ($this->knownModules as $module) { diff --git a/src/Type/Laminas/AbstractServiceLocatorGetDynamicReturnTypeExtension.php b/src/Type/Laminas/AbstractServiceLocatorGetDynamicReturnTypeExtension.php index d078e98..cf0efb0 100644 --- a/src/Type/Laminas/AbstractServiceLocatorGetDynamicReturnTypeExtension.php +++ b/src/Type/Laminas/AbstractServiceLocatorGetDynamicReturnTypeExtension.php @@ -6,12 +6,10 @@ use Laminas\ServiceManager\AbstractPluginManager; use LaminasPhpStan\ServiceManagerLoader; -use PhpParser\Node\Arg; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ReflectionProvider; -use PHPStan\ShouldNotHappenException; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\MixedType; use PHPStan\Type\NeverType; @@ -48,15 +46,7 @@ final public function getTypeFromMethodCall( $serviceManager = $this->serviceManagerLoader->getServiceLocator($calledOnType->getObjectClassNames()[0]); - $firstArg = $args[0]; - if (! $firstArg instanceof Arg) { - throw new ShouldNotHappenException(\sprintf( - 'Argument passed to %s::%s should be a string, %s given', - $methodReflection->getDeclaringClass()->getName(), - $methodReflection->getName(), - $firstArg->getType() - )); - } + $firstArg = $args[0]; $argType = $scope->getType($firstArg->value); $constantStringTypes = $argType->getConstantStrings(); if (1 !== \count($constantStringTypes)) { diff --git a/src/Type/Laminas/PluginMethodDynamicReturnTypeExtension/AbstractPluginMethodDynamicReturnTypeExtension.php b/src/Type/Laminas/PluginMethodDynamicReturnTypeExtension/AbstractPluginMethodDynamicReturnTypeExtension.php index 806fa27..1397db4 100644 --- a/src/Type/Laminas/PluginMethodDynamicReturnTypeExtension/AbstractPluginMethodDynamicReturnTypeExtension.php +++ b/src/Type/Laminas/PluginMethodDynamicReturnTypeExtension/AbstractPluginMethodDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ namespace LaminasPhpStan\Type\Laminas\PluginMethodDynamicReturnTypeExtension; use LaminasPhpStan\ServiceManagerLoader; -use PhpParser\Node\Arg; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; @@ -36,17 +35,9 @@ final public function getTypeFromMethodCall( Scope $scope ): Type { $firstArg = $methodCall->getArgs()[0]; - if (! $firstArg instanceof Arg) { - throw new ShouldNotHappenException(\sprintf( - 'Argument passed to %s::%s should be a string, %s given', - $methodReflection->getDeclaringClass()->getName(), - $methodReflection->getName(), - $firstArg->getType() - )); - } - $argType = $scope->getType($firstArg->value); - $strings = $argType->getConstantStrings(); - $plugin = 1 === \count($strings) ? $strings[0]->getValue() : null; + $argType = $scope->getType($firstArg->value); + $strings = $argType->getConstantStrings(); + $plugin = 1 === \count($strings) ? $strings[0]->getValue() : null; if (null !== $plugin) { $pluginManager = $this->serviceManagerLoader->getServiceLocator($this->getPluginManagerName()); diff --git a/tests/LaminasIntegration/IntegrationTest.php b/tests/LaminasIntegration/IntegrationTest.php index b3daaac..fc2d8b7 100644 --- a/tests/LaminasIntegration/IntegrationTest.php +++ b/tests/LaminasIntegration/IntegrationTest.php @@ -11,7 +11,7 @@ */ final class IntegrationTest extends LevelsTestCase { - public function dataTopics(): array + public static function dataTopics(): array { return [ ['serviceManagerDynamicReturn'], @@ -36,7 +36,7 @@ public function getPhpStanExecutablePath(): string return __DIR__ . '/../../vendor/bin/phpstan'; } - public function getPhpStanConfigPath(): ?string + public function getPhpStanConfigPath(): string { return __DIR__ . '/phpstan.neon'; } diff --git a/tests/LaminasIntegration/data/controllerPluginMethod-10.json b/tests/LaminasIntegration/data/controllerPluginMethod-10.json new file mode 100644 index 0000000..42810ea --- /dev/null +++ b/tests/LaminasIntegration/data/controllerPluginMethod-10.json @@ -0,0 +1,7 @@ +[ + { + "message": "Cannot call method isFoo() on mixed.", + "line": 16, + "ignorable": true + } +] \ No newline at end of file diff --git a/tests/LaminasIntegration/data/serviceManagerDynamicReturn-4.json b/tests/LaminasIntegration/data/serviceManagerDynamicReturn-4.json new file mode 100644 index 0000000..ffeca68 --- /dev/null +++ b/tests/LaminasIntegration/data/serviceManagerDynamicReturn-4.json @@ -0,0 +1,12 @@ +[ + { + "message": "Expression \"(static function (\\LaminasPhpStan\\TestAsset\\FooService $fooService): void {…\" on a separate line does not do anything.", + "line": 52, + "ignorable": true + }, + { + "message": "Expression \"(static function (\\LaminasPhpStan\\TestAsset\\FooInterface $fooService): void {…\" on a separate line does not do anything.", + "line": 55, + "ignorable": true + } +] \ No newline at end of file diff --git a/tests/LaminasIntegration/data/stdlibAbstractOptionsProperties-2.json b/tests/LaminasIntegration/data/stdlibAbstractOptionsProperties-2.json index 97f9a32..0953b86 100644 --- a/tests/LaminasIntegration/data/stdlibAbstractOptionsProperties-2.json +++ b/tests/LaminasIntegration/data/stdlibAbstractOptionsProperties-2.json @@ -5,7 +5,7 @@ "ignorable": true }, { - "message": "Property class@anonymous/stdlibAbstractOptionsProperties.php:27::$myxyz is not writable.", + "message": "Property Laminas\\Stdlib\\AbstractOptions@anonymous/stdlibAbstractOptionsProperties.php:27::$myxyz is not writable.", "line": 37, "ignorable": true }, diff --git a/tests/LaminasIntegration/data/stdlibAbstractOptionsProperties-4.json b/tests/LaminasIntegration/data/stdlibAbstractOptionsProperties-4.json index 8d68f89..b62603f 100644 --- a/tests/LaminasIntegration/data/stdlibAbstractOptionsProperties-4.json +++ b/tests/LaminasIntegration/data/stdlibAbstractOptionsProperties-4.json @@ -1,6 +1,6 @@ [ { - "message": "Property class@anonymous/stdlibAbstractOptionsProperties.php:27::$myxyz is never read, only written.", + "message": "Property Laminas\\Stdlib\\AbstractOptions@anonymous/stdlibAbstractOptionsProperties.php:27::$myxyz is never read, only written.", "line": 28, "ignorable": true } diff --git a/tests/Type/Laminas/PluginMethodReflectionTest.php b/tests/Type/Laminas/PluginMethodReflectionTest.php index 33b530f..d8d94e1 100644 --- a/tests/Type/Laminas/PluginMethodReflectionTest.php +++ b/tests/Type/Laminas/PluginMethodReflectionTest.php @@ -5,21 +5,20 @@ namespace LaminasPhpStan\Tests\Type\Laminas; use LaminasPhpStan\Type\Laminas\PluginMethodReflection; -use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\FunctionVariant; +use PHPStan\Testing\PHPStanTestCase; use PHPStan\TrinaryLogic; use PHPStan\Type\ObjectType; -use PHPUnit\Framework\TestCase; use stdClass; /** * @covers \LaminasPhpStan\Type\Laminas\PluginMethodReflection */ -final class PluginMethodReflectionTest extends TestCase +final class PluginMethodReflectionTest extends PHPStanTestCase { public function testTrivialUsage(): void { - $declaringClass = $this->createMock(ClassReflection::class); + $declaringClass = $this->createReflectionProvider()->getClass(stdClass::class); $methodName = 'redirect'; $returnType = new ObjectType(stdClass::class);