From e71c2cafdbb65a3f8a5820368b997939ed8ea8fc Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:23:29 +0700 Subject: [PATCH 01/10] [Php81] Deprecate NewInInitializerRector as depends on context --- config/set/php81.php | 2 - .../pass_class_const_fetch_arg.php.inc | 35 ---- .../Fixture/pass_const_fetch_arg.php.inc | 37 ---- .../Fixture/pass_non_dynamic_arg.php.inc | 35 ---- .../Fixture/pass_non_dynamic_arg2.php.inc | 29 --- .../Fixture/property_with_attributes.php.inc | 43 ----- .../Fixture/skip_abstract.php.inc | 14 -- ...n_coalesce_with_new_in_inner_class.php.inc | 23 --- .../Fixture/skip_dynamic_new.php.inc | 17 -- .../Fixture/skip_if_no_default_null.php.inc | 17 -- .../skip_override_abstract_method.php.inc | 17 -- .../skip_override_interface_method.php.inc | 17 -- ...lass_const_fetch_arg_dynamic_class.php.inc | 18 -- .../Fixture/skip_pass_dynamic_arg2.php.inc | 17 -- .../Fixture/skip_pass_dynamic_arg3.php.inc | 15 -- ...static_call_null_coalesce_operator.php.inc | 13 -- .../skip_static_call_right_coalesce.php.inc | 16 -- .../skip_variable_used_next_stmt.php.inc | 15 -- .../Fixture/skip_with_next_required.php.inc | 13 -- .../Fixture/some_class.php.inc | 29 --- .../NewInInitializerRectorTest.php | 28 --- .../InstantiableViaNamedConstructor.php | 17 -- .../Source/Logger/DefaultLogger.php | 8 - .../Source/Logger/NullLogger.php | 8 - .../Source/OverrideAbstractMethod.php | 14 -- .../Source/OverrideInterfaceMethod.php | 14 -- .../Source/SomeValueObject.php | 10 - .../config/configured_rule.php | 13 -- .../ClassMethod/NewInInitializerRector.php | 172 +----------------- 29 files changed, 7 insertions(+), 699 deletions(-) delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_class_const_fetch_arg.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_const_fetch_arg.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_non_dynamic_arg.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_non_dynamic_arg2.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/property_with_attributes.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_abstract.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_assign_coalesce_with_new_in_inner_class.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_dynamic_new.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_if_no_default_null.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_override_abstract_method.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_override_interface_method.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_class_const_fetch_arg_dynamic_class.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_dynamic_arg2.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_dynamic_arg3.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_static_call_null_coalesce_operator.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_static_call_right_coalesce.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_variable_used_next_stmt.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_with_next_required.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/some_class.php.inc delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/NewInInitializerRectorTest.php delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/InstantiableViaNamedConstructor.php delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/Logger/DefaultLogger.php delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/Logger/NullLogger.php delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/OverrideAbstractMethod.php delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/OverrideInterfaceMethod.php delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/SomeValueObject.php delete mode 100644 rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/config/configured_rule.php diff --git a/config/set/php81.php b/config/set/php81.php index d73299ea7da..e1152596775 100644 --- a/config/set/php81.php +++ b/config/set/php81.php @@ -41,7 +41,5 @@ ClosureFromCallableToFirstClassCallableRector::class, FunctionFirstClassCallableRector::class, RemoveReflectionSetAccessibleCallsRector::class, - - NewInInitializerRector::class, ]); }; diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_class_const_fetch_arg.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_class_const_fetch_arg.php.inc deleted file mode 100644 index e4f7e19866c..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_class_const_fetch_arg.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -dateTime = $dateTime ?? new DateTime(SomeValueObject::NOW); - } -} - -?> ------ - diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_const_fetch_arg.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_const_fetch_arg.php.inc deleted file mode 100644 index cf6a79f9754..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_const_fetch_arg.php.inc +++ /dev/null @@ -1,37 +0,0 @@ -dateTime = $dateTime ?? new DateTime(NOW); - } -} - -?> ------ - diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_non_dynamic_arg.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_non_dynamic_arg.php.inc deleted file mode 100644 index 770f8e03170..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_non_dynamic_arg.php.inc +++ /dev/null @@ -1,35 +0,0 @@ -dateTime = $dateTime ?? new DateTime('now', new DateTimeZone('Asia/Jakarta')); - } -} - -?> ------ - diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_non_dynamic_arg2.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_non_dynamic_arg2.php.inc deleted file mode 100644 index c78cd27296a..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/pass_non_dynamic_arg2.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -logger = $logger ?? new NullLogger(['a' => 'b']); - } -} - -?> ------ - 'b'])) - { - } -} - -?> diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/property_with_attributes.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/property_with_attributes.php.inc deleted file mode 100644 index 5ca8fc1980f..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/property_with_attributes.php.inc +++ /dev/null @@ -1,43 +0,0 @@ -id = $id ?? new Ulid(); - } -} - -?> ------ - diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_abstract.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_abstract.php.inc deleted file mode 100644 index b4224a81e76..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_abstract.php.inc +++ /dev/null @@ -1,14 +0,0 @@ -logger = $logger ?? new NullLogger; - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_assign_coalesce_with_new_in_inner_class.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_assign_coalesce_with_new_in_inner_class.php.inc deleted file mode 100644 index 2d7f298018b..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_assign_coalesce_with_new_in_inner_class.php.inc +++ /dev/null @@ -1,23 +0,0 @@ -logger = $logger ?? new NullLogger; - } - }; - - $this->logger = $logger; - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_dynamic_new.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_dynamic_new.php.inc deleted file mode 100644 index 6acb207f933..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_dynamic_new.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -stdClass = $stdClass ?? new $fallbackClass; - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_if_no_default_null.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_if_no_default_null.php.inc deleted file mode 100644 index 69cbaf4ca7a..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_if_no_default_null.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -logger = $logger ?? new NullLogger(); - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_override_abstract_method.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_override_abstract_method.php.inc deleted file mode 100644 index 081de648a91..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_override_abstract_method.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -dateTime = $dateTime ?? new DateTime('now'); - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_override_interface_method.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_override_interface_method.php.inc deleted file mode 100644 index d5a927051a8..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_override_interface_method.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -dateTime = $dateTime ?? new DateTime('now'); - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_class_const_fetch_arg_dynamic_class.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_class_const_fetch_arg_dynamic_class.php.inc deleted file mode 100644 index 27e4476b283..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_class_const_fetch_arg_dynamic_class.php.inc +++ /dev/null @@ -1,18 +0,0 @@ -dateTime = $dateTime ?? new DateTime($class::NOW); - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_dynamic_arg2.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_dynamic_arg2.php.inc deleted file mode 100644 index 372c1ed83cd..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_dynamic_arg2.php.inc +++ /dev/null @@ -1,17 +0,0 @@ -dateTime = $dateTime ?? new DateTime('now', new DateTimeZone($timezone)); - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_dynamic_arg3.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_dynamic_arg3.php.inc deleted file mode 100644 index 641a3f405ca..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_pass_dynamic_arg3.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -logger = $logger ?? new NullLogger(['a' => $x]); - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_static_call_null_coalesce_operator.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_static_call_null_coalesce_operator.php.inc deleted file mode 100644 index 055535b8e3d..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_static_call_null_coalesce_operator.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -foo ??= InstantiableViaNamedConstructor::make(100); - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_static_call_right_coalesce.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_static_call_right_coalesce.php.inc deleted file mode 100644 index b39541a2ed6..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_static_call_right_coalesce.php.inc +++ /dev/null @@ -1,16 +0,0 @@ -foo = $foo ?? InstantiableViaNamedConstructor::make(100); - } - -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_variable_used_next_stmt.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_variable_used_next_stmt.php.inc deleted file mode 100644 index 25965ef9b95..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_variable_used_next_stmt.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -time = $time ?? new \DateTime(); - $this->timeWasSet = null !== $time; - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_with_next_required.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_with_next_required.php.inc deleted file mode 100644 index fa30893f88d..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/skip_with_next_required.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -time = $time ?? new \DateTime(); - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/some_class.php.inc b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/some_class.php.inc deleted file mode 100644 index 69689f45e8d..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,29 +0,0 @@ -logger = $logger ?? new NullLogger; - } -} - -?> ------ - diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/NewInInitializerRectorTest.php b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/NewInInitializerRectorTest.php deleted file mode 100644 index a9c48e734cf..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/NewInInitializerRectorTest.php +++ /dev/null @@ -1,28 +0,0 @@ -doTestFile($filePath); - } - - public static function provideData(): Iterator - { - return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/InstantiableViaNamedConstructor.php b/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/InstantiableViaNamedConstructor.php deleted file mode 100644 index 53a05e8f4d2..00000000000 --- a/rules-tests/Php81/Rector/ClassMethod/NewInInitializerRector/Source/InstantiableViaNamedConstructor.php +++ /dev/null @@ -1,17 +0,0 @@ -rule(NewInInitializerRector::class); - - $rectorConfig->phpVersion(PhpVersion::PHP_81); -}; diff --git a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php index e3db19e0437..82deab6f36f 100644 --- a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php +++ b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php @@ -5,38 +5,17 @@ namespace Rector\Php81\Rector\ClassMethod; use PhpParser\Node; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\BinaryOp\Coalesce; -use PhpParser\Node\NullableType; -use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Property; -use PHPStan\Reflection\ClassReflection; -use Rector\FamilyTree\NodeAnalyzer\ClassChildAnalyzer; -use Rector\NodeManipulator\StmtsManipulator; -use Rector\Php81\NodeAnalyzer\CoalescePropertyAssignMatcher; +use Rector\Configuration\Deprecation\Contract\DeprecatedInterface; +use Rector\Exception\ShouldNotHappenException; use Rector\Rector\AbstractRector; -use Rector\Reflection\ReflectionResolver; -use Rector\ValueObject\MethodName; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -/** - * @see \Rector\Tests\Php81\Rector\ClassMethod\NewInInitializerRector\NewInInitializerRectorTest - */ -final class NewInInitializerRector extends AbstractRector implements MinPhpVersionInterface +final class NewInInitializerRector extends AbstractRector implements MinPhpVersionInterface, DeprecatedInterface { - public function __construct( - private readonly ReflectionResolver $reflectionResolver, - private readonly ClassChildAnalyzer $classChildAnalyzer, - private readonly CoalescePropertyAssignMatcher $coalescePropertyAssignMatcher, - private readonly StmtsManipulator $stmtsManipulator - ) { - } - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Replace property declaration of new state with direct new', [ @@ -81,151 +60,14 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($this->shouldSkipClass($node)) { - return null; - } - - $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); - if (! $constructClassMethod instanceof ClassMethod) { - return null; - } - - $params = $this->resolveParams($constructClassMethod); - if ($params === []) { - return null; - } - - $hasChanged = false; - foreach ((array) $constructClassMethod->stmts as $key => $stmt) { - foreach ($params as $param) { - $paramName = $this->getName($param); - - if ($param->type instanceof NullableType && $param->default === null) { - continue; - } - - $coalesce = $this->coalescePropertyAssignMatcher->matchCoalesceAssignsToLocalPropertyNamed( - $stmt, - $paramName - ); - if (! $coalesce instanceof Coalesce) { - continue; - } - - if ($this->stmtsManipulator->isVariableUsedInNextStmt($constructClassMethod, $key + 1, $paramName)) { - continue; - } - - $param->default = $coalesce->right; - - unset($constructClassMethod->stmts[$key]); - $this->processPropertyPromotion($node, $param, $paramName); - - $hasChanged = true; - } - } - - if ($hasChanged) { - return $node; - } - - return null; + throw new ShouldNotHappenException(sprintf( + '"%s" is deprecated as opinionated and group size depends on context. Cannot be automated. Use manually where needed instead', + self::class + )); } public function provideMinPhpVersion(): int { return PhpVersionFeature::NEW_INITIALIZERS; } - - /** - * @return Param[] - */ - private function resolveParams(ClassMethod $classMethod): array - { - $params = $this->matchConstructorParams($classMethod); - if ($params === []) { - return []; - } - - if ($this->isOverrideAbstractMethod($classMethod)) { - return []; - } - - return $params; - } - - private function isOverrideAbstractMethod(ClassMethod $classMethod): bool - { - $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); - $methodName = $this->getName($classMethod); - - return $classReflection instanceof ClassReflection && $this->classChildAnalyzer->hasAbstractParentClassMethod( - $classReflection, - $methodName - ); - } - - private function processPropertyPromotion(Class_ $class, Param $param, string $paramName): void - { - foreach ($class->stmts as $key => $stmt) { - if (! $stmt instanceof Property) { - continue; - } - - $property = $stmt; - if (! $this->isName($stmt, $paramName)) { - continue; - } - - $param->flags = $property->flags; - $param->attrGroups = array_merge($property->attrGroups, $param->attrGroups); - - unset($class->stmts[$key]); - } - } - - /** - * @return Param[] - */ - private function matchConstructorParams(ClassMethod $classMethod): array - { - // skip empty constructor assigns, as we need those here - if ($classMethod->stmts === null || $classMethod->stmts === []) { - return []; - } - - $params = array_filter( - $classMethod->params, - static fn (Param $param): bool => $param->type instanceof NullableType - ); - - if ($params === []) { - return $params; - } - - $totalParams = count($classMethod->params); - - foreach (array_keys($params) as $key) { - for ($iteration = $key + 1; $iteration < $totalParams; ++$iteration) { - if (isset($classMethod->params[$iteration]) && ! $classMethod->params[$iteration]->default instanceof Expr) { - return []; - } - } - } - - return $params; - } - - private function shouldSkipClass(Class_ $class): bool - { - if ($class->stmts === []) { - return true; - } - - if ($class->isAbstract()) { - return true; - } - - return $class->isAnonymous(); - } } From 511f6653d8affd10af3903862af1099544a782fc Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:24:23 +0700 Subject: [PATCH 02/10] [Php81] Deprecate NewInInitializerRector as depends on context --- rules/Php81/Rector/ClassMethod/NewInInitializerRector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php index 82deab6f36f..9f6b8ce280e 100644 --- a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php +++ b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php @@ -61,7 +61,7 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { throw new ShouldNotHappenException(sprintf( - '"%s" is deprecated as opinionated and group size depends on context. Cannot be automated. Use manually where needed instead', + '"%s" is deprecated as depends on context. Cannot be automated. Use manually where needed instead', self::class )); } From a66a06d2ae5b8366060ea1e372493be69375ec32 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 Feb 2026 19:26:24 +0000 Subject: [PATCH 03/10] [ci-review] Rector Rectify --- config/set/php81.php | 1 - 1 file changed, 1 deletion(-) diff --git a/config/set/php81.php b/config/set/php81.php index e1152596775..5a08ab3fdfd 100644 --- a/config/set/php81.php +++ b/config/set/php81.php @@ -10,7 +10,6 @@ use Rector\Php81\Rector\Array_\ArrayToFirstClassCallableRector; use Rector\Php81\Rector\Class_\MyCLabsClassToEnumRector; use Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector; -use Rector\Php81\Rector\ClassMethod\NewInInitializerRector; use Rector\Php81\Rector\FuncCall\NullToStrictIntPregSlitFuncCallLimitArgRector; use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector; use Rector\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector; From 13b896709c361e2e1c27db053ac8df1a4218d8c8 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:26:29 +0700 Subject: [PATCH 04/10] fix api --- src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php index 50d48af63a8..ceab978311e 100644 --- a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php +++ b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php @@ -10,6 +10,9 @@ use PHPStan\Type\MixedType; use PHPStan\Type\Type; +/** + * @api used by rector-downgrade-php and rector-symfony + */ final readonly class ClassChildAnalyzer { /** From 6e8814bd21d8a81fed762e17086054e9d8458721 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:27:36 +0700 Subject: [PATCH 05/10] remove unused service --- .../CoalescePropertyAssignMatcher.php | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 rules/Php81/NodeAnalyzer/CoalescePropertyAssignMatcher.php diff --git a/rules/Php81/NodeAnalyzer/CoalescePropertyAssignMatcher.php b/rules/Php81/NodeAnalyzer/CoalescePropertyAssignMatcher.php deleted file mode 100644 index 231dbea28dd..00000000000 --- a/rules/Php81/NodeAnalyzer/CoalescePropertyAssignMatcher.php +++ /dev/null @@ -1,73 +0,0 @@ -value = $param ?? 'default'; - */ - public function matchCoalesceAssignsToLocalPropertyNamed(Stmt $stmt, string $propertyName): ?Coalesce - { - if (! $stmt instanceof Expression) { - return null; - } - - if (! $stmt->expr instanceof Assign) { - return null; - } - - $assign = $stmt->expr; - - if (! $assign->expr instanceof Coalesce) { - return null; - } - - $coalesce = $assign->expr; - if (! $coalesce->right instanceof New_) { - return null; - } - - if ($this->complexNewAnalyzer->isDynamic($coalesce->right)) { - return null; - } - - if (! $this->isLocalPropertyFetchNamed($assign->var, $propertyName)) { - return null; - } - - return $assign->expr; - } - - private function isLocalPropertyFetchNamed(Expr $expr, string $propertyName): bool - { - if (! $expr instanceof PropertyFetch) { - return false; - } - - if (! $this->nodeNameResolver->isName($expr->var, 'this')) { - return false; - } - - return $this->nodeNameResolver->isName($expr->name, $propertyName); - } -} From 5210695693d613be9c86b0271c323e53b4647ed7 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:28:46 +0700 Subject: [PATCH 06/10] remove unused service --- .../Php81/NodeAnalyzer/ComplexNewAnalyzer.php | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 rules/Php81/NodeAnalyzer/ComplexNewAnalyzer.php diff --git a/rules/Php81/NodeAnalyzer/ComplexNewAnalyzer.php b/rules/Php81/NodeAnalyzer/ComplexNewAnalyzer.php deleted file mode 100644 index 5ebf99840d2..00000000000 --- a/rules/Php81/NodeAnalyzer/ComplexNewAnalyzer.php +++ /dev/null @@ -1,87 +0,0 @@ -class instanceof FullyQualified) { - return true; - } - - if ($new->isFirstClassCallable()) { - return false; - } - - $args = $new->getArgs(); - - foreach ($args as $arg) { - $value = $arg->value; - - if ($this->isAllowedNew($value)) { - continue; - } - - // new inside array is allowed for New in initializer - if ($value instanceof Array_ && $this->isAllowedArray($value)) { - continue; - } - - if (! $this->exprAnalyzer->isDynamicExpr($value)) { - continue; - } - - return true; - } - - return false; - } - - private function isAllowedNew(Expr $expr): bool - { - if ($expr instanceof New_) { - return ! $this->isDynamic($expr); - } - - return false; - } - - private function isAllowedArray(Array_ $array): bool - { - if (! $this->exprAnalyzer->isDynamicArray($array)) { - return true; - } - - $arrayItems = $array->items; - foreach ($arrayItems as $arrayItem) { - if (! $arrayItem instanceof ArrayItem) { - continue; - } - - if (! $arrayItem->value instanceof New_) { - return false; - } - - if ($this->isDynamic($arrayItem->value)) { - return false; - } - } - - return true; - } -} From bae28e75c1c7287b5edeea5d9db416040b76c92c Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:31:54 +0700 Subject: [PATCH 07/10] fix phpstan --- phpstan.neon | 1 + rules/Php81/Rector/ClassMethod/NewInInitializerRector.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index 326a1c7045c..688c9bf80c9 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -423,6 +423,7 @@ parameters: # deprecated rule - '#Rule Rector\\Php81\\Rector\\Array_\\FirstClassCallableRector must implements Rector\\VersionBonding\\Contract\\MinPhpVersionInterface#' - '#Register "Rector\\Php81\\Rector\\Array_\\FirstClassCallableRector" service to "php81\.php" config set#' + - '#Register "Rector\\Php81\\Rector\\ClassMethod\\NewInInitializerRector" service to "php81\.php" config set#' - '#Class "Rector\\CodingStyle\\Rector\\String_\\SymplifyQuoteEscapeRector" is missing @see annotation with test case class reference#' # BC layer for FileWithoutNamespace node diff --git a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php index 9f6b8ce280e..00e59bfa0f1 100644 --- a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php +++ b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php @@ -14,6 +14,9 @@ use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +/** + * @see \Rector\Tests\Php81\Rector\ClassMethod\NewInInitializerRector\NewInInitializerRectorTest + */ final class NewInInitializerRector extends AbstractRector implements MinPhpVersionInterface, DeprecatedInterface { public function getRuleDefinition(): RuleDefinition From f7b785190259432dfc78484fbfde0802f1ee8378 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:33:51 +0700 Subject: [PATCH 08/10] fix phpstan --- phpstan.neon | 1 + rules/Php81/Rector/ClassMethod/NewInInitializerRector.php | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 688c9bf80c9..3bd4cf3b344 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -425,6 +425,7 @@ parameters: - '#Register "Rector\\Php81\\Rector\\Array_\\FirstClassCallableRector" service to "php81\.php" config set#' - '#Register "Rector\\Php81\\Rector\\ClassMethod\\NewInInitializerRector" service to "php81\.php" config set#' - '#Class "Rector\\CodingStyle\\Rector\\String_\\SymplifyQuoteEscapeRector" is missing @see annotation with test case class reference#' + - '#Class "Rector\\Php81\\Rector\\ClassMethod\\NewInInitializerRector" is missing @see annotation with test case class reference#' # BC layer for FileWithoutNamespace node - diff --git a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php index 00e59bfa0f1..9f6b8ce280e 100644 --- a/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php +++ b/rules/Php81/Rector/ClassMethod/NewInInitializerRector.php @@ -14,9 +14,6 @@ use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -/** - * @see \Rector\Tests\Php81\Rector\ClassMethod\NewInInitializerRector\NewInInitializerRectorTest - */ final class NewInInitializerRector extends AbstractRector implements MinPhpVersionInterface, DeprecatedInterface { public function getRuleDefinition(): RuleDefinition From d6456a1e4fa697ac89a35e55f47d56cdcd867a84 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:46:33 +0700 Subject: [PATCH 09/10] final touch: define @ api on method for consistency with other method --- src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php index ceab978311e..f6b69155a23 100644 --- a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php +++ b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php @@ -10,13 +10,12 @@ use PHPStan\Type\MixedType; use PHPStan\Type\Type; -/** - * @api used by rector-downgrade-php and rector-symfony - */ final readonly class ClassChildAnalyzer { /** * Look both parent class and interface, yes, all PHP interface methods are abstract + * + * @api rector-symfony */ public function hasAbstractParentClassMethod(ClassReflection $classReflection, string $methodName): bool { From 40861430ed013a295873961ac17af9827458d6e6 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 24 Feb 2026 02:49:23 +0700 Subject: [PATCH 10/10] final touch: use @ api on class for global definition --- src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php index f6b69155a23..8255677d41e 100644 --- a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php +++ b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php @@ -10,6 +10,9 @@ use PHPStan\Type\MixedType; use PHPStan\Type\Type; +/** + * @api + */ final readonly class ClassChildAnalyzer { /**