From 63b86872e8702a1b6a2370030de0548f4f374237 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 4 Nov 2026 17:45:57 +0700 Subject: [PATCH 1/2] [Php70] Keep inner ternary parentheses on TernaryToNullCoalescingRector --- ...nner_ternary_parentheses_in_concat.php.inc | 27 +++++++++++++++++++ .../Printer/BetterStandardPrinter.php | 17 ++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_inner_ternary_parentheses_in_concat.php.inc diff --git a/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_inner_ternary_parentheses_in_concat.php.inc b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_inner_ternary_parentheses_in_concat.php.inc new file mode 100644 index 00000000000..11db1a17da1 --- /dev/null +++ b/rules-tests/Php70/Rector/Ternary/TernaryToNullCoalescingRector/Fixture/keep_inner_ternary_parentheses_in_concat.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/src/PhpParser/Printer/BetterStandardPrinter.php b/src/PhpParser/Printer/BetterStandardPrinter.php index 73480d5460b..025074ced94 100644 --- a/src/PhpParser/Printer/BetterStandardPrinter.php +++ b/src/PhpParser/Printer/BetterStandardPrinter.php @@ -542,6 +542,11 @@ private function wrapBinaryOpWithBrackets(Node $node): void $this->reprintLowerPrecedenceOperand($node->right); } + // a format-preserved ternary operand is printed without its original parentheses + // @see https://github.com/rectorphp/rector-src/issues/8910 + $this->keepTernaryParentheses($node->left); + $this->keepTernaryParentheses($node->right); + if ($node->left instanceof BinaryOp && $node->left->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { $node->left->setAttribute(AttributeKey::ORIGINAL_NODE, null); @@ -553,6 +558,18 @@ private function wrapBinaryOpWithBrackets(Node $node): void } } + private function keepTernaryParentheses(Expr $expr): void + { + if (! $expr instanceof Ternary || ! $expr->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { + return; + } + + if ($this->origTokens instanceof TokenStream + && $this->origTokens->haveParens($expr->getStartTokenPos(), $expr->getEndTokenPos())) { + $expr->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + } + } + /** * @see https://github.com/rectorphp/rector/issues/9800 */ From 06dcff27e5bc1cad9a5a6caeabfbdb9920d958cd Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 4 Nov 2026 20:00:06 +0700 Subject: [PATCH 2/2] [Php70] Keep inner ternary parentheses on TernaryToNullCoalescingRector --- .../Printer/BetterStandardPrinter.php | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/PhpParser/Printer/BetterStandardPrinter.php b/src/PhpParser/Printer/BetterStandardPrinter.php index 025074ced94..84d1368f8e8 100644 --- a/src/PhpParser/Printer/BetterStandardPrinter.php +++ b/src/PhpParser/Printer/BetterStandardPrinter.php @@ -542,31 +542,45 @@ private function wrapBinaryOpWithBrackets(Node $node): void $this->reprintLowerPrecedenceOperand($node->right); } - // a format-preserved ternary operand is printed without its original parentheses - // @see https://github.com/rectorphp/rector-src/issues/8910 - $this->keepTernaryParentheses($node->left); - $this->keepTernaryParentheses($node->right); - if ($node->left instanceof BinaryOp && $node->left->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { + $this->keepTernaryOperandParentheses($node->left); $node->left->setAttribute(AttributeKey::ORIGINAL_NODE, null); } if ($node->right instanceof BinaryOp && $node->right->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { + $this->keepTernaryOperandParentheses($node->right); $node->right->setAttribute(AttributeKey::ORIGINAL_NODE, null); } } - private function keepTernaryParentheses(Expr $expr): void + /** + * The binary op is about to be reprinted, while its unchanged ternary operand is still format-preserved, + * which prints it without its original parentheses + * + * @see https://github.com/rectorphp/rector-src/issues/8910 + */ + private function keepTernaryOperandParentheses(BinaryOp $binaryOp): void { - if (! $expr instanceof Ternary || ! $expr->getAttribute(AttributeKey::ORIGINAL_NODE) instanceof Node) { + $originalBinaryOp = $binaryOp->getAttribute(AttributeKey::ORIGINAL_NODE); + if (! $originalBinaryOp instanceof BinaryOp || ! $this->origTokens instanceof TokenStream) { return; } - if ($this->origTokens instanceof TokenStream - && $this->origTokens->haveParens($expr->getStartTokenPos(), $expr->getEndTokenPos())) { - $expr->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + foreach ([$binaryOp->left, $binaryOp->right] as $key => $operand) { + if (! $operand instanceof Ternary) { + continue; + } + + $originalOperand = $key === 0 ? $originalBinaryOp->left : $originalBinaryOp->right; + if ($operand->getAttribute(AttributeKey::ORIGINAL_NODE) !== $originalOperand) { + continue; + } + + if ($this->origTokens->haveParens($operand->getStartTokenPos(), $operand->getEndTokenPos())) { + $operand->setAttribute(AttributeKey::WRAPPED_IN_PARENTHESES, true); + } } }