Feature Request
NullCoalescingOperatorRector currently operates on Assign nodes, so it only recognizes the self-referencing coalesce form. The equivalent if guard which is what most pre-7.4 code actually looks like, is not detected.
Note that Rector already encodes this equivalence in the opposite direction: the PHP 7.4 downgrade set rewrites ??= into exactly the if ( ! isset( ... ) ) block requested here (see #4124). Only the upgrade direction is missing.
Diff
1. isset() guard
-if (!isset($array['user_id'])) {
- $array['user_id'] = 'value';
-}
+$array['user_id'] ??= 'value';
2. null === guard
-if (null === $value) {
- $value = 'default';
-}
+$value ??= 'default';
3. is_null() guard
-if (is_null($this->instance)) {
- $this->instance = new self();
-}
+$this->instance ??= new self();
Conditions
The transformation is only safe when all of these hold, and skipping any of them changes behavior:
- The
if has no else or elseif branch.
- The body contains exactly one statement. Anything else in the block would move from running once to running on every pass:
// must NOT be converted
if (null === self::$instance) {
self::$instance = new self();
do_action('init', self::$instance); // would fire on every call
}
- The condition is not compound.
if (null === $args['a'] || !$args['b']) is not equivalent.
- The assignment target is the same expression the condition tests.
- The assigned value does not reference the target itself (
$x = $x . 'a' would be evaluated differently).
Feature Request
NullCoalescingOperatorRectorcurrently operates onAssignnodes, so it only recognizes the self-referencing coalesce form. The equivalentifguard which is what most pre-7.4 code actually looks like, is not detected.Note that Rector already encodes this equivalence in the opposite direction: the PHP 7.4 downgrade set rewrites
??=into exactly theif ( ! isset( ... ) )block requested here (see #4124). Only the upgrade direction is missing.Diff
1.
isset()guard2.
null ===guard3.
is_null()guardConditions
The transformation is only safe when all of these hold, and skipping any of them changes behavior:
ifhas noelseorelseifbranch.if (null === $args['a'] || !$args['b'])is not equivalent.$x = $x . 'a'would be evaluated differently).