From 53dbb1498a28f46dc90d05b05472f2affa42a9d5 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Wed, 30 Oct 2024 21:27:59 -0300 Subject: [PATCH 01/34] PHPStan 2.0 Upgrade - Start with BleedingEdge to fix deprecations and possible new features --- composer.json | 3 ++- phpstan.neon | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f2a3b28..5c687a1 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^10.1", "cakephp/cakephp-codesniffer": "^5.0", - "symplify/phpstan-rules": "^12.4" + "symplify/phpstan-rules": "^12.4", + "phpstan/phpstan-deprecation-rules": "^1.2" }, "extra": { "phpstan": { diff --git a/phpstan.neon b/phpstan.neon index dbd4715..9ba7123 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,11 +1,15 @@ includes: - extension.neon + - phar://phpstan.phar/conf/bleedingEdge.neon + - vendor/phpstan/phpstan-deprecation-rules/rules.neon rules: - Symplify\PHPStanRules\Rules\Explicit\NoMixedPropertyFetcherRule - Symplify\PHPStanRules\Rules\Explicit\NoMixedMethodCallerRule parameters: level: max - checkGenericClassInNonGenericObjectType: false treatPhpDocTypesAsCertain: false cakeDC: disallowEntityArrayAccessRule: true + ignoreErrors: + - + identifier: missingType.generics From eca048a63cc37bc4239b92e894de9749bef9fe84 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Wed, 30 Oct 2024 21:32:11 -0300 Subject: [PATCH 02/34] =?UTF-8?q?PHPStan=202.0=20Upgrade=20-=20Fix=20depre?= =?UTF-8?q?cated=20use=20of=20instanceof=20Type:=20=20Doing=20instanceof?= =?UTF-8?q?=20PHPStan\Type\ArrayType=20is=20error-prone=20and=20deprecated?= =?UTF-8?q?.=20Use=20Type::isArray()=20or=20Type::getArrays()=20instead.?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=F0=9F=92=A1=20Learn=20more:=20ht?= =?UTF-8?q?tps://phpstan.org/blog/why-is-instanceof-type-wrong-and-getting?= =?UTF-8?q?-deprecated=20=20Doing=20instanceof=20PHPStan\Type\IterableType?= =?UTF-8?q?=20is=20error-prone=20and=20deprecated.=20Use=20Type::isIterabl?= =?UTF-8?q?e()=20instead.=20=20=20=20=20=20=20=20=20=20=F0=9F=92=A1=20Lear?= =?UTF-8?q?n=20more:=20https://phpstan.org/blog/why-is-instanceof-type-wro?= =?UTF-8?q?ng-and-getting-deprecated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryFirstArgIsTheReturnTypeExtension.php | 10 +++++++--- tests/test_app/Controller/NotesController.php | 8 +++++++- tests/test_app/Model/Table/NotesTable.php | 9 +++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php index 48c457d..f414364 100644 --- a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php +++ b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php @@ -46,7 +46,7 @@ class RepositoryFirstArgIsTheReturnTypeExtension implements DynamicMethodReturnT /** * @var string */ - private string $className; + protected string $className; /** * @var string @@ -107,8 +107,12 @@ public function getTypeFromMethodCall( return new UnionType($types); } if ($methodReflection->getName() == 'patchEntities') { - if ($type instanceof ArrayType || $type instanceof IterableType) { - return new ArrayType(new IntegerType(), $type->getItemType()); + if (!$type->isIterable()->yes()) { + return $this->getTypeWhenNotFound($methodReflection); + } + $valueType = $type->getIterableValueType(); + if ($valueType->isObject()->yes()) { + return new ArrayType(new IntegerType(), $valueType); } return $this->getTypeWhenNotFound($methodReflection); diff --git a/tests/test_app/Controller/NotesController.php b/tests/test_app/Controller/NotesController.php index a14fb7c..36fe0a7 100644 --- a/tests/test_app/Controller/NotesController.php +++ b/tests/test_app/Controller/NotesController.php @@ -50,7 +50,7 @@ public function add() $findOrCreate = $this->fetchTable()->findOrCreate(['user_id' => 1, 'note' => 'My Note']); Log::info('Accessing note after findOrCreate call' . $findOrCreate->note); - $entities = $this->fetchTable()->newEntities([]); + $entities = $this->fetchTable()->newEntities([['user_id' => 1], ['user_id' => 2]]); foreach ($entities as $newEntity) { $newEntity->note = 'My Empty new entities test'; Log::info('Accessing note after newEntities call' . $newEntity->note); @@ -61,6 +61,12 @@ public function add() $patchedEntity->note = 'My patched entities test'; Log::info('Accessing note after patchEntities call' . $patchedEntity->note); } + $entitiesIterable = $this->fetchTable()->iterableItems(); + $patchedEntitiesIterable = $this->fetchTable()->patchEntities($entitiesIterable, (array)$this->request->getData()); + foreach ($patchedEntitiesIterable as $patchedEntityIt) { + $patchedEntityIt->note = 'My patched entities test'; + Log::info('Accessing note after patchEntities call' . $patchedEntityIt->note); + } $savedEntities = $this->fetchTable()->saveManyOrFail($patchedEntities); foreach ($savedEntities as $savedEntity) { $savedEntity->note = 'My patched saveManyOrFail test'; diff --git a/tests/test_app/Model/Table/NotesTable.php b/tests/test_app/Model/Table/NotesTable.php index e05df87..17af587 100644 --- a/tests/test_app/Model/Table/NotesTable.php +++ b/tests/test_app/Model/Table/NotesTable.php @@ -13,6 +13,7 @@ namespace App\Model\Table; +use App\Model\Entity\Note; use Cake\ORM\Query\SelectQuery; use Cake\ORM\Table; @@ -157,4 +158,12 @@ public function getTypeTestTwoArgsButNotLegacy(): string { return 'myType'; } + + /** + * @return iterable<\App\Model\Entity\Note> + */ + public function iterableItems(): iterable + { + return [new Note(), new Note()]; + } } From 767562aaf3e37bacf07c99d8e581b4a39eb619b9 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Wed, 30 Oct 2024 22:44:34 -0300 Subject: [PATCH 03/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- ...erFetchTableDynamicReturnTypeExtension.php | 13 ++++---- ...TableLocatorDynamicReturnTypeExtension.php | 32 ++++++++++--------- tests/test_app/View/Cell/MyTestLoadCell.php | 3 ++ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/Type/ControllerFetchTableDynamicReturnTypeExtension.php b/src/Type/ControllerFetchTableDynamicReturnTypeExtension.php index d87b53b..6102969 100644 --- a/src/Type/ControllerFetchTableDynamicReturnTypeExtension.php +++ b/src/Type/ControllerFetchTableDynamicReturnTypeExtension.php @@ -14,10 +14,10 @@ namespace CakeDC\PHPStan\Type; use PhpParser\Node\Expr\MethodCall; +use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use ReflectionClass; class ControllerFetchTableDynamicReturnTypeExtension extends TableLocatorDynamicReturnTypeExtension { @@ -27,7 +27,7 @@ class ControllerFetchTableDynamicReturnTypeExtension extends TableLocatorDynamic protected function getReturnTypeWithoutArgs( MethodReflection $methodReflection, MethodCall $methodCall, - ReflectionClass $targetClassReflection + ClassReflection $targetClassReflection ): ?Type { $type = parent::getReturnTypeWithoutArgs($methodReflection, $methodCall, $targetClassReflection); if ($type !== null) { @@ -42,22 +42,23 @@ protected function getReturnTypeWithoutArgs( } /** - * @param \ReflectionClass $targetClassReflection + * @param \PHPStan\Reflection\ClassReflection $targetClassReflection * @return string|null */ - protected function getDefaultTableByControllerClass(ReflectionClass $targetClassReflection): ?string + protected function getDefaultTableByControllerClass(ClassReflection $targetClassReflection): ?string { $hasProperty = $targetClassReflection->hasProperty('defaultTable'); if (!$hasProperty) { return null; } - $namespace = $targetClassReflection->getNamespaceName(); + $nativeReflection = $targetClassReflection->getNativeReflection(); + $namespace = $nativeReflection->getNamespaceName(); $pos = strrpos($namespace, '\\Controller'); if ($pos === false) { return null; } $baseNamespace = substr($namespace, 0, $pos); - $shortName = $targetClassReflection->getShortName(); + $shortName = $nativeReflection->getShortName(); $shortName = str_replace('Controller', '', $shortName); $tableClassName = sprintf( '%s\\Model\\Table\\%sTable', diff --git a/src/Type/TableLocatorDynamicReturnTypeExtension.php b/src/Type/TableLocatorDynamicReturnTypeExtension.php index e957a3f..a1b3cb6 100644 --- a/src/Type/TableLocatorDynamicReturnTypeExtension.php +++ b/src/Type/TableLocatorDynamicReturnTypeExtension.php @@ -16,11 +16,12 @@ use Cake\ORM\Table; use CakeDC\PHPStan\Traits\BaseCakeRegistryReturnTrait; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; -use ReflectionClass; use ReflectionException; class TableLocatorDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -83,25 +84,32 @@ public function getTypeFromMethodCall( } /** - * @param \ReflectionClass $target + * @param \PHPStan\Reflection\ClassReflection $target * @return mixed * @throws \ReflectionException */ - protected function getDefaultTable(ReflectionClass $target): mixed + protected function getDefaultTable(ClassReflection $target): mixed { - return $target->getProperty('defaultTable')->getDefaultValue(); + $default = $target->getNativeReflection() + ->getProperty('defaultTable') + ->getDefaultValueExpression(); + if ($default instanceof String_) { + return $default->value; + } + + return null; } /** * @param \PHPStan\Reflection\MethodReflection $methodReflection * @param \PhpParser\Node\Expr\MethodCall $methodCall - * @param \ReflectionClass $targetClassReflection + * @param \PHPStan\Reflection\ClassReflection $targetClassReflection * @return \PHPStan\Type\Type|null */ protected function getReturnTypeWithoutArgs( MethodReflection $methodReflection, MethodCall $methodCall, - ReflectionClass $targetClassReflection + ClassReflection $targetClassReflection ): ?Type { try { $defaultTable = $this->getDefaultTable($targetClassReflection); @@ -117,16 +125,10 @@ protected function getReturnTypeWithoutArgs( /** * @param \PHPStan\Analyser\Scope $scope * @param \PhpParser\Node\Expr\MethodCall $methodCall - * @return \ReflectionClass|null + * @return \PHPStan\Reflection\ClassReflection|null */ - protected function getTargetClassReflection(Scope $scope, MethodCall $methodCall): ?ReflectionClass + protected function getTargetClassReflection(Scope $scope, MethodCall $methodCall): ?ClassReflection { - $reference = $scope->getType($methodCall->var)->getReferencedClasses()[0] ?? null; - - if ($reference === null || !class_exists($reference)) { - return null; - } - - return new ReflectionClass($reference); + return $scope->getType($methodCall->var)->getObjectClassReflections()[0] ?? null; } } diff --git a/tests/test_app/View/Cell/MyTestLoadCell.php b/tests/test_app/View/Cell/MyTestLoadCell.php index 4558713..123f169 100644 --- a/tests/test_app/View/Cell/MyTestLoadCell.php +++ b/tests/test_app/View/Cell/MyTestLoadCell.php @@ -17,6 +17,8 @@ class MyTestLoadCell extends Cell { + protected ?string $defaultTable = 'Users'; + /** * Test for TableLocatorDynamicReturnTypeExtension with loadModel * @@ -26,6 +28,7 @@ public function sample() { $article = $this->fetchTable('VeryCustomize00009Articles') ->newSample(); + $this->fetchTable()->blockOld(); $this->set('article', $article); } } From f3058ca12f0fbf62523b7ec06784f5d34437a68c Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 08:39:16 -0300 Subject: [PATCH 04/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- ...seTraitExpressionTypeResolverExtension.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Type/BaseTraitExpressionTypeResolverExtension.php b/src/Type/BaseTraitExpressionTypeResolverExtension.php index 11ef13a..4251b50 100644 --- a/src/Type/BaseTraitExpressionTypeResolverExtension.php +++ b/src/Type/BaseTraitExpressionTypeResolverExtension.php @@ -62,10 +62,10 @@ public function getType(Expr $expr, Scope $scope): ?Type } $callerType = $scope->getType($expr->var); - if (!$callerType instanceof ThisType && !$callerType instanceof ObjectType) { + if (!$callerType->isObject()->yes()) { return null; } - $reflection = $callerType->getClassReflection(); + $reflection = $callerType->getObjectClassReflections()[0] ?? null; if ($reflection === null || !$this->isFromTargetTrait($reflection, $this->targetTrait)) { return null; } @@ -96,16 +96,17 @@ protected function getBaseName(?Expr $value, ClassReflection $reflection): ?stri try { if ($value === null && $this->propertyDefaultValue) { - $value = $reflection->getNativeReflection() - ->getProperty($this->propertyDefaultValue) - ->getDefaultValue(); - - return is_string($value) ? $value : null; + $default = $reflection->getNativeReflection() + ->getProperty('defaultTable') + ->getDefaultValueExpression(); + if ($default instanceof String_) { + return $default->value; + } } + + return null; } catch (ReflectionException) { return null; } - - return null; } } From 7b3a51a11b84235422c1873314d715813dc8b645 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 08:54:20 -0300 Subject: [PATCH 05/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- src/Traits/RepositoryReferenceTrait.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Traits/RepositoryReferenceTrait.php b/src/Traits/RepositoryReferenceTrait.php index 41852b0..493b406 100644 --- a/src/Traits/RepositoryReferenceTrait.php +++ b/src/Traits/RepositoryReferenceTrait.php @@ -16,15 +16,15 @@ trait RepositoryReferenceTrait */ protected function getReferenceClass(Scope $scope, MethodCall $methodCall): ?string { - $classes = $scope->getType($methodCall->var)->getReferencedClasses(); - if (!isset($classes[0])) { + $reflections = $scope->getType($methodCall->var)->getObjectClassReflections(); + if (!isset($reflections[0])) { return null; } - if (!is_subclass_of($classes[0], Association::class)) { - return $classes[0]; + if (!$reflections[0]->isSubclassOf(Association::class)) { + return $reflections[0]->getName(); } //We should have key 1 for associations, ex: BelongsTo<\App\Model\Table\UsersTable> - return $classes[1] ?? null; + return $reflections[1]->getName() ?? null; } } From 87e85b9095acda74bc34cf596c47d4537957ca78 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 09:07:53 -0300 Subject: [PATCH 06/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- src/Traits/BaseCakeRegistryReturnTrait.php | 18 +++--------------- ...ositoryEntityDynamicReturnTypeExtension.php | 8 ++++---- ...ositoryFirstArgIsTheReturnTypeExtension.php | 10 +++++----- .../TableLocatorDynamicReturnTypeExtension.php | 6 +++--- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/Traits/BaseCakeRegistryReturnTrait.php b/src/Traits/BaseCakeRegistryReturnTrait.php index de17463..306d26d 100644 --- a/src/Traits/BaseCakeRegistryReturnTrait.php +++ b/src/Traits/BaseCakeRegistryReturnTrait.php @@ -17,7 +17,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use function count; @@ -29,16 +28,16 @@ trait BaseCakeRegistryReturnTrait * @param \PHPStan\Reflection\MethodReflection $methodReflection * @param \PhpParser\Node\Expr\MethodCall $methodCall * @param \PHPStan\Analyser\Scope $scope - * @return \PHPStan\Type\Type + * @return \PHPStan\Type\Type|null * @throws \PHPStan\ShouldNotHappenException */ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type { + ): ?Type { if (count($methodCall->getArgs()) === 0) { - return $this->getTypeWhenNotFound($methodReflection); + return null; } $argType = $scope->getType($methodCall->getArgs()[0]->value); @@ -81,15 +80,4 @@ protected function getCakeType(string $baseName): ObjectType return new ObjectType($this->defaultClass); } - - /** - * @param \PHPStan\Reflection\MethodReflection $methodReflection - * @return \PHPStan\Type\Type - * @throws \PHPStan\ShouldNotHappenException - */ - protected function getTypeWhenNotFound(MethodReflection $methodReflection): Type - { - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants()) - ->getReturnType(); - } } diff --git a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php index f3752df..d88b590 100644 --- a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php +++ b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php @@ -79,17 +79,17 @@ public function isMethodSupported(MethodReflection $methodReflection): bool * @param \PHPStan\Reflection\MethodReflection $methodReflection * @param \PhpParser\Node\Expr\MethodCall $methodCall * @param \PHPStan\Analyser\Scope $scope - * @return \PHPStan\Type\Type + * @return \PHPStan\Type\Type|null * @throws \PHPStan\ShouldNotHappenException */ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type { + ): ?Type { $className = $this->getReferenceClass($scope, $methodCall); if ($className === null || $className === Table::class) { - return $this->getTypeWhenNotFound($methodReflection); + return null; } $entityClass = $this->getEntityClassByTableClass($className); @@ -102,7 +102,7 @@ public function getTypeFromMethodCall( return new ObjectType($entityClass); } - return $this->getTypeWhenNotFound($methodReflection); + return null; } /** diff --git a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php index f414364..78b0d3e 100644 --- a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php +++ b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php @@ -80,17 +80,17 @@ public function isMethodSupported(MethodReflection $methodReflection): bool * @param \PHPStan\Reflection\MethodReflection $methodReflection * @param \PhpParser\Node\Expr\MethodCall $methodCall * @param \PHPStan\Analyser\Scope $scope - * @return \PHPStan\Type\Type + * @return \PHPStan\Type\Type|null * @throws \PHPStan\ShouldNotHappenException */ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type { + ): ?Type { $args = $methodCall->getArgs(); if (count($args) === 0) { - return $this->getTypeWhenNotFound($methodReflection); + return null; } $type = $scope->getType($args[0]->value); @@ -108,14 +108,14 @@ public function getTypeFromMethodCall( } if ($methodReflection->getName() == 'patchEntities') { if (!$type->isIterable()->yes()) { - return $this->getTypeWhenNotFound($methodReflection); + return null; } $valueType = $type->getIterableValueType(); if ($valueType->isObject()->yes()) { return new ArrayType(new IntegerType(), $valueType); } - return $this->getTypeWhenNotFound($methodReflection); + return null; } return $type; diff --git a/src/Type/TableLocatorDynamicReturnTypeExtension.php b/src/Type/TableLocatorDynamicReturnTypeExtension.php index a1b3cb6..dbfce75 100644 --- a/src/Type/TableLocatorDynamicReturnTypeExtension.php +++ b/src/Type/TableLocatorDynamicReturnTypeExtension.php @@ -59,14 +59,14 @@ public function __construct(string $className, string $methodName) * @param \PHPStan\Reflection\MethodReflection $methodReflection * @param \PhpParser\Node\Expr\MethodCall $methodCall * @param \PHPStan\Analyser\Scope $scope - * @return \PHPStan\Type\Type + * @return \PHPStan\Type\Type|null * @throws \PHPStan\ShouldNotHappenException */ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type { + ): ?Type { if (count($methodCall->getArgs()) === 0) { $targetClassReflection = $this->getTargetClassReflection($scope, $methodCall); $type = null; @@ -77,7 +77,7 @@ public function getTypeFromMethodCall( return $type; } - return $this->getTypeWhenNotFound($methodReflection); + return null; } return $this->getTypeFromMethodCallWithArgs($methodReflection, $methodCall, $scope); From 91ce87e7027656ccf943d621ed4af4c1b719481a Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 09:08:24 -0300 Subject: [PATCH 07/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- src/Type/BaseTraitExpressionTypeResolverExtension.php | 1 - src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Type/BaseTraitExpressionTypeResolverExtension.php b/src/Type/BaseTraitExpressionTypeResolverExtension.php index 4251b50..9473750 100644 --- a/src/Type/BaseTraitExpressionTypeResolverExtension.php +++ b/src/Type/BaseTraitExpressionTypeResolverExtension.php @@ -22,7 +22,6 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Type\ExpressionTypeResolverExtension; use PHPStan\Type\ObjectType; -use PHPStan\Type\ThisType; use PHPStan\Type\Type; use ReflectionException; diff --git a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php index 78b0d3e..c78665c 100644 --- a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php +++ b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php @@ -22,7 +22,6 @@ use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\IntegerType; -use PHPStan\Type\IterableType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; From 66e4cc349905c42cda9664017053d6631864afb2 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 09:22:19 -0300 Subject: [PATCH 08/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- .../AnalyseCheckLineStartsWithTrait.php | 8 ++++---- src/Testing/CustomRuleTestCase.php | 10 ++++++++++ src/Traits/RepositoryReferenceTrait.php | 2 +- .../Model/AddAssociationMatchOptionsTypesRuleTest.php | 7 ++----- 4 files changed, 17 insertions(+), 10 deletions(-) rename src/{Rule/Traits => Testing}/AnalyseCheckLineStartsWithTrait.php (87%) create mode 100644 src/Testing/CustomRuleTestCase.php diff --git a/src/Rule/Traits/AnalyseCheckLineStartsWithTrait.php b/src/Testing/AnalyseCheckLineStartsWithTrait.php similarity index 87% rename from src/Rule/Traits/AnalyseCheckLineStartsWithTrait.php rename to src/Testing/AnalyseCheckLineStartsWithTrait.php index 61a48ec..42a128f 100644 --- a/src/Rule/Traits/AnalyseCheckLineStartsWithTrait.php +++ b/src/Testing/AnalyseCheckLineStartsWithTrait.php @@ -1,19 +1,19 @@ $expected * @return void */ public function analyseCheckLineStartsWith(array $files, array $expected): void diff --git a/src/Testing/CustomRuleTestCase.php b/src/Testing/CustomRuleTestCase.php new file mode 100644 index 0000000..86615e1 --- /dev/null +++ b/src/Testing/CustomRuleTestCase.php @@ -0,0 +1,10 @@ + - return $reflections[1]->getName() ?? null; + return $reflections[1]?->getName() ?? null; } } diff --git a/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php b/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php index 68bc6f6..4d7f53a 100644 --- a/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php +++ b/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php @@ -4,16 +4,13 @@ namespace CakeDC\PHPStan\Test\TestCase\Rule\Model; use CakeDC\PHPStan\Rule\Model\AddAssociationMatchOptionsTypesRule; -use CakeDC\PHPStan\Rule\Traits\AnalyseCheckLineStartsWithTrait; +use CakeDC\PHPStan\Testing\CustomRuleTestCase; use PHPStan\Rules\Properties\PropertyReflectionFinder; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleLevelHelper; -use PHPStan\Testing\RuleTestCase; -class AddAssociationMatchOptionsTypesRuleTest extends RuleTestCase +class AddAssociationMatchOptionsTypesRuleTest extends CustomRuleTestCase { - use AnalyseCheckLineStartsWithTrait; - /** * @return \PHPStan\Rules\Rule */ From 16450668719fe33fca0b38817247a77a8a82b18f Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 09:33:02 -0300 Subject: [PATCH 09/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- src/Testing/AnalyseCheckLineStartsWithTrait.php | 4 ++-- src/Traits/RepositoryReferenceTrait.php | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Testing/AnalyseCheckLineStartsWithTrait.php b/src/Testing/AnalyseCheckLineStartsWithTrait.php index 42a128f..d72e7ce 100644 --- a/src/Testing/AnalyseCheckLineStartsWithTrait.php +++ b/src/Testing/AnalyseCheckLineStartsWithTrait.php @@ -13,7 +13,7 @@ trait AnalyseCheckLineStartsWithTrait { /** * @param string[] $files - * @param array $expected + * @param array{array{'0': string, '1':int}} $expected * @return void */ public function analyseCheckLineStartsWith(array $files, array $expected): void @@ -32,7 +32,7 @@ public function analyseCheckLineStartsWith(array $files, array $expected): void }, $actualErrors); $expected = array_map(static function (array $item) use ($messageText): string { - return $messageText($item[1], $item[0]); + return $messageText((int)$item[1], (string)$item[0]); }, $expected); $this->assertThat($expected, new ArrayOfStringStartsWith($actualErrors)); } diff --git a/src/Traits/RepositoryReferenceTrait.php b/src/Traits/RepositoryReferenceTrait.php index 29e3c9b..b88efc0 100644 --- a/src/Traits/RepositoryReferenceTrait.php +++ b/src/Traits/RepositoryReferenceTrait.php @@ -24,7 +24,10 @@ protected function getReferenceClass(Scope $scope, MethodCall $methodCall): ?str return $reflections[0]->getName(); } //We should have key 1 for associations, ex: BelongsTo<\App\Model\Table\UsersTable> + if (isset($reflections[1])) { + return $reflections[1]->getName(); + } - return $reflections[1]?->getName() ?? null; + return null; } } From 4e38a464f163362f026164e8807a7b2ea813a10b Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 09:45:49 -0300 Subject: [PATCH 10/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- .../OrmSelectQueryFindMatchOptionsTypesRule.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php index 73ee26f..28b9a85 100644 --- a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php +++ b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php @@ -18,7 +18,7 @@ use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; -use PHPStan\Reflection\Php\PhpMethodReflection; +use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; use PHPStan\Rules\RuleErrorBuilder; @@ -214,19 +214,15 @@ protected function processPropertyTypeCheck( /** * @param \PHPStan\Analyser\Scope $scope * @param string $targetMethod - * @return \PHPStan\Reflection\Php\PhpMethodReflection + * @return \PHPStan\Reflection\ExtendedMethodReflection|null * @throws \PHPStan\Reflection\MissingMethodFromReflectionException */ - protected function getTargetMethod(Scope $scope, string $targetMethod): PhpMethodReflection + protected function getTargetMethod(Scope $scope, string $targetMethod): ?ExtendedMethodReflection { $object = new ObjectType(SelectQuery::class); $classReflection = $object->getClassReflection(); - assert($classReflection instanceof ClassReflection); - $methodReflection = $classReflection - ->getMethod($targetMethod, $scope); - assert($methodReflection instanceof PhpMethodReflection); - return $methodReflection; + return $classReflection?->getMethod($targetMethod, $scope); } /** @@ -362,7 +358,7 @@ protected function getSpecificFinderOptions(array $details, Scope $scope): array if ( $secondParam->getName() === 'options' && !$secondParam->isVariadic() - && ($paramType instanceof MixedType || $paramType instanceof ArrayType) + && ($paramType instanceof MixedType || $paramType->isArray()->yes()) ) { return []; } @@ -389,6 +385,9 @@ protected function getExpectedType(int|string $name, Scope $scope, array $specif { if (isset($this->queryOptionsMap[$name])) { $methodReflection = $this->getTargetMethod($scope, $this->queryOptionsMap[$name]); + if ($methodReflection === null) { + return null; + } $parameter = $methodReflection->getVariants()[0]->getParameters()[0]; return $parameter->getType(); From 1492d6b5fdabda9367726309ba74480396f0c39b Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 09:59:34 -0300 Subject: [PATCH 11/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- src/Rule/Model/DisallowEntityArrayAccessRule.php | 8 ++++++-- .../Rule/Model/DisallowEntityArrayAccessRuleTest.php | 2 +- tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php | 3 +++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Rule/Model/DisallowEntityArrayAccessRule.php b/src/Rule/Model/DisallowEntityArrayAccessRule.php index 781fc89..2849975 100644 --- a/src/Rule/Model/DisallowEntityArrayAccessRule.php +++ b/src/Rule/Model/DisallowEntityArrayAccessRule.php @@ -32,14 +32,18 @@ public function processNode(Node $node, Scope $scope): array { assert($node instanceof ArrayDimFetch); $type = $scope->getType($node->var); - if (!$type instanceof ObjectType || !is_a($type->getClassName(), EntityInterface::class, true)) { + if (!$type->isObject()->yes()) { + return []; + } + $reflection = $type->getObjectClassReflections()[0] ?? null; + if ($reflection === null || !$reflection->is(EntityInterface::class)) { return []; } return [ RuleErrorBuilder::message(sprintf( 'Array access to entity to %s is not allowed, access as object instead', - $type->getClassName(), + $reflection->getName(), )) ->identifier('cake.entity.arrayAccess') ->build(), diff --git a/tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php b/tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php index 442355f..71d297d 100644 --- a/tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php +++ b/tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php @@ -55,7 +55,7 @@ public function testRule(): void ], [ 'Array access to entity to App\Model\Entity\Note is not allowed, access as object instead', - 33, // asserted error line + 36, // asserted error line ], ]); } diff --git a/tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php b/tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php index 097b994..2e2bd61 100644 --- a/tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php +++ b/tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php @@ -28,6 +28,9 @@ public function execute(): array $date = $unknown['create']; $user = $this->fetchTable('Users')->get(10); $user['role'] = 'Admin'; + $array = new \SplFixedArray(2); + $array[0] = 'a'; + $array[1] = 'b'; return [ 'userId' => $entity['user_id'], From 2044dffe26b5fccfb46840d8014add5a23bd51a2 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 17:16:13 -0300 Subject: [PATCH 12/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- src/PhpDoc/TableAssociationTypeNodeResolverExtension.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php b/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php index 731ed36..9a1546a 100644 --- a/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php +++ b/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php @@ -65,10 +65,13 @@ public function resolve(TypeNode $typeNode, NameScope $nameScope): ?Type 'table' => null, ]; foreach ($types as $type) { - if (!$type instanceof ObjectType) { + if (!$type->isObject()) { + continue; + } + $className = $type->getObjectClassNames()[0] ?? null; + if ($className === null) { continue; } - $className = $type->getClassName(); if ($config['association'] === null && in_array($className, $this->associationTypes)) { $config['association'] = $type; } elseif ($config['table'] === null && str_ends_with($className, 'Table')) { @@ -77,7 +80,7 @@ public function resolve(TypeNode $typeNode, NameScope $nameScope): ?Type } if ($config['table'] && $config['association']) { return new GenericObjectType( - $config['association']->getClassName(), + $config['association']->getObjectClassNames()[0], [$config['table']] ); } From 5ca22c6b770521f8f0b45185c7ec217413339816 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 17:34:11 -0300 Subject: [PATCH 13/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- ...tionTableMixinClassReflectionExtension.php | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/Method/AssociationTableMixinClassReflectionExtension.php b/src/Method/AssociationTableMixinClassReflectionExtension.php index 797e5a4..e4d9953 100644 --- a/src/Method/AssociationTableMixinClassReflectionExtension.php +++ b/src/Method/AssociationTableMixinClassReflectionExtension.php @@ -9,31 +9,25 @@ use Cake\ORM\Association; use Cake\ORM\Table; -use PHPStan\Broker\Broker; -use PHPStan\Reflection\BrokerAwareExtension; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\MethodsClassReflectionExtension; use PHPStan\Reflection\PropertiesClassReflectionExtension; use PHPStan\Reflection\PropertyReflection; +use PHPStan\Reflection\ReflectionProvider; class AssociationTableMixinClassReflectionExtension implements PropertiesClassReflectionExtension, - MethodsClassReflectionExtension, - BrokerAwareExtension + MethodsClassReflectionExtension { /** - * @var \PHPStan\Broker\Broker + * @var \PHPStan\Reflection\ReflectionProvider */ - private Broker $broker; + protected ReflectionProvider $reflectionProvider; - /** - * @param \PHPStan\Broker\Broker $broker Class reflection broker - * @return void - */ - public function setBroker(Broker $broker): void + public function __construct(ReflectionProvider $reflectionProvider) { - $this->broker = $broker; + $this->reflectionProvider = $reflectionProvider; } /** @@ -41,7 +35,7 @@ public function setBroker(Broker $broker): void */ protected function getTableReflection(): ClassReflection { - return $this->broker->getClass(Table::class); + return $this->reflectionProvider->getClass(Table::class); } /** From c1922935f0d97989c8cc2569566227a45b8cd0a4 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 17:38:56 -0300 Subject: [PATCH 14/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- src/Constraint/ArrayOfStringStartsWith.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Constraint/ArrayOfStringStartsWith.php b/src/Constraint/ArrayOfStringStartsWith.php index 68e7a9b..9f81563 100644 --- a/src/Constraint/ArrayOfStringStartsWith.php +++ b/src/Constraint/ArrayOfStringStartsWith.php @@ -37,7 +37,7 @@ public function toString(): string } /** - * @param mixed $other + * @param array $other * @return bool */ protected function matches(mixed $other): bool From b3f3f23dd7473276ae98bcba3bf7070422642791 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 17:55:14 -0300 Subject: [PATCH 15/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- ...leAssociationTypeNodeResolverExtension.php | 1 - .../Model/DisallowEntityArrayAccessRule.php | 1 - ...rmSelectQueryFindMatchOptionsTypesRule.php | 2 -- .../AnalyseCheckLineStartsWithTrait.php | 2 +- src/Testing/CustomRuleTestCase.php | 1 + src/Traits/RepositoryReferenceTrait.php | 28 +++++++++++++------ .../Rule/Model/Fake/FailingEntityUseLogic.php | 3 +- 7 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php b/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php index 9a1546a..dadb99c 100644 --- a/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php +++ b/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php @@ -15,7 +15,6 @@ use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Type\Generic\GenericObjectType; -use PHPStan\Type\ObjectType; use PHPStan\Type\Type; /** diff --git a/src/Rule/Model/DisallowEntityArrayAccessRule.php b/src/Rule/Model/DisallowEntityArrayAccessRule.php index 2849975..83fc513 100644 --- a/src/Rule/Model/DisallowEntityArrayAccessRule.php +++ b/src/Rule/Model/DisallowEntityArrayAccessRule.php @@ -9,7 +9,6 @@ use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; -use PHPStan\Type\ObjectType; class DisallowEntityArrayAccessRule implements Rule { diff --git a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php index 28b9a85..65cc17c 100644 --- a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php +++ b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php @@ -17,13 +17,11 @@ use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; -use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Rules\RuleLevelHelper; -use PHPStan\Type\ArrayType; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; diff --git a/src/Testing/AnalyseCheckLineStartsWithTrait.php b/src/Testing/AnalyseCheckLineStartsWithTrait.php index d72e7ce..8e27a6c 100644 --- a/src/Testing/AnalyseCheckLineStartsWithTrait.php +++ b/src/Testing/AnalyseCheckLineStartsWithTrait.php @@ -12,7 +12,7 @@ trait AnalyseCheckLineStartsWithTrait { /** - * @param string[] $files + * @param array $files * @param array{array{'0': string, '1':int}} $expected * @return void */ diff --git a/src/Testing/CustomRuleTestCase.php b/src/Testing/CustomRuleTestCase.php index 86615e1..a0d31d9 100644 --- a/src/Testing/CustomRuleTestCase.php +++ b/src/Testing/CustomRuleTestCase.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace CakeDC\PHPStan\Testing; + use PHPStan\Testing\RuleTestCase; abstract class CustomRuleTestCase extends RuleTestCase diff --git a/src/Traits/RepositoryReferenceTrait.php b/src/Traits/RepositoryReferenceTrait.php index b88efc0..72ac242 100644 --- a/src/Traits/RepositoryReferenceTrait.php +++ b/src/Traits/RepositoryReferenceTrait.php @@ -4,11 +4,26 @@ namespace CakeDC\PHPStan\Traits; use Cake\ORM\Association; +use Cake\ORM\Association\BelongsTo; +use Cake\ORM\Association\BelongsToMany; +use Cake\ORM\Association\HasMany; +use Cake\ORM\Association\HasOne; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; trait RepositoryReferenceTrait { + /** + * @var string[] + */ + protected array $associationsClasses = [ + Association::class, + BelongsTo::class, + BelongsToMany::class, + HasMany::class, + HasOne::class, + ]; + /** * @param \PHPStan\Analyser\Scope $scope * @param \PhpParser\Node\Expr\MethodCall $methodCall @@ -16,18 +31,15 @@ trait RepositoryReferenceTrait */ protected function getReferenceClass(Scope $scope, MethodCall $methodCall): ?string { - $reflections = $scope->getType($methodCall->var)->getObjectClassReflections(); - if (!isset($reflections[0])) { + $classes = $scope->getType($methodCall->var)->getReferencedClasses(); + if (!isset($classes[0])) { return null; } - if (!$reflections[0]->isSubclassOf(Association::class)) { - return $reflections[0]->getName(); + if (!in_array($classes[0], $this->associationsClasses)) { + return $classes[0]; } //We should have key 1 for associations, ex: BelongsTo<\App\Model\Table\UsersTable> - if (isset($reflections[1])) { - return $reflections[1]->getName(); - } - return null; + return $classes[1] ?? null; } } diff --git a/tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php b/tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php index 2e2bd61..c167531 100644 --- a/tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php +++ b/tests/TestCase/Rule/Model/Fake/FailingEntityUseLogic.php @@ -4,6 +4,7 @@ namespace CakeDC\PHPStan\Test\TestCase\Rule\Model\Fake; use Cake\ORM\Locator\LocatorAwareTrait; +use SplFixedArray; class FailingEntityUseLogic { @@ -28,7 +29,7 @@ public function execute(): array $date = $unknown['create']; $user = $this->fetchTable('Users')->get(10); $user['role'] = 'Admin'; - $array = new \SplFixedArray(2); + $array = new SplFixedArray(2); $array[0] = 'a'; $array[1] = 'b'; From bf00c69225c4d069d6b76bae5a5fd68d7300b5cb Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 17:56:13 -0300 Subject: [PATCH 16/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- src/Method/AssociationTableMixinClassReflectionExtension.php | 3 +++ src/Traits/RepositoryReferenceTrait.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Method/AssociationTableMixinClassReflectionExtension.php b/src/Method/AssociationTableMixinClassReflectionExtension.php index e4d9953..52e578f 100644 --- a/src/Method/AssociationTableMixinClassReflectionExtension.php +++ b/src/Method/AssociationTableMixinClassReflectionExtension.php @@ -25,6 +25,9 @@ class AssociationTableMixinClassReflectionExtension implements */ protected ReflectionProvider $reflectionProvider; + /** + * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider + */ public function __construct(ReflectionProvider $reflectionProvider) { $this->reflectionProvider = $reflectionProvider; diff --git a/src/Traits/RepositoryReferenceTrait.php b/src/Traits/RepositoryReferenceTrait.php index 72ac242..f772954 100644 --- a/src/Traits/RepositoryReferenceTrait.php +++ b/src/Traits/RepositoryReferenceTrait.php @@ -14,7 +14,7 @@ trait RepositoryReferenceTrait { /** - * @var string[] + * @var array */ protected array $associationsClasses = [ Association::class, From 8636b9196eec046c1ed5b9a6e6b6ac9271f6ea9c Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 18:00:00 -0300 Subject: [PATCH 17/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- tests/test_app/Model/Entity/Note.php | 4 ++-- tests/test_app/Model/Table/NotesTable.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_app/Model/Entity/Note.php b/tests/test_app/Model/Entity/Note.php index 7b4eaeb..2d576bc 100644 --- a/tests/test_app/Model/Entity/Note.php +++ b/tests/test_app/Model/Entity/Note.php @@ -10,8 +10,8 @@ * @property string $note * @property int $user_id * @property \App\Model\Entity\User $user - * @property \Cake\I18n\FrozenDate $created - * @property \Cake\I18n\FrozenDate $modified + * @property \Cake\I18n\Date $created + * @property \Cake\I18n\Date $modified */ class Note extends Entity { diff --git a/tests/test_app/Model/Table/NotesTable.php b/tests/test_app/Model/Table/NotesTable.php index 17af587..9ce9669 100644 --- a/tests/test_app/Model/Table/NotesTable.php +++ b/tests/test_app/Model/Table/NotesTable.php @@ -18,7 +18,7 @@ use Cake\ORM\Table; /** - * @method \App\Model\Entity\Note|\Cake\Datasource\EntityInterface get(mixed $primaryKey, array|string $finder = 'all',CacheInterface|string|null $cache = null,\Closure|string|null $cacheKey = null, mixed ...$args) + * @method \App\Model\Entity\Note|\Cake\Datasource\EntityInterface get(mixed $primaryKey, string[]|string $finder = 'all',\Psr\SimpleCache\CacheInterface|string|null $cache = null,\Closure|string|null $cacheKey = null, mixed ...$args) * @property \App\Model\Table\VeryCustomize00009ArticlesTable&\Cake\ORM\Association\HasMany $VeryCustomize00009Articles * @property \Cake\ORM\Association\BelongsTo<\App\Model\Table\UsersTable> $Users * @property \Cake\ORM\Association\BelongsTo&\App\Model\Table\UsersTable $MyUsers From a02effc35fccb079d3b21b0c46dc026e20e7da8f Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 18:05:49 -0300 Subject: [PATCH 18/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- .../Rule/Model/DisallowEntityArrayAccessRuleTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php b/tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php index 71d297d..62a2a68 100644 --- a/tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php +++ b/tests/TestCase/Rule/Model/DisallowEntityArrayAccessRuleTest.php @@ -39,23 +39,23 @@ public function testRule(): void $this->analyse([__DIR__ . '/Fake/FailingEntityUseLogic.php'], [ [ 'Array access to entity to App\Model\Entity\Note is not allowed, access as object instead', - 22, // asserted error line + 23, // asserted error line ], [ 'Array access to entity to App\Model\Entity\Note is not allowed, access as object instead', - 23, // asserted error line + 24, // asserted error line ], [ 'Array access to entity to Cake\Datasource\EntityInterface is not allowed, access as object instead', - 28, + 29, ], [ 'Array access to entity to App\Model\Entity\User is not allowed, access as object instead', - 30, // asserted error line + 31, // asserted error line ], [ 'Array access to entity to App\Model\Entity\Note is not allowed, access as object instead', - 36, // asserted error line + 37, // asserted error line ], ]); } From 97894dbe0b6cbfa5dd7533bb690ac139207e492f Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 18:15:35 -0300 Subject: [PATCH 19/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- composer.json | 4 ++-- phpstan-test-app.neon | 17 +++++++++++++++++ phpstan-test-plugin.neon | 14 ++++++++++++++ phpstan.neon | 8 -------- 4 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 phpstan-test-app.neon create mode 100644 phpstan-test-plugin.neon diff --git a/composer.json b/composer.json index 5c687a1..cbd7146 100644 --- a/composer.json +++ b/composer.json @@ -46,8 +46,8 @@ "cs-fix": "phpcbf -p src/ tests", "test": "phpunit --stderr", "stan-integration": [ - "phpstan analyse --debug tests/test_app/", - "phpstan analyse --debug tests/test_plugin/" + "phpstan analyse --debug -c phpstan-test-app.neon", + "phpstan analyse --debug -c phpstan-test-plugin.neon" ], "stan": "phpstan analyse --debug src/", "check": [ diff --git a/phpstan-test-app.neon b/phpstan-test-app.neon new file mode 100644 index 0000000..acaa6ce --- /dev/null +++ b/phpstan-test-app.neon @@ -0,0 +1,17 @@ +includes: + - extension.neon + - phar://phpstan.phar/conf/bleedingEdge.neon + - vendor/phpstan/phpstan-deprecation-rules/rules.neon +rules: + - Symplify\PHPStanRules\Rules\Explicit\NoMixedPropertyFetcherRule + - Symplify\PHPStanRules\Rules\Explicit\NoMixedMethodCallerRule +parameters: + level: max + paths: + - tests/test_app + treatPhpDocTypesAsCertain: false + cakeDC: + disallowEntityArrayAccessRule: true + ignoreErrors: + - + identifier: missingType.generics diff --git a/phpstan-test-plugin.neon b/phpstan-test-plugin.neon new file mode 100644 index 0000000..1f605f3 --- /dev/null +++ b/phpstan-test-plugin.neon @@ -0,0 +1,14 @@ +includes: + - extension.neon + - phar://phpstan.phar/conf/bleedingEdge.neon + - vendor/phpstan/phpstan-deprecation-rules/rules.neon +rules: + - Symplify\PHPStanRules\Rules\Explicit\NoMixedPropertyFetcherRule + - Symplify\PHPStanRules\Rules\Explicit\NoMixedMethodCallerRule +parameters: + level: max + paths: + - tests/test_plugin + treatPhpDocTypesAsCertain: false + cakeDC: + disallowEntityArrayAccessRule: true diff --git a/phpstan.neon b/phpstan.neon index 9ba7123..953d9b9 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,14 +2,6 @@ includes: - extension.neon - phar://phpstan.phar/conf/bleedingEdge.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon -rules: - - Symplify\PHPStanRules\Rules\Explicit\NoMixedPropertyFetcherRule - - Symplify\PHPStanRules\Rules\Explicit\NoMixedMethodCallerRule parameters: level: max treatPhpDocTypesAsCertain: false - cakeDC: - disallowEntityArrayAccessRule: true - ignoreErrors: - - - identifier: missingType.generics From 4c1d00dd39b142100eed9c17bdf9317124341f97 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 18:33:28 -0300 Subject: [PATCH 20/34] PHPStan 2.0 Upgrade - Fix deprecated usage --- composer.json | 2 +- phpstan.neon | 8 ++++++++ .../AddAssociationMatchOptionsTypesRuleTest.php | 12 +----------- .../OrmSelectQueryFindMatchOptionsTypesRuleTest.php | 11 +---------- .../Rule/Model/TableGetMatchOptionsTypesRuleTest.php | 11 +---------- 5 files changed, 12 insertions(+), 32 deletions(-) diff --git a/composer.json b/composer.json index cbd7146..695cd2a 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "phpstan analyse --debug -c phpstan-test-app.neon", "phpstan analyse --debug -c phpstan-test-plugin.neon" ], - "stan": "phpstan analyse --debug src/", + "stan": "phpstan analyse --debug", "check": [ "@cs-check", "@stan", diff --git a/phpstan.neon b/phpstan.neon index 953d9b9..9b0a083 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,4 +4,12 @@ includes: - vendor/phpstan/phpstan-deprecation-rules/rules.neon parameters: level: max + paths: + - src + - tests/TestCase + excludePaths: + - */Fake/* treatPhpDocTypesAsCertain: false + ignoreErrors: + - + identifier: missingType.generics diff --git a/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php b/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php index 4d7f53a..ef96e09 100644 --- a/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php +++ b/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php @@ -18,17 +18,7 @@ protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule return new AddAssociationMatchOptionsTypesRule( - new RuleLevelHelper( - $this->createReflectionProvider(), - true, - false, - true, - false, - false, - true, - false - ), - new PropertyReflectionFinder() + static::getContainer()->getByType(RuleLevelHelper::class) ); } diff --git a/tests/TestCase/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRuleTest.php b/tests/TestCase/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRuleTest.php index 0ca6622..273886c 100644 --- a/tests/TestCase/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRuleTest.php +++ b/tests/TestCase/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRuleTest.php @@ -17,16 +17,7 @@ protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule return new OrmSelectQueryFindMatchOptionsTypesRule( - new RuleLevelHelper( - $this->createReflectionProvider(), - true, - false, - true, - false, - false, - true, - false - ) + static::getContainer()->getByType(RuleLevelHelper::class) ); } diff --git a/tests/TestCase/Rule/Model/TableGetMatchOptionsTypesRuleTest.php b/tests/TestCase/Rule/Model/TableGetMatchOptionsTypesRuleTest.php index ae2548e..16cc824 100644 --- a/tests/TestCase/Rule/Model/TableGetMatchOptionsTypesRuleTest.php +++ b/tests/TestCase/Rule/Model/TableGetMatchOptionsTypesRuleTest.php @@ -17,16 +17,7 @@ protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule return new TableGetMatchOptionsTypesRule( - new RuleLevelHelper( - $this->createReflectionProvider(), - true, - false, - true, - false, - false, - true, - false - ) + static::getContainer()->getByType(RuleLevelHelper::class) ); } From 6a48cd7dec90f04ad0f2ad21e7f9ea5c0e8c506b Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 18:50:25 -0300 Subject: [PATCH 21/34] PHPStan 2.0 Upgrade - Removed unnecessary tests (already check in test rules) --- ...nentLoadDynamicReturnTypeExtensionTest.php | 61 -------------- ...eLocatorDynamicReturnTypeExtensionTest.php | 80 ------------------- 2 files changed, 141 deletions(-) delete mode 100644 tests/TestCase/Type/ComponentLoadDynamicReturnTypeExtensionTest.php delete mode 100644 tests/TestCase/Type/TableLocatorDynamicReturnTypeExtensionTest.php diff --git a/tests/TestCase/Type/ComponentLoadDynamicReturnTypeExtensionTest.php b/tests/TestCase/Type/ComponentLoadDynamicReturnTypeExtensionTest.php deleted file mode 100644 index 05c8b73..0000000 --- a/tests/TestCase/Type/ComponentLoadDynamicReturnTypeExtensionTest.php +++ /dev/null @@ -1,61 +0,0 @@ -assertSame(Controller::class, $subject->getClass()); - } - - /** - * Data provider for testIsMethodSupported method - * - * @return array - */ - public static function dataProviderIsMethodSupported() - { - return [ - ['loadComponent', true], - ['get', false], - ['loadModel', false], - ]; - } - - /** - * Test getClassMethod - * - * @param string $testMethod - * @param bool $expected - * @dataProvider dataProviderIsMethodSupported - */ - public function testIsMethodSupported(string $testMethod, bool $expected) - { - $subject = new ComponentLoadDynamicReturnTypeExtension(); - $methodReflection = new DummyMethodReflection($testMethod); - $this->assertSame($expected, $subject->isMethodSupported($methodReflection)); - } -} diff --git a/tests/TestCase/Type/TableLocatorDynamicReturnTypeExtensionTest.php b/tests/TestCase/Type/TableLocatorDynamicReturnTypeExtensionTest.php deleted file mode 100644 index a7348d4..0000000 --- a/tests/TestCase/Type/TableLocatorDynamicReturnTypeExtensionTest.php +++ /dev/null @@ -1,80 +0,0 @@ -assertSame($targetClass, $subject->getClass()); - } - - /** - * Data provider for testIsMethodSupported method - * - * @return array - */ - public static function dataProviderIsMethodSupported() - { - return [ - ['get', 'get', true], - ['get', 'read', false], - ['loadModel', 'loadModel', true], - ['loadModel', 'get', false], - ]; - } - - /** - * Test getClassMethod - * - * @param string $allowedMethod - * @param string $testMethod - * @param bool $expected - * @dataProvider dataProviderIsMethodSupported - */ - public function testIsMethodSupported(string $allowedMethod, string $testMethod, bool $expected) - { - $subject = new TableLocatorDynamicReturnTypeExtension( - 'Cake\ORM\Locator\LocatorInterface', - $allowedMethod - ); - $methodReflection = new DummyMethodReflection($testMethod); - $this->assertSame($expected, $subject->isMethodSupported($methodReflection)); - } -} From 60b09672d6e76cd63754cca07f776270a01334e4 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 18:50:54 -0300 Subject: [PATCH 22/34] PHPStan 2.0 Upgrade --- .../Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php b/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php index ef96e09..96ce888 100644 --- a/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php +++ b/tests/TestCase/Rule/Model/AddAssociationMatchOptionsTypesRuleTest.php @@ -5,7 +5,6 @@ use CakeDC\PHPStan\Rule\Model\AddAssociationMatchOptionsTypesRule; use CakeDC\PHPStan\Testing\CustomRuleTestCase; -use PHPStan\Rules\Properties\PropertyReflectionFinder; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleLevelHelper; From 303e86324d2964c4083d3210d7a934f044068a2d Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 19:05:14 -0300 Subject: [PATCH 23/34] PHPStan 2.0 Upgrade - Strict types fixes --- composer.json | 3 ++- phpstan.neon | 1 + src/Visitor/AddAssociationSetClassNameVisitor.php | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 695cd2a..f310db8 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "phpunit/phpunit": "^10.1", "cakephp/cakephp-codesniffer": "^5.0", "symplify/phpstan-rules": "^12.4", - "phpstan/phpstan-deprecation-rules": "^1.2" + "phpstan/phpstan-deprecation-rules": "^1.2", + "phpstan/phpstan-strict-rules": "^1.6" }, "extra": { "phpstan": { diff --git a/phpstan.neon b/phpstan.neon index 9b0a083..6fa3973 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,6 +2,7 @@ includes: - extension.neon - phar://phpstan.phar/conf/bleedingEdge.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon parameters: level: max paths: diff --git a/src/Visitor/AddAssociationSetClassNameVisitor.php b/src/Visitor/AddAssociationSetClassNameVisitor.php index 6805bff..4c76d22 100644 --- a/src/Visitor/AddAssociationSetClassNameVisitor.php +++ b/src/Visitor/AddAssociationSetClassNameVisitor.php @@ -5,6 +5,7 @@ use CakeDC\PHPStan\Rule\Traits\ParseClassNameFromArgTrait; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\MethodCall; use PhpParser\NodeVisitorAbstract; @@ -43,9 +44,10 @@ public function enterNode(Node $node): ?Node return null; } if ($this->optionsSet === null && $node->name->name === 'setClassName') { - $this->optionsSet = $node->args[0]->value ?? null; + $arg = $node->args[0] ?? null; + $this->optionsSet = $arg instanceof Arg ? $arg->value : null; } - if (in_array($node->name->name, ['load', 'belongsTo', 'belongsToMany', 'hasOne', 'hasMany'])) { + if (in_array($node->name->name, ['load', 'belongsTo', 'belongsToMany', 'hasOne', 'hasMany'], true)) { $node->setAttribute(self::ATTRIBUTE_NAME, $this->optionsSet); } From 6e20bec96fe7666fb0ba2c967b6a1c54e0843ccb Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 19:09:12 -0300 Subject: [PATCH 24/34] PHPStan 2.0 Upgrade - Strict types fixes --- src/Type/TableLocatorDynamicReturnTypeExtension.php | 6 +++--- src/Utility/CakeNameRegistry.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Type/TableLocatorDynamicReturnTypeExtension.php b/src/Type/TableLocatorDynamicReturnTypeExtension.php index dbfce75..95e8168 100644 --- a/src/Type/TableLocatorDynamicReturnTypeExtension.php +++ b/src/Type/TableLocatorDynamicReturnTypeExtension.php @@ -85,10 +85,10 @@ public function getTypeFromMethodCall( /** * @param \PHPStan\Reflection\ClassReflection $target - * @return mixed + * @return string|null * @throws \ReflectionException */ - protected function getDefaultTable(ClassReflection $target): mixed + protected function getDefaultTable(ClassReflection $target): ?string { $default = $target->getNativeReflection() ->getProperty('defaultTable') @@ -113,7 +113,7 @@ protected function getReturnTypeWithoutArgs( ): ?Type { try { $defaultTable = $this->getDefaultTable($targetClassReflection); - if (is_string($defaultTable) && $defaultTable) { + if (is_string($defaultTable) && $defaultTable !== '') { return $this->getCakeType($defaultTable); } } catch (ReflectionException) { diff --git a/src/Utility/CakeNameRegistry.php b/src/Utility/CakeNameRegistry.php index 09b0810..80e7f17 100644 --- a/src/Utility/CakeNameRegistry.php +++ b/src/Utility/CakeNameRegistry.php @@ -9,8 +9,8 @@ class CakeNameRegistry { /** * @param string $baseName - * @return array - * @psalm-return array{string|null, string} + * @return array{string|null,string} + * @psalm-return array{string|null,string} */ protected static function pluginSplit(string $baseName): array { @@ -29,7 +29,7 @@ public static function getClassName(string $baseName, string|array $namespaceFor } [$plugin, $name] = static::pluginSplit($baseName); - $prefixes = $plugin ? [$plugin] : ['App', 'Cake']; + $prefixes = $plugin !== null ? [$plugin] : ['App', 'Cake']; $namespaceFormat = (array)$namespaceFormat; foreach ($namespaceFormat as $format) { foreach ($prefixes as $prefix) { From 384d361bb46d75cdcea5ccecb07755a3c64a93f1 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Thu, 31 Oct 2024 21:38:17 -0300 Subject: [PATCH 25/34] PHPStan 2.0 Upgrade - Strict types fixes --- src/Rule/Mailer/GetMailerExistsClassRule.php | 6 +-- .../AddAssociationMatchOptionsTypesRule.php | 11 +++--- .../Model/DisallowEntityArrayAccessRule.php | 5 +-- ...rmSelectQueryFindMatchOptionsTypesRule.php | 39 ++++++++++++------- .../Model/TableGetMatchOptionsTypesRule.php | 2 +- .../AnalyseCheckLineStartsWithTrait.php | 4 +- src/Traits/RepositoryReferenceTrait.php | 2 +- ...seTraitExpressionTypeResolverExtension.php | 2 +- ...sitoryEntityDynamicReturnTypeExtension.php | 4 +- ...sitoryFirstArgIsTheReturnTypeExtension.php | 6 +-- 10 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/Rule/Mailer/GetMailerExistsClassRule.php b/src/Rule/Mailer/GetMailerExistsClassRule.php index c8aa957..623804e 100644 --- a/src/Rule/Mailer/GetMailerExistsClassRule.php +++ b/src/Rule/Mailer/GetMailerExistsClassRule.php @@ -30,7 +30,7 @@ class GetMailerExistsClassRule implements Rule protected string $identifier = 'cake.getMailer.existClass'; /** - * @return string + * @inheritDoc */ public function getNodeType(): string { @@ -40,7 +40,7 @@ public function getNodeType(): string /** * @param \PhpParser\Node $node * @param \PHPStan\Analyser\Scope $scope - * @return array<\PHPStan\Rules\RuleError> + * @return list<\PHPStan\Rules\IdentifierRuleError> */ public function processNode(Node $node, Scope $scope): array { @@ -66,7 +66,7 @@ public function processNode(Node $node, Scope $scope): array } $reflection = $callerType->getClassReflection(); - if (CakeNameRegistry::getMailerClassName($value->value)) { + if (CakeNameRegistry::getMailerClassName($value->value) !== null) { return []; } diff --git a/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php b/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php index cc220c8..ce19cde 100644 --- a/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php +++ b/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php @@ -16,6 +16,7 @@ use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; +use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; use PHPStan\Rules\RuleErrorBuilder; @@ -51,7 +52,7 @@ public function __construct(RuleLevelHelper $ruleLevelHelper) ]; /** - * @return string + * @inheritDoc */ public function getNodeType(): string { @@ -61,7 +62,7 @@ public function getNodeType(): string /** * @param \PhpParser\Node $node * @param \PHPStan\Analyser\Scope $scope - * @return array<\PHPStan\Rules\RuleError> + * @return list<\PHPStan\Rules\IdentifierRuleError> */ public function processNode(Node $node, Scope $scope): array { @@ -100,7 +101,7 @@ public function processNode(Node $node, Scope $scope): array $item, $scope ); - if ($error) { + if ($error !== null) { $errors[] = $error; } } else { @@ -158,7 +159,7 @@ protected function getDetails(string $reference, string $methodName, array $args * @param string $property * @param \PhpParser\Node\Expr\ArrayItem $item * @param \PHPStan\Analyser\Scope $scope - * @return \PHPStan\Rules\RuleError|null + * @return \PHPStan\Rules\IdentifierRuleError|null * @throws \PHPStan\Reflection\MissingPropertyFromReflectionException * @throws \PHPStan\ShouldNotHappenException */ @@ -167,7 +168,7 @@ protected function processPropertyTypeCheck( string $property, ArrayItem $item, Scope $scope - ): ?RuleError { + ): ?IdentifierRuleError { $object = new ObjectType($details['type']); $classReflection = $object->getClassReflection(); assert($classReflection instanceof ClassReflection); diff --git a/src/Rule/Model/DisallowEntityArrayAccessRule.php b/src/Rule/Model/DisallowEntityArrayAccessRule.php index 83fc513..fa50a72 100644 --- a/src/Rule/Model/DisallowEntityArrayAccessRule.php +++ b/src/Rule/Model/DisallowEntityArrayAccessRule.php @@ -13,7 +13,7 @@ class DisallowEntityArrayAccessRule implements Rule { /** - * @return string + * @inheritDoc */ public function getNodeType(): string { @@ -23,9 +23,8 @@ public function getNodeType(): string /** * @param \PhpParser\Node $node * @param \PHPStan\Analyser\Scope $scope - * @return array<\PHPStan\Rules\RuleError> + * @return list<\PHPStan\Rules\IdentifierRuleError> * @throws \PHPStan\ShouldNotHappenException - * @throws \PHPStan\Reflection\MissingMethodFromReflectionException */ public function processNode(Node $node, Scope $scope): array { diff --git a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php index 65cc17c..7322a41 100644 --- a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php +++ b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php @@ -18,10 +18,12 @@ use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ExtendedMethodReflection; +use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Rules\RuleLevelHelper; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; @@ -78,7 +80,7 @@ public function __construct(RuleLevelHelper $ruleLevelHelper) } /** - * @return string + * @inheritDoc */ public function getNodeType(): string { @@ -88,7 +90,7 @@ public function getNodeType(): string /** * @param \PhpParser\Node $node * @param \PHPStan\Analyser\Scope $scope - * @return array<\PHPStan\Rules\RuleError> + * @return list<\PHPStan\Rules\IdentifierRuleError> * @throws \PHPStan\ShouldNotHappenException * @throws \PHPStan\Reflection\MissingMethodFromReflectionException */ @@ -96,11 +98,11 @@ public function processNode(Node $node, Scope $scope): array { assert($node instanceof MethodCall); $args = $node->getArgs(); - if (!$node->name instanceof Node\Identifier || !in_array($node->name->name, $this->targetMethods)) { + if (!$node->name instanceof Node\Identifier || !in_array($node->name->name, $this->targetMethods, true)) { return []; } $referenceClasses = $scope->getType($node->var)->getReferencedClasses(); - if (empty($referenceClasses)) { + if ($referenceClasses === []) { return []; } try { @@ -112,24 +114,31 @@ public function processNode(Node $node, Scope $scope): array return []; } $specificFinderOptions = $this->getSpecificFinderOptions($details, $scope); - if (empty($details['options'])) { + if ($details['options'] === []) { return $this->checkMissingRequiredOptions($specificFinderOptions, $details, []); } $paramNamesIgnore = $this->getParamNamesIgnore($scope, $node); $errors = []; foreach ($details['options'] as $name => $item) { - if (in_array($name, $paramNamesIgnore)) { + if (in_array($name, $paramNamesIgnore, true)) { continue; } $parameterType = $this->getExpectedType($name, $scope, $specificFinderOptions); - $error = $parameterType ? $this->processPropertyTypeCheck( + $error = $parameterType !== null ? $this->processPropertyTypeCheck( $parameterType, $scope->getType($item), $details, $name ) : null; - if ($error) { + if ($error !== null) { + if (!$error instanceof IdentifierRuleError) { + throw new ShouldNotHappenException(\sprintf( + 'Expected error message to be instance of "%s", but got instance of "%s" instead.', + IdentifierRuleError::class, + $error::class, + )); + } $errors[] = $error; } } @@ -149,7 +158,7 @@ protected function getDetails(array $referenceClasses, string $methodName, array if ( str_ends_with($reference, 'Table') || $reference === SelectQuery::class - || in_array($reference, $this->associationTypes) + || in_array($reference, $this->associationTypes, true) ) { $lastOptionPosition = 1; $finder = 'all'; @@ -249,13 +258,13 @@ protected function getOptions(array $args, int $optionsArgPosition): array if ( count($args) === $totalArgsMethod && $args[$lastArgPos]->value instanceof Array_ - && !$args[$lastArgPos]->name + && $args[$lastArgPos]->name === null && $args[$lastArgPos]->unpack !== true ) { return $this->getOptionsFromArray($args[$lastArgPos]->value, $options); } foreach ($args as $arg) { - if ($arg->name) { + if ($arg->name !== null) { $options[$arg->name->name] = $arg->value; } $options = $this->extractOptionsUnpackedArray($arg, $options); @@ -315,7 +324,7 @@ protected function getReference(array $referenceClasses): string { $reference = $referenceClasses[0]; //Is association generic? ex: Cake\ORM\Association\BelongsTo - if (isset($referenceClasses[1]) && in_array($reference, $this->associationTypes)) { + if (isset($referenceClasses[1]) && in_array($reference, $this->associationTypes, true)) { $reference = $referenceClasses[1]; } @@ -331,7 +340,7 @@ protected function getSpecificFinderOptions(array $details, Scope $scope): array { $specificFinderOptions = []; $finder = $details['finder']; - if (!$finder || $finder === 'all') { + if ($finder === null || $finder === 'all') { return []; } $tableClass = $details['reference']; @@ -401,8 +410,8 @@ protected function getExpectedType(int|string $name, Scope $scope, array $specif /** * @param array $specificFinderOptions * @param array{'options': array<\PhpParser\Node\Expr>, 'reference':string, 'methodName':string, 'finder': string|null} $details - * @param array<\PHPStan\Rules\RuleError> $errors - * @return array<\PHPStan\Rules\RuleError> + * @param list<\PHPStan\Rules\IdentifierRuleError> $errors + * @return list<\PHPStan\Rules\IdentifierRuleError> * @throws \PHPStan\ShouldNotHappenException */ protected function checkMissingRequiredOptions(array $specificFinderOptions, array $details, array $errors): array diff --git a/src/Rule/Model/TableGetMatchOptionsTypesRule.php b/src/Rule/Model/TableGetMatchOptionsTypesRule.php index fd0c395..c2f0455 100644 --- a/src/Rule/Model/TableGetMatchOptionsTypesRule.php +++ b/src/Rule/Model/TableGetMatchOptionsTypesRule.php @@ -16,7 +16,7 @@ class TableGetMatchOptionsTypesRule extends OrmSelectQueryFindMatchOptionsTypesR protected function getDetails(array $referenceClasses, string $methodName, array $args): ?array { $reference = $this->getReference($referenceClasses); - if (str_ends_with($reference, 'Table') || in_array($reference, $this->associationTypes)) { + if (str_ends_with($reference, 'Table') || in_array($reference, $this->associationTypes, true)) { $lastOptionPosition = 4; $options = $this->getOptions($args, $lastOptionPosition); diff --git a/src/Testing/AnalyseCheckLineStartsWithTrait.php b/src/Testing/AnalyseCheckLineStartsWithTrait.php index 8e27a6c..0377e48 100644 --- a/src/Testing/AnalyseCheckLineStartsWithTrait.php +++ b/src/Testing/AnalyseCheckLineStartsWithTrait.php @@ -32,8 +32,8 @@ public function analyseCheckLineStartsWith(array $files, array $expected): void }, $actualErrors); $expected = array_map(static function (array $item) use ($messageText): string { - return $messageText((int)$item[1], (string)$item[0]); + return $messageText($item[1], $item[0]); }, $expected); - $this->assertThat($expected, new ArrayOfStringStartsWith($actualErrors)); + static::assertThat($expected, new ArrayOfStringStartsWith($actualErrors)); } } diff --git a/src/Traits/RepositoryReferenceTrait.php b/src/Traits/RepositoryReferenceTrait.php index f772954..9f211a7 100644 --- a/src/Traits/RepositoryReferenceTrait.php +++ b/src/Traits/RepositoryReferenceTrait.php @@ -35,7 +35,7 @@ protected function getReferenceClass(Scope $scope, MethodCall $methodCall): ?str if (!isset($classes[0])) { return null; } - if (!in_array($classes[0], $this->associationsClasses)) { + if (!in_array($classes[0], $this->associationsClasses, true)) { return $classes[0]; } //We should have key 1 for associations, ex: BelongsTo<\App\Model\Table\UsersTable> diff --git a/src/Type/BaseTraitExpressionTypeResolverExtension.php b/src/Type/BaseTraitExpressionTypeResolverExtension.php index 9473750..0d88c5c 100644 --- a/src/Type/BaseTraitExpressionTypeResolverExtension.php +++ b/src/Type/BaseTraitExpressionTypeResolverExtension.php @@ -94,7 +94,7 @@ protected function getBaseName(?Expr $value, ClassReflection $reflection): ?stri } try { - if ($value === null && $this->propertyDefaultValue) { + if ($value === null && $this->propertyDefaultValue !== null) { $default = $reflection->getNativeReflection() ->getProperty('defaultTable') ->getDefaultValueExpression(); diff --git a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php index d88b590..a63bc06 100644 --- a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php +++ b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php @@ -72,7 +72,7 @@ public function __construct(string $className) */ public function isMethodSupported(MethodReflection $methodReflection): bool { - return in_array($methodReflection->getName(), $this->methodNames); + return in_array($methodReflection->getName(), $this->methodNames, true); } /** @@ -95,7 +95,7 @@ public function getTypeFromMethodCall( $entityClass = $this->getEntityClassByTableClass($className); if ($entityClass !== null && class_exists($entityClass)) { - if ($methodReflection->getName() == 'newEntities') { + if ($methodReflection->getName() === 'newEntities') { return new ArrayType(new IntegerType(), new ObjectType($entityClass)); } diff --git a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php index c78665c..e6a1ccc 100644 --- a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php +++ b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php @@ -72,7 +72,7 @@ public function __construct(string $className) */ public function isMethodSupported(MethodReflection $methodReflection): bool { - return in_array($methodReflection->getName(), $this->methodNames); + return in_array($methodReflection->getName(), $this->methodNames, true); } /** @@ -95,7 +95,7 @@ public function getTypeFromMethodCall( $type = $scope->getType($args[0]->value); $name = $methodReflection->getName(); - if (in_array($name, ['save', 'saveMany', 'deleteMany'])) { + if (in_array($name, ['save', 'saveMany', 'deleteMany'], true)) { if ($type instanceof UnionType) { $types = $type->getTypes(); $types[] = new ConstantBooleanType(false); @@ -105,7 +105,7 @@ public function getTypeFromMethodCall( return new UnionType($types); } - if ($methodReflection->getName() == 'patchEntities') { + if ($methodReflection->getName() === 'patchEntities') { if (!$type->isIterable()->yes()) { return null; } From 7506a5e9470e7d9df10221a24906b47ddbcb20f2 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 17:14:11 -0300 Subject: [PATCH 26/34] PHPStan 2.0 Upgrade - Strict types fixes --- src/Constraint/ArrayOfStringStartsWith.php | 5 +++-- src/PhpDoc/TableAssociationTypeNodeResolverExtension.php | 6 +++--- src/Rule/LoadObjectExistsCakeClassRule.php | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Constraint/ArrayOfStringStartsWith.php b/src/Constraint/ArrayOfStringStartsWith.php index 9f81563..b27488c 100644 --- a/src/Constraint/ArrayOfStringStartsWith.php +++ b/src/Constraint/ArrayOfStringStartsWith.php @@ -37,7 +37,7 @@ public function toString(): string } /** - * @param array $other + * @param mixed $other * @return bool */ protected function matches(mixed $other): bool @@ -46,6 +46,7 @@ protected function matches(mixed $other): bool $this->notExpected = $this->actual; assert(is_array($other)); foreach ($other as $key => $error) { + $error = is_string($error) ? $error : ('Wrong error: ' . json_encode($error)); if (!isset($this->actual[$key])) { $this->result[$key] = ['expected' => $error, 'type' => 'missing', 'actual' => null]; $result = false; @@ -58,7 +59,7 @@ protected function matches(mixed $other): bool } } - return $result && empty($this->notExpected); + return $result && $this->notExpected === []; } /** diff --git a/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php b/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php index dadb99c..6fb3265 100644 --- a/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php +++ b/src/PhpDoc/TableAssociationTypeNodeResolverExtension.php @@ -64,20 +64,20 @@ public function resolve(TypeNode $typeNode, NameScope $nameScope): ?Type 'table' => null, ]; foreach ($types as $type) { - if (!$type->isObject()) { + if (!$type->isObject()->yes()) { continue; } $className = $type->getObjectClassNames()[0] ?? null; if ($className === null) { continue; } - if ($config['association'] === null && in_array($className, $this->associationTypes)) { + if ($config['association'] === null && in_array($className, $this->associationTypes, true)) { $config['association'] = $type; } elseif ($config['table'] === null && str_ends_with($className, 'Table')) { $config['table'] = $type; } } - if ($config['table'] && $config['association']) { + if ($config['table'] !== null && $config['association'] !== null) { return new GenericObjectType( $config['association']->getObjectClassNames()[0], [$config['table']] diff --git a/src/Rule/LoadObjectExistsCakeClassRule.php b/src/Rule/LoadObjectExistsCakeClassRule.php index 0af86ec..7cbea77 100644 --- a/src/Rule/LoadObjectExistsCakeClassRule.php +++ b/src/Rule/LoadObjectExistsCakeClassRule.php @@ -33,7 +33,7 @@ abstract class LoadObjectExistsCakeClassRule implements Rule protected string $identifier; /** - * @return string + * @inheritDoc */ public function getNodeType(): string { @@ -43,7 +43,7 @@ public function getNodeType(): string /** * @param \PhpParser\Node $node * @param \PHPStan\Analyser\Scope $scope - * @return array<\PHPStan\Rules\RuleError> + * @return list<\PHPStan\Rules\IdentifierRuleError> */ public function processNode(Node $node, Scope $scope): array { @@ -60,7 +60,7 @@ public function processNode(Node $node, Scope $scope): array if ( $details === null - || !in_array($node->name->name, $details['sourceMethods']) + || !in_array($node->name->name, $details['sourceMethods'], true) || !$details['alias'] instanceof Arg || !$details['alias']->value instanceof String_ ) { @@ -74,7 +74,7 @@ public function processNode(Node $node, Scope $scope): array $details['options'] ); } - if ($inputClassName === null || $this->getTargetClassName($inputClassName)) { + if ($inputClassName === null || $this->getTargetClassName($inputClassName) !== null) { return []; } From 399df91e484e9b4723d00412cb19681ca8be0a85 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 17:39:56 -0300 Subject: [PATCH 27/34] PHPStan 2.0 Upgrade - Updated composer and temporary remove symplify/phpstan-rules until they release a compatible version. --- composer.json | 13 +++++++------ phpstan-test-app.neon | 3 --- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index f310db8..e4183a1 100644 --- a/composer.json +++ b/composer.json @@ -12,16 +12,15 @@ ], "require": { "php": ">=8.1.0", - "phpstan/phpstan": "^1.12", + "phpstan/phpstan": "^2.0", "cakephp/cakephp": "^5.0" }, "require-dev": { - "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^10.1", "cakephp/cakephp-codesniffer": "^5.0", - "symplify/phpstan-rules": "^12.4", - "phpstan/phpstan-deprecation-rules": "^1.2", - "phpstan/phpstan-strict-rules": "^1.6" + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0" }, "extra": { "phpstan": { @@ -62,5 +61,7 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } - } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/phpstan-test-app.neon b/phpstan-test-app.neon index acaa6ce..b0b3d65 100644 --- a/phpstan-test-app.neon +++ b/phpstan-test-app.neon @@ -2,9 +2,6 @@ includes: - extension.neon - phar://phpstan.phar/conf/bleedingEdge.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon -rules: - - Symplify\PHPStanRules\Rules\Explicit\NoMixedPropertyFetcherRule - - Symplify\PHPStanRules\Rules\Explicit\NoMixedMethodCallerRule parameters: level: max paths: From 8fe4c82809239e894fe33b87a70338d576192c45 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 17:41:09 -0300 Subject: [PATCH 28/34] PHPStan 2.0 Upgrade - Updated composer and temporary remove symplify/phpstan-rules until they release a compatible version. --- src/Constraint/ArrayOfStringStartsWith.php | 2 +- src/Rule/Model/AddAssociationMatchOptionsTypesRule.php | 1 - src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Constraint/ArrayOfStringStartsWith.php b/src/Constraint/ArrayOfStringStartsWith.php index b27488c..e02bc6f 100644 --- a/src/Constraint/ArrayOfStringStartsWith.php +++ b/src/Constraint/ArrayOfStringStartsWith.php @@ -46,7 +46,7 @@ protected function matches(mixed $other): bool $this->notExpected = $this->actual; assert(is_array($other)); foreach ($other as $key => $error) { - $error = is_string($error) ? $error : ('Wrong error: ' . json_encode($error)); + $error = is_string($error) ? $error : 'Wrong error: ' . json_encode($error); if (!isset($this->actual[$key])) { $this->result[$key] = ['expected' => $error, 'type' => 'missing', 'actual' => null]; $result = false; diff --git a/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php b/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php index ce19cde..0f65a76 100644 --- a/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php +++ b/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php @@ -18,7 +18,6 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; -use PHPStan\Rules\RuleError; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Rules\RuleLevelHelper; use PHPStan\Type\ObjectType; diff --git a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php index 7322a41..36c696f 100644 --- a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php +++ b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php @@ -28,6 +28,7 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\VerbosityLevel; +use function sprintf; class OrmSelectQueryFindMatchOptionsTypesRule implements Rule { @@ -133,7 +134,7 @@ public function processNode(Node $node, Scope $scope): array if ($error !== null) { if (!$error instanceof IdentifierRuleError) { - throw new ShouldNotHappenException(\sprintf( + throw new ShouldNotHappenException(sprintf( 'Expected error message to be instance of "%s", but got instance of "%s" instead.', IdentifierRuleError::class, $error::class, From 5140e8df4ddfa4d1903906a38932d0134f4504d2 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 17:56:57 -0300 Subject: [PATCH 29/34] PHPStan 2.0 Upgrade --- src/Traits/BaseCakeRegistryReturnTrait.php | 10 ++++++---- src/Type/ComponentLoadDynamicReturnTypeExtension.php | 2 +- .../ConsoleHelperLoadDynamicReturnTypeExtension.php | 2 +- .../RepositoryEntityDynamicReturnTypeExtension.php | 4 ++-- .../RepositoryFirstArgIsTheReturnTypeExtension.php | 4 ++-- src/Type/TableLocatorDynamicReturnTypeExtension.php | 4 ++-- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Traits/BaseCakeRegistryReturnTrait.php b/src/Traits/BaseCakeRegistryReturnTrait.php index 306d26d..8df3ab5 100644 --- a/src/Traits/BaseCakeRegistryReturnTrait.php +++ b/src/Traits/BaseCakeRegistryReturnTrait.php @@ -44,14 +44,16 @@ public function getTypeFromMethodCall( if (!method_exists($argType, 'getValue')) { return new ObjectType($this->defaultClass); } + $value = $argType->getValue(); + if (!is_string($value)) { + return null; + } - return $this->getCakeType($argType->getValue()); + return $this->getCakeType($value); } /** - * Get the target class. - * - * @return string + * @inheritDoc */ public function getClass(): string { diff --git a/src/Type/ComponentLoadDynamicReturnTypeExtension.php b/src/Type/ComponentLoadDynamicReturnTypeExtension.php index 3db22bf..e5eb1a5 100644 --- a/src/Type/ComponentLoadDynamicReturnTypeExtension.php +++ b/src/Type/ComponentLoadDynamicReturnTypeExtension.php @@ -23,7 +23,7 @@ class ComponentLoadDynamicReturnTypeExtension implements DynamicMethodReturnType use BaseCakeRegistryReturnTrait; /** - * @var string + * @var class-string */ private string $className; /** diff --git a/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php b/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php index 16922f1..f2cd3db 100644 --- a/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php +++ b/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php @@ -26,7 +26,7 @@ class ConsoleHelperLoadDynamicReturnTypeExtension implements DynamicMethodReturn } /** - * @var string + * @var class-string */ private string $className; /** diff --git a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php index a63bc06..1745cf5 100644 --- a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php +++ b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php @@ -33,7 +33,7 @@ class RepositoryEntityDynamicReturnTypeExtension implements DynamicMethodReturnT use RepositoryReferenceTrait; /** - * @var string + * @var class-string */ private string $className; /** @@ -57,7 +57,7 @@ class RepositoryEntityDynamicReturnTypeExtension implements DynamicMethodReturnT protected string $namespaceFormat; /** - * @param string $className The target className. + * @param class-string $className The target className. */ public function __construct(string $className) { diff --git a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php index e6a1ccc..fc60958 100644 --- a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php +++ b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php @@ -43,7 +43,7 @@ class RepositoryFirstArgIsTheReturnTypeExtension implements DynamicMethodReturnT 'deleteManyOrFail', ]; /** - * @var string + * @var class-string */ protected string $className; @@ -57,7 +57,7 @@ class RepositoryFirstArgIsTheReturnTypeExtension implements DynamicMethodReturnT protected string $namespaceFormat; /** - * @param string $className The target className. + * @param class-string $className The target className. */ public function __construct(string $className) { diff --git a/src/Type/TableLocatorDynamicReturnTypeExtension.php b/src/Type/TableLocatorDynamicReturnTypeExtension.php index 95e8168..954f844 100644 --- a/src/Type/TableLocatorDynamicReturnTypeExtension.php +++ b/src/Type/TableLocatorDynamicReturnTypeExtension.php @@ -31,7 +31,7 @@ class TableLocatorDynamicReturnTypeExtension implements DynamicMethodReturnTypeE } /** - * @var string + * @var class-string */ protected string $className; /** @@ -44,7 +44,7 @@ class TableLocatorDynamicReturnTypeExtension implements DynamicMethodReturnTypeE /** * TableLocatorDynamicReturnTypeExtension constructor. * - * @param string $className The target className. + * @param class-string $className The target className. * @param string $methodName The dynamic method to handle. */ public function __construct(string $className, string $methodName) From d0a677d7b1af230fe5582f7e429b7efaaec250fa Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 18:11:36 -0300 Subject: [PATCH 30/34] PHPStan 2.0 Upgrade --- src/Method/TableFindByPropertyMethodReflection.php | 4 ++-- src/Rule/Model/AddAssociationMatchOptionsTypesRule.php | 10 ++++------ .../Model/OrmSelectQueryFindMatchOptionsTypesRule.php | 8 ++++---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Method/TableFindByPropertyMethodReflection.php b/src/Method/TableFindByPropertyMethodReflection.php index 400890c..7b2813c 100644 --- a/src/Method/TableFindByPropertyMethodReflection.php +++ b/src/Method/TableFindByPropertyMethodReflection.php @@ -32,7 +32,7 @@ class TableFindByPropertyMethodReflection implements MethodReflection private ClassReflection $declaringClass; /** - * @var array<\PHPStan\Reflection\FunctionVariant> + * @var list<\PHPStan\Reflection\FunctionVariant> */ private array $variants; @@ -205,7 +205,7 @@ public function hasSideEffects(): TrinaryLogic /** * @param string $method - * @return array + * @return list */ protected function getParams(string $method): array { diff --git a/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php b/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php index 0f65a76..9dccf4d 100644 --- a/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php +++ b/src/Rule/Model/AddAssociationMatchOptionsTypesRule.php @@ -11,7 +11,7 @@ use CakeDC\PHPStan\Rule\Traits\ParseClassNameFromArgTrait; use PhpParser\Node; use PhpParser\Node\Arg; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\ArrayItem; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; @@ -59,9 +59,7 @@ public function getNodeType(): string } /** - * @param \PhpParser\Node $node - * @param \PHPStan\Analyser\Scope $scope - * @return list<\PHPStan\Rules\IdentifierRuleError> + * @inheritDoc */ public function processNode(Node $node, Scope $scope): array { @@ -156,7 +154,7 @@ protected function getDetails(string $reference, string $methodName, array $args /** * @param array{'alias': ?string, 'options': ?\PhpParser\Node\Arg, 'type': string, 'reference':string, 'methodName':string} $details * @param string $property - * @param \PhpParser\Node\Expr\ArrayItem $item + * @param \PhpParser\Node\ArrayItem $item * @param \PHPStan\Analyser\Scope $scope * @return \PHPStan\Rules\IdentifierRuleError|null * @throws \PHPStan\Reflection\MissingPropertyFromReflectionException @@ -175,7 +173,7 @@ protected function processPropertyTypeCheck( ->getProperty('_' . $property, $scope) ->getWritableType(); $assignedValueType = $scope->getType($item->value); - $accepts = $this->ruleLevelHelper->acceptsWithReason($propertyType, $assignedValueType, true);//@phpstan-ignore-line + $accepts = $this->ruleLevelHelper->accepts($propertyType, $assignedValueType, true); if ($accepts->result) { return null; } diff --git a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php index 36c696f..40cbc34 100644 --- a/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php +++ b/src/Rule/Model/OrmSelectQueryFindMatchOptionsTypesRule.php @@ -193,7 +193,7 @@ protected function processPropertyTypeCheck( array $details, string $property ): ?RuleError { - $accepts = $this->ruleLevelHelper->acceptsWithReason($inputType, $assignedValueType, true);//@phpstan-ignore-line + $accepts = $this->ruleLevelHelper->accepts($inputType, $assignedValueType, true); if ($accepts->result) { return null; @@ -335,7 +335,7 @@ protected function getReference(array $referenceClasses): string /** * @param array{'options': array<\PhpParser\Node\Expr>, 'reference':string, 'methodName':string, 'finder': string|null} $details * @param \PHPStan\Analyser\Scope $scope - * @return array + * @return array */ protected function getSpecificFinderOptions(array $details, Scope $scope): array { @@ -385,7 +385,7 @@ protected function getSpecificFinderOptions(array $details, Scope $scope): array /** * @param string|int $name * @param \PHPStan\Analyser\Scope $scope - * @param array $specificFinderOptions + * @param array $specificFinderOptions * @return \PHPStan\Type\Type|null * @throws \PHPStan\Reflection\MissingMethodFromReflectionException */ @@ -409,7 +409,7 @@ protected function getExpectedType(int|string $name, Scope $scope, array $specif } /** - * @param array $specificFinderOptions + * @param array $specificFinderOptions * @param array{'options': array<\PhpParser\Node\Expr>, 'reference':string, 'methodName':string, 'finder': string|null} $details * @param list<\PHPStan\Rules\IdentifierRuleError> $errors * @return list<\PHPStan\Rules\IdentifierRuleError> From 0bd8dfb26808d08e21c6b534e7cb61b74096b0fe Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 18:13:38 -0300 Subject: [PATCH 31/34] PHPStan 2.0 Upgrade --- phpstan-test-plugin.neon | 3 --- tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/phpstan-test-plugin.neon b/phpstan-test-plugin.neon index 1f605f3..6debf35 100644 --- a/phpstan-test-plugin.neon +++ b/phpstan-test-plugin.neon @@ -2,9 +2,6 @@ includes: - extension.neon - phar://phpstan.phar/conf/bleedingEdge.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon -rules: - - Symplify\PHPStanRules\Rules\Explicit\NoMixedPropertyFetcherRule - - Symplify\PHPStanRules\Rules\Explicit\NoMixedMethodCallerRule parameters: level: max paths: diff --git a/tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php b/tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php index c6c67a9..3fc87e8 100644 --- a/tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php +++ b/tests/test_app/Model/Table/VeryCustomize00009ArticlesTable.php @@ -25,8 +25,7 @@ class VeryCustomize00009ArticlesTable extends Table { /** - * @param string[] $config Configuration options passed to the constructor - * @return void + * @inheritDoc */ public function initialize(array $config): void { From 64f908e0d1b33025586b3d8650cdfc173b64ddb4 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 18:35:07 -0300 Subject: [PATCH 32/34] PHPStan 2.0 Upgrade - bleedingEdge is not necessary after migration. --- phpstan-test-app.neon | 1 - phpstan-test-plugin.neon | 1 - phpstan.neon | 1 - 3 files changed, 3 deletions(-) diff --git a/phpstan-test-app.neon b/phpstan-test-app.neon index b0b3d65..c250b63 100644 --- a/phpstan-test-app.neon +++ b/phpstan-test-app.neon @@ -1,6 +1,5 @@ includes: - extension.neon - - phar://phpstan.phar/conf/bleedingEdge.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon parameters: level: max diff --git a/phpstan-test-plugin.neon b/phpstan-test-plugin.neon index 6debf35..a8f89ef 100644 --- a/phpstan-test-plugin.neon +++ b/phpstan-test-plugin.neon @@ -1,6 +1,5 @@ includes: - extension.neon - - phar://phpstan.phar/conf/bleedingEdge.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon parameters: level: max diff --git a/phpstan.neon b/phpstan.neon index 6fa3973..b6df14b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,6 +1,5 @@ includes: - extension.neon - - phar://phpstan.phar/conf/bleedingEdge.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon - vendor/phpstan/phpstan-strict-rules/rules.neon parameters: From 5abe96dd8ad4c4472b3ebbf929db46b32e21d364 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 18:35:51 -0300 Subject: [PATCH 33/34] PHPStan 2.0 Upgrade - fix ci --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5567260..4686feb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,8 +76,8 @@ jobs: - name: Run phpstan integration test app if: always() - run: vendor/bin/phpstan analyse --debug --error-format=github tests/test_app/ + run: vendor/bin/phpstan analyse --debug -c phpstan-test-app.neon --error-format=github tests/test_app/ - name: Run phpstan integration test plugin if: always() - run: vendor/bin/phpstan analyse --debug --error-format=github tests/test_plugin/ + run: vendor/bin/phpstan analyse --debug -c phpstan-test-plugin.neon --error-format=github tests/test_plugin/ From 2d6b6930499fea8b75e9b31502dcefb7962254c1 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 1 Nov 2024 18:49:20 -0300 Subject: [PATCH 34/34] PHPStan 2.0 Upgrade - Cleanup extension usage for fetchTable --- extension.neon | 16 ---------------- .../Controller/Component/WarningComponent.php | 4 ++++ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/extension.neon b/extension.neon index 7d8faa2..0d47eed 100644 --- a/extension.neon +++ b/extension.neon @@ -14,22 +14,6 @@ services: factory: CakeDC\PHPStan\Type\ControllerFetchTableDynamicReturnTypeExtension(Cake\Controller\Controller, fetchTable) tags: - phpstan.broker.dynamicMethodReturnTypeExtension - - - factory: CakeDC\PHPStan\Type\TableLocatorDynamicReturnTypeExtension(Cake\Command\Command, fetchTable) - tags: - - phpstan.broker.dynamicMethodReturnTypeExtension - - - factory: CakeDC\PHPStan\Type\TableLocatorDynamicReturnTypeExtension(Cake\Mailer\Mailer, fetchTable) - tags: - - phpstan.broker.dynamicMethodReturnTypeExtension - - - factory: CakeDC\PHPStan\Type\TableLocatorDynamicReturnTypeExtension(Cake\View\Cell, fetchTable) - tags: - - phpstan.broker.dynamicMethodReturnTypeExtension - - - factory: CakeDC\PHPStan\Type\TableLocatorDynamicReturnTypeExtension(Cake\Controller\Component, fetchTable) - tags: - - phpstan.broker.dynamicMethodReturnTypeExtension - factory: CakeDC\PHPStan\Type\TableLocatorDynamicReturnTypeExtension(Cake\ORM\Locator\LocatorInterface, get) tags: diff --git a/tests/test_app/Controller/Component/WarningComponent.php b/tests/test_app/Controller/Component/WarningComponent.php index 12be98f..9582a62 100644 --- a/tests/test_app/Controller/Component/WarningComponent.php +++ b/tests/test_app/Controller/Component/WarningComponent.php @@ -14,9 +14,12 @@ namespace App\Controller\Component; use Cake\Controller\Component; +use Cake\ORM\Locator\LocatorAwareTrait; class WarningComponent extends Component { + use LocatorAwareTrait; + /** * @return void */ @@ -27,5 +30,6 @@ public function testWarning() */ $controller = $this->getController(); $controller->fetchTable()->warning(); + $this->fetchTable('Users')->blockOld(); } }