Skip to content

[Php74] Add rule to convert if (!isset($x)) { $x = ...; } to ??= #9843

Description

@Soean

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions