From 48d0f3e8a978839d302ec7f40702ec881dd08ea5 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Fri, 26 Jun 2026 18:07:10 +0200 Subject: [PATCH 1/3] bump to PHP 8.4 --- .github/workflows/code_analysis.yaml | 2 +- .github/workflows/rector.yaml | 2 +- composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index b5d58010..b161284d 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -50,7 +50,7 @@ jobs: - uses: shivammathur/setup-php@v2 with: - php-version: 8.3 + php-version: 8.4 coverage: none - uses: "ramsey/composer-install@v2" diff --git a/.github/workflows/rector.yaml b/.github/workflows/rector.yaml index bf8faa43..2f3082a5 100644 --- a/.github/workflows/rector.yaml +++ b/.github/workflows/rector.yaml @@ -23,7 +23,7 @@ jobs: - uses: shivammathur/setup-php@v2 with: - php-version: 8.3 + php-version: 8.4 coverage: none - uses: "ramsey/composer-install@v2" diff --git a/composer.json b/composer.json index b248418b..238f7244 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Rector upgrades rules for PHPUnit", "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { "boundwize/structarmed": "^0.9", From 27b62d6dc3d67eb1be33e32646d3fb0e7036c06a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Jun 2026 16:07:48 +0000 Subject: [PATCH 2/3] [rector] Rector fixes --- .../NodeAnalyser/DoctrineEntityDocumentAnalyser.php | 11 +---------- ...eplaceTestAnnotationWithPrefixedFunctionRector.php | 9 +-------- .../Rector/Class_/PreferTestsWithCamelCaseRector.php | 7 ++++--- .../Rector/Class_/PreferTestsWithSnakeCaseRector.php | 3 ++- .../AssertArrayCastedObjectToAssertSameRector.php | 9 +-------- ...ompareOnCountableWithMethodToAssertCountRector.php | 2 +- .../Class_/AllowMockObjectsWhereParentClassRector.php | 9 +-------- ...wMockObjectsWithoutExpectationsAttributeRector.php | 8 +------- src/NodeAnalyzer/AssertCallAnalyzer.php | 9 +-------- src/NodeAnalyzer/TestsNodeAnalyzer.php | 9 +-------- 10 files changed, 14 insertions(+), 62 deletions(-) diff --git a/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php b/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php index 2da29616..7e6573d6 100644 --- a/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php +++ b/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php @@ -31,15 +31,6 @@ public function isEntityClass(string $className): bool if (! $resolvedPhpDocBlock instanceof ResolvedPhpDocBlock) { return false; } - - foreach (self::ENTITY_DOCBLOCK_MARKERS as $entityDocBlockMarkers) { - if (str_contains($resolvedPhpDocBlock->getPhpDocString(), $entityDocBlockMarkers)) { - return true; - } - } - - // @todo apply attributes as well - - return false; + return array_any(self::ENTITY_DOCBLOCK_MARKERS, fn(string $entityDocBlockMarkers): bool => str_contains($resolvedPhpDocBlock->getPhpDocString(), $entityDocBlockMarkers)); } } diff --git a/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php b/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php index c7e3d88e..065a7ff7 100644 --- a/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php +++ b/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php @@ -85,14 +85,7 @@ public function refactor(Node $node): ?Node if (! $docComment instanceof Doc) { return null; } - - $hasAnnotation = false; - foreach (NewLineSplitter::split($docComment->getText()) as $row) { - if (in_array(trim($row), ['*@test', '* @test'], true)) { - $hasAnnotation = true; - break; - } - } + $hasAnnotation = array_any(NewLineSplitter::split($docComment->getText()), fn(string $row): bool => in_array(trim($row), ['*@test', '* @test'], true)); if (! $hasAnnotation) { return null; diff --git a/rules/CodeQuality/Rector/Class_/PreferTestsWithCamelCaseRector.php b/rules/CodeQuality/Rector/Class_/PreferTestsWithCamelCaseRector.php index 0a4d5f29..d97a0c49 100644 --- a/rules/CodeQuality/Rector/Class_/PreferTestsWithCamelCaseRector.php +++ b/rules/CodeQuality/Rector/Class_/PreferTestsWithCamelCaseRector.php @@ -4,6 +4,7 @@ namespace Rector\PHPUnit\CodeQuality\Rector\Class_; +use PhpParser\Node\Identifier; use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; @@ -86,7 +87,7 @@ public function refactor(Node $node): ?Node continue; } - $classMethod->name = new Node\Identifier($newName); + $classMethod->name = new Identifier($newName); $hasChanged = true; } @@ -100,8 +101,8 @@ public function refactor(Node $node): ?Node private function toCamelCase(string $value): string { $words = explode(' ', str_replace(['-', '_'], ' ', $value)); - $words = array_map(fn (string $word): string => ucfirst($word), $words); + $words = array_map(ucfirst(...), $words); - return lcfirst(implode($words)); + return lcfirst(implode('', $words)); } } diff --git a/rules/CodeQuality/Rector/Class_/PreferTestsWithSnakeCaseRector.php b/rules/CodeQuality/Rector/Class_/PreferTestsWithSnakeCaseRector.php index 6e5a3838..497861a3 100644 --- a/rules/CodeQuality/Rector/Class_/PreferTestsWithSnakeCaseRector.php +++ b/rules/CodeQuality/Rector/Class_/PreferTestsWithSnakeCaseRector.php @@ -4,6 +4,7 @@ namespace Rector\PHPUnit\CodeQuality\Rector\Class_; +use PhpParser\Node\Identifier; use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer; @@ -86,7 +87,7 @@ public function refactor(Node $node): ?Node continue; } - $classMethod->name = new Node\Identifier($newName); + $classMethod->name = new Identifier($newName); $hasChanged = true; } diff --git a/rules/CodeQuality/Rector/Expression/AssertArrayCastedObjectToAssertSameRector.php b/rules/CodeQuality/Rector/Expression/AssertArrayCastedObjectToAssertSameRector.php index 917203f1..b4c729ec 100644 --- a/rules/CodeQuality/Rector/Expression/AssertArrayCastedObjectToAssertSameRector.php +++ b/rules/CodeQuality/Rector/Expression/AssertArrayCastedObjectToAssertSameRector.php @@ -161,13 +161,6 @@ public function refactor(Node $node): ?array */ private function hasAllKeysString(array $assertedArrayValues): bool { - // all keys must be string - foreach (array_keys($assertedArrayValues) as $key) { - if (! is_string($key)) { - return false; - } - } - - return true; + return array_all(array_keys($assertedArrayValues), fn(int|string $key): bool => is_string($key)); } } diff --git a/rules/CodeQuality/Rector/MethodCall/AssertCompareOnCountableWithMethodToAssertCountRector.php b/rules/CodeQuality/Rector/MethodCall/AssertCompareOnCountableWithMethodToAssertCountRector.php index c5802481..b107b449 100644 --- a/rules/CodeQuality/Rector/MethodCall/AssertCompareOnCountableWithMethodToAssertCountRector.php +++ b/rules/CodeQuality/Rector/MethodCall/AssertCompareOnCountableWithMethodToAssertCountRector.php @@ -104,7 +104,7 @@ public function refactor(Node $node): MethodCall|StaticCall|null ) { $type = $this->getType($comparedExpr->var); - if ((new ObjectType('Countable'))->isSuperTypeOf($type)->yes()) { + if (new ObjectType('Countable')->isSuperTypeOf($type)->yes()) { $args = $assertArgs; $args[1] = new Arg($comparedExpr->var); diff --git a/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWhereParentClassRector.php b/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWhereParentClassRector.php index a091dd81..09f307ea 100644 --- a/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWhereParentClassRector.php +++ b/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWhereParentClassRector.php @@ -118,13 +118,6 @@ private function hasRelateParentClass(Class_ $class): bool if (! $classReflection instanceof ClassReflection) { return false; } - - foreach (self::PARENT_CLASSES as $parentClass) { - if ($classReflection->is($parentClass)) { - return true; - } - } - - return false; + return array_any(self::PARENT_CLASSES, fn(string $parentClass): bool => $classReflection->is($parentClass)); } } diff --git a/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php b/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php index 69dfb3cb..a6be0425 100644 --- a/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php +++ b/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php @@ -270,13 +270,7 @@ private function shouldAddAttribute(array $missedTestMethodsByMockPropertyName): */ private function isAtLeastOneMockPropertyMockedOnce(array $usingTestMethodsByMockPropertyName): bool { - foreach ($usingTestMethodsByMockPropertyName as $usingTestMethods) { - if (count($usingTestMethods) !== 0) { - return true; - } - } - - return false; + return array_any($usingTestMethodsByMockPropertyName, fn(array $usingTestMethods): bool => $usingTestMethods !== []); } private function isMissingExpectsOnMockObjectMethodCallInSetUp(Class_ $class): bool diff --git a/src/NodeAnalyzer/AssertCallAnalyzer.php b/src/NodeAnalyzer/AssertCallAnalyzer.php index 141c07ca..2cfbdc97 100644 --- a/src/NodeAnalyzer/AssertCallAnalyzer.php +++ b/src/NodeAnalyzer/AssertCallAnalyzer.php @@ -96,14 +96,7 @@ public function isAssertMethodCall(MethodCall|StaticCall $call): bool if (! is_string($callName)) { return false; } - - foreach (self::ASSERT_METHOD_NAME_PREFIXES as $assertMethodNamePrefix) { - if (str_starts_with($callName, $assertMethodNamePrefix)) { - return true; - } - } - - return false; + return array_any(self::ASSERT_METHOD_NAME_PREFIXES, fn(string $assertMethodNamePrefix): bool => str_starts_with($callName, $assertMethodNamePrefix)); } private function hasDirectAssertOrMockCall(ClassMethod $classMethod): bool diff --git a/src/NodeAnalyzer/TestsNodeAnalyzer.php b/src/NodeAnalyzer/TestsNodeAnalyzer.php index 493f5878..be897339 100644 --- a/src/NodeAnalyzer/TestsNodeAnalyzer.php +++ b/src/NodeAnalyzer/TestsNodeAnalyzer.php @@ -34,14 +34,7 @@ public function isInTestClass(Node $node): bool if (! $classReflection instanceof ClassReflection) { return false; } - - foreach (PHPUnitClassName::TEST_CLASSES as $testCaseObjectClass) { - if ($classReflection->is($testCaseObjectClass)) { - return true; - } - } - - return false; + return array_any(PHPUnitClassName::TEST_CLASSES, fn(string $testCaseObjectClass): bool => $classReflection->is($testCaseObjectClass)); } public function isTestClassMethod(ClassMethod $classMethod): bool From d3ae117bbc6bfbbb4357ff6e4caf3e787c90899a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 26 Jun 2026 16:08:19 +0000 Subject: [PATCH 3/3] [rector] Rector fixes --- .../CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php | 1 + .../ReplaceTestAnnotationWithPrefixedFunctionRector.php | 1 + .../Rector/Class_/AllowMockObjectsWhereParentClassRector.php | 1 + src/NodeAnalyzer/AssertCallAnalyzer.php | 1 + src/NodeAnalyzer/TestsNodeAnalyzer.php | 1 + 5 files changed, 5 insertions(+) diff --git a/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php b/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php index 7e6573d6..e73200ea 100644 --- a/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php +++ b/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php @@ -31,6 +31,7 @@ public function isEntityClass(string $className): bool if (! $resolvedPhpDocBlock instanceof ResolvedPhpDocBlock) { return false; } + return array_any(self::ENTITY_DOCBLOCK_MARKERS, fn(string $entityDocBlockMarkers): bool => str_contains($resolvedPhpDocBlock->getPhpDocString(), $entityDocBlockMarkers)); } } diff --git a/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php b/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php index 065a7ff7..bbf6ea55 100644 --- a/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php +++ b/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php @@ -85,6 +85,7 @@ public function refactor(Node $node): ?Node if (! $docComment instanceof Doc) { return null; } + $hasAnnotation = array_any(NewLineSplitter::split($docComment->getText()), fn(string $row): bool => in_array(trim($row), ['*@test', '* @test'], true)); if (! $hasAnnotation) { diff --git a/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWhereParentClassRector.php b/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWhereParentClassRector.php index 09f307ea..ba35cfa7 100644 --- a/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWhereParentClassRector.php +++ b/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWhereParentClassRector.php @@ -118,6 +118,7 @@ private function hasRelateParentClass(Class_ $class): bool if (! $classReflection instanceof ClassReflection) { return false; } + return array_any(self::PARENT_CLASSES, fn(string $parentClass): bool => $classReflection->is($parentClass)); } } diff --git a/src/NodeAnalyzer/AssertCallAnalyzer.php b/src/NodeAnalyzer/AssertCallAnalyzer.php index 2cfbdc97..7908aa42 100644 --- a/src/NodeAnalyzer/AssertCallAnalyzer.php +++ b/src/NodeAnalyzer/AssertCallAnalyzer.php @@ -96,6 +96,7 @@ public function isAssertMethodCall(MethodCall|StaticCall $call): bool if (! is_string($callName)) { return false; } + return array_any(self::ASSERT_METHOD_NAME_PREFIXES, fn(string $assertMethodNamePrefix): bool => str_starts_with($callName, $assertMethodNamePrefix)); } diff --git a/src/NodeAnalyzer/TestsNodeAnalyzer.php b/src/NodeAnalyzer/TestsNodeAnalyzer.php index be897339..6139ba5e 100644 --- a/src/NodeAnalyzer/TestsNodeAnalyzer.php +++ b/src/NodeAnalyzer/TestsNodeAnalyzer.php @@ -34,6 +34,7 @@ public function isInTestClass(Node $node): bool if (! $classReflection instanceof ClassReflection) { return false; } + return array_any(PHPUnitClassName::TEST_CLASSES, fn(string $testCaseObjectClass): bool => $classReflection->is($testCaseObjectClass)); }