From 0d8aeab01b38cc9dff4e691bb63d6d9b05d11c8a Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 12 May 2026 06:53:50 +0700 Subject: [PATCH 1/4] [CodeQuality] Handle on static closure on WithCallbackIdenticalToStandaloneAssertsRector --- .../Fixture/on_static_closure.php.inc | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 rules-tests/CodeQuality/Rector/MethodCall/WithCallbackIdenticalToStandaloneAssertsRector/Fixture/on_static_closure.php.inc diff --git a/rules-tests/CodeQuality/Rector/MethodCall/WithCallbackIdenticalToStandaloneAssertsRector/Fixture/on_static_closure.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/WithCallbackIdenticalToStandaloneAssertsRector/Fixture/on_static_closure.php.inc new file mode 100644 index 00000000..b6cc6177 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/WithCallbackIdenticalToStandaloneAssertsRector/Fixture/on_static_closure.php.inc @@ -0,0 +1,47 @@ +createMock('SomeClass') + ->expects($this->once()) + ->method('someMethod') + ->with($this->callback(static function (array $args): bool { + return count($args) === 2 && $args[0] === 'correct'; + })); + } +} + +?> +----- +createMock('SomeClass') + ->expects($this->once()) + ->method('someMethod') + ->with($this->callback(static function (array $args): bool { + self::assertCount(2, $args); + self::assertSame('correct', $args[0]); + return true; + })); + } +} + +?> From 730c10bc4a9de23c6cb9c1a2032b150d11c0f8e8 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 12 May 2026 06:55:22 +0700 Subject: [PATCH 2/4] fix --- .../FromBinaryAndAssertExpressionsFactory.php | 37 +++++++++++++------ ...backIdenticalToStandaloneAssertsRector.php | 6 ++- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/rules/CodeQuality/NodeFactory/FromBinaryAndAssertExpressionsFactory.php b/rules/CodeQuality/NodeFactory/FromBinaryAndAssertExpressionsFactory.php index 9b25d553..44fa633b 100644 --- a/rules/CodeQuality/NodeFactory/FromBinaryAndAssertExpressionsFactory.php +++ b/rules/CodeQuality/NodeFactory/FromBinaryAndAssertExpressionsFactory.php @@ -13,6 +13,7 @@ use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node\Expr\Isset_; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Scalar\Int_; @@ -33,14 +34,14 @@ public function __construct( * @param Expr[] $exprs * @return Stmt[] */ - public function create(array $exprs): array + public function create(array $exprs, bool $isStaticClosure = false): array { $assertMethodCalls = []; foreach ($exprs as $expr) { // implicit bool compare if ($expr instanceof MethodCall) { - $assertMethodCalls[] = $this->nodeFactory->createMethodCall('this', 'assertTrue', [$expr]); + $assertMethodCalls[] = $this->createAssertMethodCall($isStaticClosure, 'assertTrue', [$expr]); continue; } @@ -51,8 +52,8 @@ public function create(array $exprs): array $dimExpr = $expr->getArgs()[0] ->value; - $assertMethodCalls[] = $this->nodeFactory->createMethodCall( - 'this', + $assertMethodCalls[] = $this->createAssertMethodCall( + $isStaticClosure, 'assertArrayHasKey', [$dimExpr, $variableExpr] ); @@ -63,8 +64,8 @@ public function create(array $exprs): array if ($expr instanceof Isset_) { foreach ($expr->vars as $issetVariable) { if ($issetVariable instanceof ArrayDimFetch) { - $assertMethodCalls[] = $this->nodeFactory->createMethodCall( - 'this', + $assertMethodCalls[] = $this->createAssertMethodCall( + $isStaticClosure, 'assertArrayHasKey', [$issetVariable->dim, $issetVariable->var] ); @@ -86,8 +87,8 @@ public function create(array $exprs): array $classNameExpr = $expr->class; } - $assertMethodCalls[] = $this->nodeFactory->createMethodCall( - 'this', + $assertMethodCalls[] = $this->createAssertMethodCall( + $isStaticClosure, 'assertInstanceOf', [$classNameExpr, $expr->expr] ); @@ -102,8 +103,8 @@ public function create(array $exprs): array ->value; // create assertCount() - $assertMethodCalls[] = $this->nodeFactory->createMethodCall( - 'this', + $assertMethodCalls[] = $this->createAssertMethodCall( + $isStaticClosure, 'assertCount', [$expr->right, $countedExpr] ); @@ -116,8 +117,8 @@ public function create(array $exprs): array } // create assertSame() - $assertMethodCalls[] = $this->nodeFactory->createMethodCall( - 'this', + $assertMethodCalls[] = $this->createAssertMethodCall( + $isStaticClosure, $expr instanceof Identical ? 'assertSame' : 'assertEquals', [$expr->right, $expr->left] ); @@ -141,4 +142,16 @@ public function create(array $exprs): array return $stmts; } + + /** + * @param Expr[] $args + */ + private function createAssertMethodCall(bool $isStaticClosure, string $method, array $args): MethodCall|StaticCall + { + if ($isStaticClosure) { + return new StaticCall(new Name('self'), $method, $this->nodeFactory->createArgs($args)); + } + + return $this->nodeFactory->createMethodCall('this', $method, $args); + } } diff --git a/rules/CodeQuality/Rector/MethodCall/WithCallbackIdenticalToStandaloneAssertsRector.php b/rules/CodeQuality/Rector/MethodCall/WithCallbackIdenticalToStandaloneAssertsRector.php index a7766975..dffafec4 100644 --- a/rules/CodeQuality/Rector/MethodCall/WithCallbackIdenticalToStandaloneAssertsRector.php +++ b/rules/CodeQuality/Rector/MethodCall/WithCallbackIdenticalToStandaloneAssertsRector.php @@ -125,7 +125,10 @@ public function refactor(Node $node): MethodCall|null continue; } - $assertExprStmts = $this->fromBinaryAndAssertExpressionsFactory->create($joinedExprs); + $innerFunctionLike = $argAndFunctionLike->getFunctionLike(); + $isStaticClosure = $innerFunctionLike instanceof Closure && $innerFunctionLike->static; + + $assertExprStmts = $this->fromBinaryAndAssertExpressionsFactory->create($joinedExprs, $isStaticClosure); if ($assertExprStmts === []) { continue; } @@ -134,7 +137,6 @@ public function refactor(Node $node): MethodCall|null // last si return true; $assertExprStmts[] = new Return_($this->nodeFactory->createTrue()); - $innerFunctionLike = $argAndFunctionLike->getFunctionLike(); if ($innerFunctionLike instanceof Closure) { $innerFunctionLike->stmts = array_merge($nonReturnCallbackStmts, $assertExprStmts); From 7735765ed1e3bfc24008d5db1b350a2819ea4887 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 12 May 2026 06:56:34 +0700 Subject: [PATCH 3/4] fix phpstan --- .../NodeFactory/FromBinaryAndAssertExpressionsFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/CodeQuality/NodeFactory/FromBinaryAndAssertExpressionsFactory.php b/rules/CodeQuality/NodeFactory/FromBinaryAndAssertExpressionsFactory.php index 44fa633b..49354c12 100644 --- a/rules/CodeQuality/NodeFactory/FromBinaryAndAssertExpressionsFactory.php +++ b/rules/CodeQuality/NodeFactory/FromBinaryAndAssertExpressionsFactory.php @@ -63,7 +63,7 @@ public function create(array $exprs, bool $isStaticClosure = false): array if ($expr instanceof Isset_) { foreach ($expr->vars as $issetVariable) { - if ($issetVariable instanceof ArrayDimFetch) { + if ($issetVariable instanceof ArrayDimFetch && $issetVariable->dim instanceof Expr) { $assertMethodCalls[] = $this->createAssertMethodCall( $isStaticClosure, 'assertArrayHasKey', From 227c038a6d8f2309bd9c19448cf6e1ed4d5184c1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 11 May 2026 23:57:39 +0000 Subject: [PATCH 4/4] [rector] Rector fixes --- .../ReplaceTestAnnotationWithPrefixedFunctionRector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php b/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php index 02b5d843..5b9e7ae0 100644 --- a/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php +++ b/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php @@ -88,7 +88,7 @@ public function refactor(Node $node): ?Node $hasAnnotation = false; foreach(NewLineSplitter::split($docComment->getText()) as $row) { - if (in_array(trim($row), ['*@test', '* @test'])) { + if (in_array(trim($row), ['*@test', '* @test'], true)) { $hasAnnotation = true; break; }