From 5fb5718adbbac66b59e4fdb0b692024f9d48a81f Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 5 Apr 2026 23:22:28 +0700 Subject: [PATCH 1/5] [CodeQuality] Add CoalesceToTernaryRector --- .../CoalesceToTernaryRectorTest.php | 28 ++++++ .../Fixture/non_nullable_left.php.inc | 27 ++++++ .../Fixture/skip_array_dim_fetch.php.inc | 11 +++ .../Fixture/skip_docblock_based.php.inc | 14 +++ .../Fixture/skip_mixed_type.php.inc | 11 +++ .../Fixture/skip_not_defined.php.inc | 11 +++ .../Fixture/skip_null_type.php.inc | 11 +++ .../Fixture/skip_nullable_left.php.inc | 11 +++ .../Fixture/skip_union_nullable_left.php.inc | 11 +++ .../config/configured_rule.php | 9 ++ .../Coalesce/CoalesceToTernaryRector.php | 97 +++++++++++++++++++ src/Config/Level/CodeQualityLevel.php | 2 + 12 files changed, 243 insertions(+) create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/CoalesceToTernaryRectorTest.php create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_array_dim_fetch.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_docblock_based.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_mixed_type.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_not_defined.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_null_type.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_nullable_left.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_union_nullable_left.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/config/configured_rule.php create mode 100644 rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/CoalesceToTernaryRectorTest.php b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/CoalesceToTernaryRectorTest.php new file mode 100644 index 00000000000..794029d9f2d --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/CoalesceToTernaryRectorTest.php @@ -0,0 +1,28 @@ +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/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc new file mode 100644 index 00000000000..44f46e2630c --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_array_dim_fetch.php.inc b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_array_dim_fetch.php.inc new file mode 100644 index 00000000000..3b345d5c502 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_array_dim_fetch.php.inc @@ -0,0 +1,11 @@ +withRules([CoalesceToTernaryRector::class]); diff --git a/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php b/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php new file mode 100644 index 00000000000..877205601c2 --- /dev/null +++ b/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php @@ -0,0 +1,97 @@ +> + */ + public function getNodeTypes(): array + { + return [Coalesce::class]; + } + + /** + * @param Coalesce $node + */ + public function refactor(Node $node): ?Node + { + /** + * indexed data maybe false positive + */ + if ($node->left instanceof ArrayDimFetch) { + return null; + } + + /** + * Scope needs to use parent Coalesce to properly get type from left side of coalesce + */ + $scope = ScopeFetcher::fetch($node); + $nativeType = $scope->getNativeType($node->left); + + if ($nativeType instanceof MixedType) { + return null; + } + + if ($nativeType instanceof ErrorType) { + return null; + } + + if ($nativeType instanceof NullType) { + return null; + } + + if ($nativeType instanceof UnionType) { + foreach ($nativeType->getTypes() as $unionedType) { + if ($unionedType instanceof NullType) { + return null; + } + } + } + + return new Ternary($node->left, null, $node->right); + } +} diff --git a/src/Config/Level/CodeQualityLevel.php b/src/Config/Level/CodeQualityLevel.php index aae830a863f..098eacdc4f9 100644 --- a/src/Config/Level/CodeQualityLevel.php +++ b/src/Config/Level/CodeQualityLevel.php @@ -23,6 +23,7 @@ use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector; use Rector\CodeQuality\Rector\ClassMethod\LocallyCalledStaticMethodToNonStaticRector; use Rector\CodeQuality\Rector\ClassMethod\OptionalParametersAfterRequiredRector; +use Rector\CodeQuality\Rector\Coalesce\CoalesceToTernaryRector; use Rector\CodeQuality\Rector\Concat\JoinStringConcatRector; use Rector\CodeQuality\Rector\Empty_\SimplifyEmptyCheckOnEmptyArrayRector; use Rector\CodeQuality\Rector\Equal\UseIdenticalOverEqualWithSameTypeRector; @@ -185,6 +186,7 @@ final class CodeQualityLevel SortAttributeNamedArgsRector::class, RemoveReadonlyPropertyVisibilityOnReadonlyClassRector::class, SafeDeclareStrictTypesRector::class, + CoalesceToTernaryRector::class, ]; /** From 7c40c7e3a14e5543440edcb08dca198c718c821a Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 5 Apr 2026 23:25:21 +0700 Subject: [PATCH 2/5] Fix phpstan --- rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php b/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php index 877205601c2..e582396dcbb 100644 --- a/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php +++ b/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php @@ -72,11 +72,11 @@ public function refactor(Node $node): ?Node $scope = ScopeFetcher::fetch($node); $nativeType = $scope->getNativeType($node->left); - if ($nativeType instanceof MixedType) { + if ($nativeType instanceof ErrorType) { return null; } - if ($nativeType instanceof ErrorType) { + if ($nativeType instanceof MixedType) { return null; } From 75b09f896a7d4c3611e8524301cc6ce8c61d7889 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 5 Apr 2026 23:26:06 +0700 Subject: [PATCH 3/5] eol --- .../CoalesceToTernaryRector/Fixture/skip_docblock_based.php.inc | 2 +- .../CoalesceToTernaryRector/Fixture/skip_mixed_type.php.inc | 2 +- .../CoalesceToTernaryRector/Fixture/skip_not_defined.php.inc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_docblock_based.php.inc b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_docblock_based.php.inc index 7327c4235ef..aead14915aa 100644 --- a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_docblock_based.php.inc +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_docblock_based.php.inc @@ -11,4 +11,4 @@ class SkipDocblockBased { return $name ?? 'tom'; } -} \ No newline at end of file +} diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_mixed_type.php.inc b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_mixed_type.php.inc index 01b8929313b..a93ff1269a9 100644 --- a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_mixed_type.php.inc +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_mixed_type.php.inc @@ -8,4 +8,4 @@ class SkipMixedType { return $name ?? 'tom'; } -} \ No newline at end of file +} diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_not_defined.php.inc b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_not_defined.php.inc index cb89b0d6617..d814dc6b8f4 100644 --- a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_not_defined.php.inc +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/skip_not_defined.php.inc @@ -8,4 +8,4 @@ class SkipNotDefined { return $name ?? 'tom'; } -} \ No newline at end of file +} From 4f7d9b72026824ddb6820817d96a2ea8f0ef038d Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 5 Apr 2026 23:39:47 +0700 Subject: [PATCH 4/5] final touch: fix fixture namespace --- .../CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc | 4 ++-- .../Fixture/skip_array_dim_fetch.php.inc | 2 +- .../Fixture/skip_docblock_based.php.inc | 2 +- .../CoalesceToTernaryRector/Fixture/skip_mixed_type.php.inc | 2 +- .../CoalesceToTernaryRector/Fixture/skip_not_defined.php.inc | 2 +- .../CoalesceToTernaryRector/Fixture/skip_null_type.php.inc | 2 +- .../Fixture/skip_nullable_left.php.inc | 2 +- .../Fixture/skip_union_nullable_left.php.inc | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc index 44f46e2630c..5c094d481ec 100644 --- a/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc +++ b/rules-tests/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector/Fixture/non_nullable_left.php.inc @@ -1,6 +1,6 @@ Date: Mon, 6 Apr 2026 22:18:37 +0700 Subject: [PATCH 5/5] unregister from code quality set --- src/Config/Level/CodeQualityLevel.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Config/Level/CodeQualityLevel.php b/src/Config/Level/CodeQualityLevel.php index 098eacdc4f9..aae830a863f 100644 --- a/src/Config/Level/CodeQualityLevel.php +++ b/src/Config/Level/CodeQualityLevel.php @@ -23,7 +23,6 @@ use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector; use Rector\CodeQuality\Rector\ClassMethod\LocallyCalledStaticMethodToNonStaticRector; use Rector\CodeQuality\Rector\ClassMethod\OptionalParametersAfterRequiredRector; -use Rector\CodeQuality\Rector\Coalesce\CoalesceToTernaryRector; use Rector\CodeQuality\Rector\Concat\JoinStringConcatRector; use Rector\CodeQuality\Rector\Empty_\SimplifyEmptyCheckOnEmptyArrayRector; use Rector\CodeQuality\Rector\Equal\UseIdenticalOverEqualWithSameTypeRector; @@ -186,7 +185,6 @@ final class CodeQualityLevel SortAttributeNamedArgsRector::class, RemoveReadonlyPropertyVisibilityOnReadonlyClassRector::class, SafeDeclareStrictTypesRector::class, - CoalesceToTernaryRector::class, ]; /**