From eccb4d1c53b8b06982ee77290ee12b032e246b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Tue, 21 Apr 2026 17:47:30 +0400 Subject: [PATCH 01/14] Fix the behavior of `ClassPropertyAssignToConstructorPromotionRector` when types do not match --- ...h_interface_and_its_implementation.php.inc | 49 +++++++++++++++++++ ...ertyAssignToConstructorPromotionRector.php | 41 +++++++++++++++- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc new file mode 100644 index 00000000000..0188a02255a --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc @@ -0,0 +1,49 @@ +x = $x; + $this->y = $y; + $this->z = $z; + } +} + +?> +----- + diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index 7d897abebc3..d38cea939ab 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -10,6 +10,7 @@ use PhpParser\Node\Expr\Variable; use PhpParser\Node\FunctionLike; use PhpParser\Node\Identifier; +use PhpParser\Node\Name; use PhpParser\Node\NullableType; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; @@ -20,6 +21,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\Reflection\ClassReflection; use PHPStan\Type\MixedType; +use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Contract\Rector\ConfigurableRectorInterface; @@ -30,6 +32,7 @@ use Rector\Php80\DocBlock\PropertyPromotionDocBlockMerger; use Rector\Php80\Guard\MakePropertyPromotionGuard; use Rector\Php80\NodeAnalyzer\PromotedPropertyCandidateResolver; +use Rector\PhpParser\Node\Value\ValueResolver; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\Rector\AbstractRector; use Rector\Reflection\ReflectionResolver; @@ -90,7 +93,8 @@ public function __construct( private readonly ReflectionResolver $reflectionResolver, private readonly PropertyPromotionRenamer $propertyPromotionRenamer, private readonly PhpDocInfoFactory $phpDocInfoFactory, - private readonly StaticTypeMapper $staticTypeMapper + private readonly StaticTypeMapper $staticTypeMapper, + private readonly ValueResolver $valueResolver, ) { } @@ -291,6 +295,23 @@ public function provideMinPhpVersion(): int private function processUnionType(Property $property, Param $param): void { if ($property->type instanceof Node) { + // Keep the more specific compatible type when property and param differ. + if ($param->type instanceof Node) { + $propertyType = $this->getType($property->type); + $paramType = $this->getType($param->type); + if ($this->typeComparator->isSubtype($paramType, $propertyType)) { + // Make the parameter type nullable if its default value is null + if ( + $this->hasNullDefaultOnNonNullableParam($param, $paramType) + && ($param->type instanceof Identifier || $param->type instanceof Name) + ) { + $param->type = new NullableType($param->type); + } + + return; + } + } + $param->type = $property->type; return; } @@ -354,6 +375,24 @@ private function shouldSkipParam(Param $param): bool return false; } + private function hasNullDefaultOnNonNullableParam(Param $param, Type $paramType): bool + { + if ($param->default === null) { + return false; + } + + if (! $this->valueResolver->isNull($param->default)) { + return false; + } + + if ($paramType instanceof MixedType) { + return false; + } + + return ! $paramType->isNull() + ->yes(); + } + private function isCallableTypeIdentifier(?Node $node): bool { return $node instanceof Identifier && $this->isName($node, 'callable'); From 579b07e5eb6636fcecaa700d3cc0e6cfdb3ea615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Tue, 21 Apr 2026 22:13:55 +0400 Subject: [PATCH 02/14] CR fix --- ...h_interface_and_its_implementation.php.inc | 22 +++++++------------ .../Source/SomeInterface.php | 5 +++++ .../Source/SomeInterfaceImplementation.php | 5 +++++ 3 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/SomeInterface.php create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/SomeInterfaceImplementation.php diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc index 0188a02255a..dfbf9e8f551 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc @@ -2,11 +2,8 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; -interface SomeInterface {} - -class FirstImplementation implements SomeInterface {} - -class SecondImplementation implements SomeInterface {} +use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterface; +use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation; class WithInterfaceAndItsImplementation { @@ -17,9 +14,9 @@ class WithInterfaceAndItsImplementation private ?SomeInterface $z; public function __construct( - FirstImplementation $x, - SecondImplementation $y = new SecondImplementation(), - SecondImplementation $z = null, + SomeInterfaceImplementation $x, + SomeInterfaceImplementation $y = new SomeInterfaceImplementation(), + SomeInterfaceImplementation $z = null, ) { $this->x = $x; $this->y = $y; @@ -33,15 +30,12 @@ class WithInterfaceAndItsImplementation namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; -interface SomeInterface {} - -class FirstImplementation implements SomeInterface {} - -class SecondImplementation implements SomeInterface {} +use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterface; +use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation; class WithInterfaceAndItsImplementation { - public function __construct(private FirstImplementation $x, private SecondImplementation $y = new SecondImplementation(), private ?SecondImplementation $z = null) + public function __construct(private SomeInterfaceImplementation $x, private SomeInterfaceImplementation $y = new SomeInterfaceImplementation(), private ?SomeInterfaceImplementation $z = null) { } } diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/SomeInterface.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/SomeInterface.php new file mode 100644 index 00000000000..036a459cfef --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/SomeInterface.php @@ -0,0 +1,5 @@ + Date: Wed, 22 Apr 2026 12:24:06 +0400 Subject: [PATCH 03/14] CR fix --- ...h_interface_and_its_implementation.php.inc | 24 ++++---- ...ertyAssignToConstructorPromotionRector.php | 57 +++++++------------ 2 files changed, 33 insertions(+), 48 deletions(-) diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc index dfbf9e8f551..1c7fc84bd4f 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc @@ -7,20 +7,24 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementation { - private SomeInterface $x; + private SomeInterface $basic; - private SomeInterface $y; + private SomeInterface $withDefaultValue; - private ?SomeInterface $z; + private ?SomeInterface $nullable; + + private null|SomeInterface $unionWithNull; public function __construct( - SomeInterfaceImplementation $x, - SomeInterfaceImplementation $y = new SomeInterfaceImplementation(), - SomeInterfaceImplementation $z = null, + SomeInterfaceImplementation $basic, + SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), + SomeInterfaceImplementation $nullable = null, + SomeInterfaceImplementation $unionWithNull = null, ) { - $this->x = $x; - $this->y = $y; - $this->z = $z; + $this->basic = $basic; + $this->withDefaultValue = $withDefaultValue; + $this->nullable = $nullable; + $this->unionWithNull = $unionWithNull; } } @@ -35,7 +39,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementation { - public function __construct(private SomeInterfaceImplementation $x, private SomeInterfaceImplementation $y = new SomeInterfaceImplementation(), private ?SomeInterfaceImplementation $z = null) + public function __construct(private SomeInterfaceImplementation $basic, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullable = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNull = null) { } } diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index d38cea939ab..23e23d8481f 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -32,7 +32,6 @@ use Rector\Php80\DocBlock\PropertyPromotionDocBlockMerger; use Rector\Php80\Guard\MakePropertyPromotionGuard; use Rector\Php80\NodeAnalyzer\PromotedPropertyCandidateResolver; -use Rector\PhpParser\Node\Value\ValueResolver; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\Rector\AbstractRector; use Rector\Reflection\ReflectionResolver; @@ -94,7 +93,6 @@ public function __construct( private readonly PropertyPromotionRenamer $propertyPromotionRenamer, private readonly PhpDocInfoFactory $phpDocInfoFactory, private readonly StaticTypeMapper $staticTypeMapper, - private readonly ValueResolver $valueResolver, ) { } @@ -294,24 +292,7 @@ public function provideMinPhpVersion(): int private function processUnionType(Property $property, Param $param): void { - if ($property->type instanceof Node) { - // Keep the more specific compatible type when property and param differ. - if ($param->type instanceof Node) { - $propertyType = $this->getType($property->type); - $paramType = $this->getType($param->type); - if ($this->typeComparator->isSubtype($paramType, $propertyType)) { - // Make the parameter type nullable if its default value is null - if ( - $this->hasNullDefaultOnNonNullableParam($param, $paramType) - && ($param->type instanceof Identifier || $param->type instanceof Name) - ) { - $param->type = new NullableType($param->type); - } - - return; - } - } - + if ($this->shouldUsePropertyTypeForPromotedParam($property, $param)) { $param->type = $property->type; return; } @@ -375,24 +356,6 @@ private function shouldSkipParam(Param $param): bool return false; } - private function hasNullDefaultOnNonNullableParam(Param $param, Type $paramType): bool - { - if ($param->default === null) { - return false; - } - - if (! $this->valueResolver->isNull($param->default)) { - return false; - } - - if ($paramType instanceof MixedType) { - return false; - } - - return ! $paramType->isNull() - ->yes(); - } - private function isCallableTypeIdentifier(?Node $node): bool { return $node instanceof Identifier && $this->isName($node, 'callable'); @@ -415,4 +378,22 @@ private function shouldSkipPropertyOrParam(Property $property, Param $param): bo && $property->hooks !== [] && ! $this->nodeComparator->areNodesEqual($property->type, $param->type); } + + private function shouldUsePropertyTypeForPromotedParam(Property $property, Param $param): bool + { + if ($property->type === null) { + return false; + } + + if ($param->type === null) { + return true; + } + + $propertyType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($property->type); + $propertyTypeWithoutNull = TypeCombinator::removeNull($propertyType); + + $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + + return $this->typeComparator->areTypesEqual($propertyTypeWithoutNull, $paramType); + } } From 3320d627930fbab06faf999e49b0767f134609bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 12:26:01 +0400 Subject: [PATCH 04/14] remove unnecessary changes --- .../Class_/ClassPropertyAssignToConstructorPromotionRector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index 23e23d8481f..e91d59bccfd 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -92,7 +92,7 @@ public function __construct( private readonly ReflectionResolver $reflectionResolver, private readonly PropertyPromotionRenamer $propertyPromotionRenamer, private readonly PhpDocInfoFactory $phpDocInfoFactory, - private readonly StaticTypeMapper $staticTypeMapper, + private readonly StaticTypeMapper $staticTypeMapper ) { } From 231c4293cd174f5962810f40d970d0b5135ab2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 16:00:48 +0400 Subject: [PATCH 05/14] CR fix --- .../Class_/ClassPropertyAssignToConstructorPromotionRector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index e91d59bccfd..bd19eaab281 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -393,7 +393,8 @@ private function shouldUsePropertyTypeForPromotedParam(Property $property, Param $propertyTypeWithoutNull = TypeCombinator::removeNull($propertyType); $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + $paramTypeWithoutNull = TypeCombinator::removeNull($paramType); - return $this->typeComparator->areTypesEqual($propertyTypeWithoutNull, $paramType); + return $this->typeComparator->areTypesEqual($propertyTypeWithoutNull, $paramTypeWithoutNull); } } From b6dc0af323b81cd279cf9135cb79dd7791fc2cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 16:30:56 +0400 Subject: [PATCH 06/14] add tests --- .../Fixture/with_interface_and_its_implementation.php.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc index 1c7fc84bd4f..8354cef893c 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc @@ -15,16 +15,20 @@ class WithInterfaceAndItsImplementation private null|SomeInterface $unionWithNull; + private SomeInterface $withNullableParam; + public function __construct( SomeInterfaceImplementation $basic, SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), SomeInterfaceImplementation $nullable = null, SomeInterfaceImplementation $unionWithNull = null, + ?SomeInterfaceImplementation $withNullableParam = null, ) { $this->basic = $basic; $this->withDefaultValue = $withDefaultValue; $this->nullable = $nullable; $this->unionWithNull = $unionWithNull; + $this->withNullableParam = $withNullableParam; } } @@ -39,7 +43,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementation { - public function __construct(private SomeInterfaceImplementation $basic, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullable = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNull = null) + public function __construct(private SomeInterfaceImplementation $basic, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullable = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNull = null, private ?SomeInterfaceImplementation $withNullableParam = null) { } } From 33de8dbe264f68305fefe25553d8d4b2f62bafd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 16:32:40 +0400 Subject: [PATCH 07/14] add tests --- .../Fixture/with_interface_and_its_implementation.php.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc index 8354cef893c..0da6531098f 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc @@ -17,18 +17,22 @@ class WithInterfaceAndItsImplementation private SomeInterface $withNullableParam; + private SomeInterface $withUnionWithNullInParam; + public function __construct( SomeInterfaceImplementation $basic, SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), SomeInterfaceImplementation $nullable = null, SomeInterfaceImplementation $unionWithNull = null, ?SomeInterfaceImplementation $withNullableParam = null, + null|SomeInterfaceImplementation $withUnionWithNullInParam = null, ) { $this->basic = $basic; $this->withDefaultValue = $withDefaultValue; $this->nullable = $nullable; $this->unionWithNull = $unionWithNull; $this->withNullableParam = $withNullableParam; + $this->withUnionWithNullInParam = $withUnionWithNullInParam; } } @@ -43,7 +47,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementation { - public function __construct(private SomeInterfaceImplementation $basic, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullable = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNull = null, private ?SomeInterfaceImplementation $withNullableParam = null) + public function __construct(private SomeInterfaceImplementation $basic, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullable = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNull = null, private ?SomeInterfaceImplementation $withNullableParam = null, private null|SomeInterfaceImplementation $withUnionWithNullInParam = null) { } } From 9646c930f220c1ebc38b5ef5b89036143cc9afd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 17:20:21 +0400 Subject: [PATCH 08/14] add tests --- .../with_interface_and_its_implementation.php.inc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc index 0da6531098f..25c8c78a2b6 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc @@ -9,6 +9,10 @@ class WithInterfaceAndItsImplementation { private SomeInterface $basic; + private SomeInterface $withNullableParamWithoutDefaultValue; + + private SomeInterface $withUnionWithNullInParamWithoutDefaultValue; + private SomeInterface $withDefaultValue; private ?SomeInterface $nullable; @@ -21,6 +25,8 @@ class WithInterfaceAndItsImplementation public function __construct( SomeInterfaceImplementation $basic, + ?SomeInterfaceImplementation $withNullableParamWithoutDefaultValue, + null|SomeInterfaceImplementation $withUnionWithNullInParamWithoutDefaultValue, SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), SomeInterfaceImplementation $nullable = null, SomeInterfaceImplementation $unionWithNull = null, @@ -28,6 +34,8 @@ class WithInterfaceAndItsImplementation null|SomeInterfaceImplementation $withUnionWithNullInParam = null, ) { $this->basic = $basic; + $this->withNullableParamWithoutDefaultValue = $withNullableParamWithoutDefaultValue; + $this->withUnionWithNullInParamWithoutDefaultValue = $withUnionWithNullInParamWithoutDefaultValue; $this->withDefaultValue = $withDefaultValue; $this->nullable = $nullable; $this->unionWithNull = $unionWithNull; @@ -47,7 +55,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementation { - public function __construct(private SomeInterfaceImplementation $basic, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullable = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNull = null, private ?SomeInterfaceImplementation $withNullableParam = null, private null|SomeInterfaceImplementation $withUnionWithNullInParam = null) + public function __construct(private SomeInterfaceImplementation $basic, private ?SomeInterfaceImplementation $withNullableParamWithoutDefaultValue, private null|SomeInterfaceImplementation $withUnionWithNullInParamWithoutDefaultValue, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullable = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNull = null, private ?SomeInterfaceImplementation $withNullableParam = null, private null|SomeInterfaceImplementation $withUnionWithNullInParam = null) { } } From b458b6aca1ced6c260fe89a5dc2301dd9a4ba0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 17:24:39 +0400 Subject: [PATCH 09/14] naming --- ...h_interface_and_its_implementation.php.inc | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc index 25c8c78a2b6..e2472980c90 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc @@ -9,38 +9,38 @@ class WithInterfaceAndItsImplementation { private SomeInterface $basic; - private SomeInterface $withNullableParamWithoutDefaultValue; + private SomeInterface $withNullableParam; - private SomeInterface $withUnionWithNullInParamWithoutDefaultValue; + private SomeInterface $withUnionWithNullInParam; private SomeInterface $withDefaultValue; - private ?SomeInterface $nullable; + private ?SomeInterface $nullableWithDefaultValue; - private null|SomeInterface $unionWithNull; + private null|SomeInterface $unionWithNullWithDefaultValue; - private SomeInterface $withNullableParam; + private SomeInterface $withNullableParamWithDefaultValue; - private SomeInterface $withUnionWithNullInParam; + private SomeInterface $withUnionWithNullInParamWithDefaultValue; public function __construct( SomeInterfaceImplementation $basic, - ?SomeInterfaceImplementation $withNullableParamWithoutDefaultValue, - null|SomeInterfaceImplementation $withUnionWithNullInParamWithoutDefaultValue, + ?SomeInterfaceImplementation $withNullableParam, + null|SomeInterfaceImplementation $withUnionWithNullInParam, SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), - SomeInterfaceImplementation $nullable = null, - SomeInterfaceImplementation $unionWithNull = null, - ?SomeInterfaceImplementation $withNullableParam = null, - null|SomeInterfaceImplementation $withUnionWithNullInParam = null, + SomeInterfaceImplementation $nullableWithDefaultValue = null, + SomeInterfaceImplementation $unionWithNullWithDefaultValue = null, + ?SomeInterfaceImplementation $withNullableParamWithDefaultValue = null, + null|SomeInterfaceImplementation $withUnionWithNullInParamWithDefaultValue = null, ) { $this->basic = $basic; - $this->withNullableParamWithoutDefaultValue = $withNullableParamWithoutDefaultValue; - $this->withUnionWithNullInParamWithoutDefaultValue = $withUnionWithNullInParamWithoutDefaultValue; - $this->withDefaultValue = $withDefaultValue; - $this->nullable = $nullable; - $this->unionWithNull = $unionWithNull; $this->withNullableParam = $withNullableParam; $this->withUnionWithNullInParam = $withUnionWithNullInParam; + $this->withDefaultValue = $withDefaultValue; + $this->nullableWithDefaultValue = $nullableWithDefaultValue; + $this->unionWithNullWithDefaultValue = $unionWithNullWithDefaultValue; + $this->withNullableParamWithDefaultValue = $withNullableParamWithDefaultValue; + $this->withUnionWithNullInParamWithDefaultValue = $withUnionWithNullInParamWithDefaultValue; } } @@ -55,7 +55,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementation { - public function __construct(private SomeInterfaceImplementation $basic, private ?SomeInterfaceImplementation $withNullableParamWithoutDefaultValue, private null|SomeInterfaceImplementation $withUnionWithNullInParamWithoutDefaultValue, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullable = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNull = null, private ?SomeInterfaceImplementation $withNullableParam = null, private null|SomeInterfaceImplementation $withUnionWithNullInParam = null) + public function __construct(private SomeInterfaceImplementation $basic, private ?SomeInterfaceImplementation $withNullableParam, private null|SomeInterfaceImplementation $withUnionWithNullInParam, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullableWithDefaultValue = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNullWithDefaultValue = null, private ?SomeInterfaceImplementation $withNullableParamWithDefaultValue = null, private null|SomeInterfaceImplementation $withUnionWithNullInParamWithDefaultValue = null) { } } From 396e58b951d4fd056b89d1ac22b89fa78d9313f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 17:28:27 +0400 Subject: [PATCH 10/14] naming --- ...h_interface_and_its_implementation.php.inc | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc index e2472980c90..8df25508d26 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc @@ -11,36 +11,36 @@ class WithInterfaceAndItsImplementation private SomeInterface $withNullableParam; - private SomeInterface $withUnionWithNullInParam; + private SomeInterface $withUnionAndNullInParam; private SomeInterface $withDefaultValue; - private ?SomeInterface $nullableWithDefaultValue; + private ?SomeInterface $withNonNullableParamAndDefaultValueNull; - private null|SomeInterface $unionWithNullWithDefaultValue; + private null|SomeInterface $unionWithNullAndDefaultValue; - private SomeInterface $withNullableParamWithDefaultValue; + private SomeInterface $withNullableParamAndDefaultValue; - private SomeInterface $withUnionWithNullInParamWithDefaultValue; + private SomeInterface $withUnionAndNullInParamAndDefaultValue; public function __construct( SomeInterfaceImplementation $basic, ?SomeInterfaceImplementation $withNullableParam, - null|SomeInterfaceImplementation $withUnionWithNullInParam, + null|SomeInterfaceImplementation $withUnionAndNullInParam, SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), - SomeInterfaceImplementation $nullableWithDefaultValue = null, - SomeInterfaceImplementation $unionWithNullWithDefaultValue = null, - ?SomeInterfaceImplementation $withNullableParamWithDefaultValue = null, - null|SomeInterfaceImplementation $withUnionWithNullInParamWithDefaultValue = null, + SomeInterfaceImplementation $withNonNullableParamAndDefaultValueNull = null, + SomeInterfaceImplementation $unionWithNullAndDefaultValue = null, + ?SomeInterfaceImplementation $withNullableParamAndDefaultValue = null, + null|SomeInterfaceImplementation $withUnionAndNullInParamAndDefaultValue = null, ) { $this->basic = $basic; $this->withNullableParam = $withNullableParam; - $this->withUnionWithNullInParam = $withUnionWithNullInParam; + $this->withUnionAndNullInParam = $withUnionAndNullInParam; $this->withDefaultValue = $withDefaultValue; - $this->nullableWithDefaultValue = $nullableWithDefaultValue; - $this->unionWithNullWithDefaultValue = $unionWithNullWithDefaultValue; - $this->withNullableParamWithDefaultValue = $withNullableParamWithDefaultValue; - $this->withUnionWithNullInParamWithDefaultValue = $withUnionWithNullInParamWithDefaultValue; + $this->withNonNullableParamAndDefaultValueNull = $withNonNullableParamAndDefaultValueNull; + $this->unionWithNullAndDefaultValue = $unionWithNullAndDefaultValue; + $this->withNullableParamAndDefaultValue = $withNullableParamAndDefaultValue; + $this->withUnionAndNullInParamAndDefaultValue = $withUnionAndNullInParamAndDefaultValue; } } @@ -55,7 +55,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementation { - public function __construct(private SomeInterfaceImplementation $basic, private ?SomeInterfaceImplementation $withNullableParam, private null|SomeInterfaceImplementation $withUnionWithNullInParam, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $nullableWithDefaultValue = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNullWithDefaultValue = null, private ?SomeInterfaceImplementation $withNullableParamWithDefaultValue = null, private null|SomeInterfaceImplementation $withUnionWithNullInParamWithDefaultValue = null) + public function __construct(private SomeInterfaceImplementation $basic, private ?SomeInterfaceImplementation $withNullableParam, private null|SomeInterfaceImplementation $withUnionAndNullInParam, private SomeInterfaceImplementation $withDefaultValue = new SomeInterfaceImplementation(), private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $withNonNullableParamAndDefaultValueNull = null, private ?\Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $unionWithNullAndDefaultValue = null, private ?SomeInterfaceImplementation $withNullableParamAndDefaultValue = null, private null|SomeInterfaceImplementation $withUnionAndNullInParamAndDefaultValue = null) { } } From 7581ad7e2377ebd0941e91bbec0b5cf847aa686e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 18:09:04 +0400 Subject: [PATCH 11/14] separate tests --- ...h_interface_and_its_implementation.php.inc | 63 ------------------- ...face_and_its_implementation__basic.php.inc | 35 +++++++++++ ...ementation__default_value_in_param.php.inc | 35 +++++++++++ ...n_nullable_param_with_null_default.php.inc | 35 +++++++++++ ...its_implementation__nullable_param.php.inc | 35 +++++++++++ ...__nullable_param_and_default_value.php.inc | 35 +++++++++++ ...entation__union_with_null_in_param.php.inc | 35 +++++++++++ ...th_null_in_param_and_default_value.php.inc | 35 +++++++++++ 8 files changed, 245 insertions(+), 63 deletions(-) delete mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__basic.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__default_value_in_param.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_default_value.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_default_value.php.inc diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc deleted file mode 100644 index 8df25508d26..00000000000 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation.php.inc +++ /dev/null @@ -1,63 +0,0 @@ -basic = $basic; - $this->withNullableParam = $withNullableParam; - $this->withUnionAndNullInParam = $withUnionAndNullInParam; - $this->withDefaultValue = $withDefaultValue; - $this->withNonNullableParamAndDefaultValueNull = $withNonNullableParamAndDefaultValueNull; - $this->unionWithNullAndDefaultValue = $unionWithNullAndDefaultValue; - $this->withNullableParamAndDefaultValue = $withNullableParamAndDefaultValue; - $this->withUnionAndNullInParamAndDefaultValue = $withUnionAndNullInParamAndDefaultValue; - } -} - -?> ------ - diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__basic.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__basic.php.inc new file mode 100644 index 00000000000..fdbbcfe3b65 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__basic.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__default_value_in_param.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__default_value_in_param.php.inc new file mode 100644 index 00000000000..70b2aa11eb1 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__default_value_in_param.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default.php.inc new file mode 100644 index 00000000000..3f9b4d6ec75 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param.php.inc new file mode 100644 index 00000000000..29128df2c5c --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_default_value.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_default_value.php.inc new file mode 100644 index 00000000000..40f2f4c5fbc --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_default_value.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param.php.inc new file mode 100644 index 00000000000..bdfa4a9cc2e --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_default_value.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_default_value.php.inc new file mode 100644 index 00000000000..66090020cb8 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_default_value.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + From 7a26a3fc2050ad17da968d7decc08ab3161eca56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 18:49:41 +0400 Subject: [PATCH 12/14] add fixtures --- ...le_param_and_non_nullable_property.php.inc | 35 +++++++++++++++++++ ...in_param_and_non_nullable_property.php.inc | 35 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_non_nullable_property.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_non_nullable_property.php.inc diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_non_nullable_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_non_nullable_property.php.inc new file mode 100644 index 00000000000..8fc692b36fa --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_non_nullable_property.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_non_nullable_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_non_nullable_property.php.inc new file mode 100644 index 00000000000..9416bd140e3 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_non_nullable_property.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + From 25d9baf2c46b395456d5ad6bc60e89fe4dbbeaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Wed, 22 Apr 2026 18:51:43 +0400 Subject: [PATCH 13/14] add fixtures --- ...n_nullable_param_with_null_default.php.inc | 2 +- ..._default_and_non_nullable_property.php.inc | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default_and_non_nullable_property.php.inc diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default.php.inc index 3f9b4d6ec75..ae084310e99 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default.php.inc @@ -7,7 +7,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementationNonNullableParamWithNullDefault { - private SomeInterface $x; + private ?SomeInterface $x; public function __construct( SomeInterfaceImplementation $x = null, diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default_and_non_nullable_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default_and_non_nullable_property.php.inc new file mode 100644 index 00000000000..a028195641d --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__non_nullable_param_with_null_default_and_non_nullable_property.php.inc @@ -0,0 +1,35 @@ +x = $x; + } +} + +?> +----- + From fcc1179dab68636590b4957edbd1309d34d2f90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A1=D0=BF=D0=B8?= =?UTF-8?q?=D1=80=D0=BA=D0=BE=D0=B2?= Date: Thu, 23 Apr 2026 10:52:21 +0400 Subject: [PATCH 14/14] CR fix --- ...le_param_and_non_nullable_property.php.inc | 2 +- ...in_param_and_non_nullable_property.php.inc | 2 +- ...ertyAssignToConstructorPromotionRector.php | 39 +++++++++++++++++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_non_nullable_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_non_nullable_property.php.inc index 8fc692b36fa..f42207f5e34 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_non_nullable_property.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__nullable_param_and_non_nullable_property.php.inc @@ -27,7 +27,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementationNullableParamAndNonNullableProperty { - public function __construct(private ?SomeInterfaceImplementation $x) + public function __construct(private \Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $x) { } } diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_non_nullable_property.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_non_nullable_property.php.inc index 9416bd140e3..49ee90ea0dc 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_non_nullable_property.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/with_interface_and_its_implementation__union_with_null_in_param_and_non_nullable_property.php.inc @@ -27,7 +27,7 @@ use Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRe class WithInterfaceAndItsImplementationUnionWithNullInParamAndNonNullableProperty { - public function __construct(private null|SomeInterfaceImplementation $x) + public function __construct(private \Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\SomeInterfaceImplementation $x) { } } diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index bd19eaab281..5d4190cb229 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -32,6 +32,7 @@ use Rector\Php80\DocBlock\PropertyPromotionDocBlockMerger; use Rector\Php80\Guard\MakePropertyPromotionGuard; use Rector\Php80\NodeAnalyzer\PromotedPropertyCandidateResolver; +use Rector\PhpParser\Node\Value\ValueResolver; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\Rector\AbstractRector; use Rector\Reflection\ReflectionResolver; @@ -92,7 +93,8 @@ public function __construct( private readonly ReflectionResolver $reflectionResolver, private readonly PropertyPromotionRenamer $propertyPromotionRenamer, private readonly PhpDocInfoFactory $phpDocInfoFactory, - private readonly StaticTypeMapper $staticTypeMapper + private readonly StaticTypeMapper $staticTypeMapper, + private readonly ValueResolver $valueResolver, ) { } @@ -297,16 +299,22 @@ private function processUnionType(Property $property, Param $param): void return; } - if (! $param->default instanceof Expr) { + if (! $param->type instanceof Node) { return; } - if (! $param->type instanceof Node) { + $paramType = $this->getType($param->type); + + if ($this->shouldRemoveNullFromForPromotedParamType($property, $param)) { + $paramType = TypeCombinator::removeNull($paramType); + $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramType, TypeKind::PARAM); + } + + if (! $param->default instanceof Expr) { return; } $defaultType = $this->getType($param->default); - $paramType = $this->getType($param->type); if ($this->typeComparator->isSubtype($defaultType, $paramType)) { return; @@ -379,6 +387,29 @@ private function shouldSkipPropertyOrParam(Property $property, Param $param): bo && ! $this->nodeComparator->areNodesEqual($property->type, $param->type); } + private function shouldRemoveNullFromForPromotedParamType(Property $property, Param $param): bool + { + if ($property->type === null || $param->type === null) { + return false; + } + + if ($param->default !== null && $this->valueResolver->isNull($param->default)) { + return false; + } + + $propertyType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($property->type); + $propertyTypeWithoutNull = TypeCombinator::removeNull($propertyType); + + if (! $this->typeComparator->areTypesEqual($propertyTypeWithoutNull, $propertyType)) { + return false; + } + + $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + $paramTypeWithoutNull = TypeCombinator::removeNull($paramType); + + return ! $this->typeComparator->areTypesEqual($paramTypeWithoutNull, $paramType); + } + private function shouldUsePropertyTypeForPromotedParam(Property $property, Param $param): bool { if ($property->type === null) {