diff --git a/rules-tests/DowngradePhp81/Rector/FunctionLike/DowngradeNewInInitializerRector/Fixture/new_in_array.php.inc b/rules-tests/DowngradePhp81/Rector/FunctionLike/DowngradeNewInInitializerRector/Fixture/new_in_array.php.inc new file mode 100644 index 00000000000..4ef88699555 --- /dev/null +++ b/rules-tests/DowngradePhp81/Rector/FunctionLike/DowngradeNewInInitializerRector/Fixture/new_in_array.php.inc @@ -0,0 +1,34 @@ + new stdClass() + ]) + { + } +} + +?> +----- +property = $property ?? [ + 'a' => new stdClass() + ]; + } +} + +?> diff --git a/rules/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector.php b/rules/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector.php index 36c1c36d67b..eaea7e66300 100644 --- a/rules/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector.php +++ b/rules/DowngradePhp80/Rector/Class_/DowngradePropertyPromotionRector.php @@ -6,6 +6,7 @@ use PhpParser\Comment; use PhpParser\Node; +use PhpParser\Node\Expr\New_; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; @@ -208,7 +209,11 @@ private function createPropertiesFromParams(array $params): array $property->type = $param->type; $this->decoratePropertyWithParamDocInfo($param, $property); - if ($param->default !== null) { + $hasNew = $param->default === null + ? false + : (bool) $this->betterNodeFinder->findFirstInstanceOf($param->default, New_::class); + + if ($param->default !== null && ! $hasNew) { $property->props[0]->default = $param->default; } diff --git a/rules/DowngradePhp81/Rector/FunctionLike/DowngradeNewInInitializerRector.php b/rules/DowngradePhp81/Rector/FunctionLike/DowngradeNewInInitializerRector.php index 5d947c7bed0..600fbd3f06d 100644 --- a/rules/DowngradePhp81/Rector/FunctionLike/DowngradeNewInInitializerRector.php +++ b/rules/DowngradePhp81/Rector/FunctionLike/DowngradeNewInInitializerRector.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\ComplexType; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\AssignOp\Coalesce as AssignCoalesce; @@ -18,6 +19,7 @@ use PhpParser\Node\IntersectionType; use PhpParser\Node\Name; use PhpParser\Node\NullableType; +use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; @@ -95,11 +97,7 @@ public function refactor(Node $node): ?FunctionLike private function shouldSkip(FunctionLike $functionLike): bool { foreach ($functionLike->getParams() as $param) { - if (! $param->default instanceof New_) { - continue; - } - - if ($param->type instanceof IntersectionType) { + if ($this->isParamSkipped($param)) { continue; } @@ -125,27 +123,44 @@ private function convertArrowFunctionToClosure(FunctionLike $functionLike): Func ); } + private function isParamSkipped(Param $param): bool + { + if ($param->default === null) { + return true; + } + + $hasNew = (bool) $this->betterNodeFinder->findFirstInstanceOf($param->default, New_::class); + if (! $hasNew) { + return true; + } + + return $param->type instanceof IntersectionType; + } + private function replaceNewInParams(FunctionLike $functionLike): FunctionLike { $isConstructor = $functionLike instanceof ClassMethod && $this->isName($functionLike, MethodName::CONSTRUCT); $stmts = []; foreach ($functionLike->getParams() as $param) { - if (! $param->default instanceof New_) { + if ($this->isParamSkipped($param)) { continue; } + /** @var Expr $default */ + $default = $param->default; + // check for property promotion if ($isConstructor && $param->flags > 0) { $propertyFetch = new PropertyFetch(new Variable('this'), $param->var->name); - $coalesce = new Coalesce($param->var, $param->default); + $coalesce = new Coalesce($param->var, $default); $assign = new Assign($propertyFetch, $coalesce); if ($param->type !== null) { $param->type = $this->ensureNullableType($param->type); } } else { - $assign = new AssignCoalesce($param->var, $param->default); + $assign = new AssignCoalesce($param->var, $default); } $stmts[] = new Expression($assign); diff --git a/tests/Issues/DowngradeNewInArrayInitializerPromotion/DowngradeNewInArrayInitializerPromotionTest.php b/tests/Issues/DowngradeNewInArrayInitializerPromotion/DowngradeNewInArrayInitializerPromotionTest.php new file mode 100644 index 00000000000..fc19a3d74dc --- /dev/null +++ b/tests/Issues/DowngradeNewInArrayInitializerPromotion/DowngradeNewInArrayInitializerPromotionTest.php @@ -0,0 +1,33 @@ +doTestFileInfo($fileInfo); + } + + /** + * @return Iterator + */ + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/DowngradeNewInArrayInitializerPromotion/Fixture/new_in_array.php.inc b/tests/Issues/DowngradeNewInArrayInitializerPromotion/Fixture/new_in_array.php.inc new file mode 100644 index 00000000000..2b4ff457aa8 --- /dev/null +++ b/tests/Issues/DowngradeNewInArrayInitializerPromotion/Fixture/new_in_array.php.inc @@ -0,0 +1,45 @@ + $property + */ + public function __construct(public array $property = [ + 'a' => new stdClass() + ]) + { + } +} + +?> +----- + + */ + public $property; + /** + * @param array $property + */ + public function __construct(array $property = null) + { + $property ??= [ + 'a' => new stdClass() + ]; + $this->property = $property; + } +} + +?> diff --git a/tests/Issues/DowngradeNewInArrayInitializerPromotion/config/configured_rule.php b/tests/Issues/DowngradeNewInArrayInitializerPromotion/config/configured_rule.php new file mode 100644 index 00000000000..7a9fb9bb275 --- /dev/null +++ b/tests/Issues/DowngradeNewInArrayInitializerPromotion/config/configured_rule.php @@ -0,0 +1,15 @@ +services(); + $services->set(DowngradeNewInInitializerRector::class); + $services->set(DowngradePropertyPromotionRector::class); + $services->set(DowngradeTypedPropertyRector::class); +};