Mike Doe
Mike Doe

Reputation: 17566

What is null coalescing assignment ??= operator in PHP 7.4

I've just seen a video about upcoming PHP 7.4 features and saw new ??= operator. I already know the ?? operator. How's this different?

Upvotes: 82

Views: 43024

Answers (7)

vahanpwns
vahanpwns

Reputation: 963

Null Coalescing Assignment ??= means return the value, but if it's null then assign it a value first.

Without ??=

if ($a === null) {
    $a = 1;
}
return $a;

With ??=

return $a ??= 1;

In both of the examples above:

  • if $a was null, change it to 1
  • null is never returned

The main use of this operator is to provide a default value in case of null.


Example use case for this Null Coalescing Assignment operator in 2024:

<?php

namespace App\Pages;

use App\Entities\User;

class WelcomePage extends AbstractPage
{
    protected User $user;
    
    protected function setUser(User $user): static
    {
        // assign the value (always) and then return it
        return $this->user = $user;
    }

    protected function getUser(): User
    {
        // Null Coalescing Assignment:
        // assign the value (only if it's null) and then return it
        // else just return it
        return $this->user ??= new User("Anonymous");
    }

    public function display(): string
    {
        return $this->getUser()->loggedIn() ?
            "<h1>Welcome {$this->getUser()->name}! :D</h1>" :
            '<h1>Welcome! :)</h1><a href="/login">log in</a>';
    }
}

In this example, App\Pages\WelcomePage is an extension of the App\Pages\AbstractPage class which contains all of the common functions of pages in our app.

This particular page needs to get the current User as an object and display the name, but only if logged in. This page is not responsible for managing the login state of the user - that's handled elsewhere. The getUser() function is going to try to read the $user property of our class, but if the property is not initialised, because we haven't called setUser() yet, it will first initialise it with new User("Anonymous") and then return the value of the property. Subsequent calls to getUser() will not need to initialise the property.

We can use this WelcomePage class later in some other file like this for anonymous users:

$page = new WelcomePage();
echo $page->display();

Or like this for logged-in users:

$page = new WelcomePage();
echo $page->setUser($loggedInUser)->display();

To extend this further, we could move $user and its getter and setter into the AbstractPage so that all of our pages can extend it and have $user initialised automatically, but only when we want to use it and not more than once.

If we didn't have this operator, we would have to rewrite this function:

    protected function getUser(): User
    {
        if (!isset($this->user)) {
            $this->user = new User("Anonymous");
        }
        return $this->user;
    }

Or use a simple getter:

    protected function getUser(): User
    {
        return $this->user; // can cause an error if not initialised
    }

Upvotes: -2

Pavel Lint
Pavel Lint

Reputation: 3527

From the docs:

Coalesce equal or ??=operator is an assignment operator. If the left parameter is null, assigns the value of the right parameter to the left one. If the value is not null, nothing is done.

Example:

// The following lines are doing the same
$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';
// Instead of repeating variables with long names, the equal coalesce operator is used
$this->request->data['comments']['user_id'] ??= 'value';

So it's basically just a shorthand to assign a value if it hasn't been assigned before.

Upvotes: 71

dkellner
dkellner

Reputation: 9926

It can roughly be translated as "$a defaults to $b", like this:

$page ??= 1;            //  If page is not specified, start at the beginning
$menu ??= "main";       //  Default menu is the main menu
$name ??= "John Doe";   //  Name not given --> use John Doe

A long-awaited tool in the world of PHP.
Before PHP 7.4, we did this with a function:

function defaultOf(&$var, $value) {
    if(is_null($var)) $var=$value;
}

// Now the 3 statements above would look like:

defaultOf( $page, 1 );
defaultOf( $menu, "main" );
defaultOf( $name, "John Doe" );

(I still use it because it's more readable.)

Upvotes: 0

Buttle Butkus
Buttle Butkus

Reputation: 9456

You can use this to initialize variables during a loop's first iteration. But beware!

$reverse_values = array();
$array = ['a','b','c']; // with [NULL, 'b', 'c'], $first_value === 'b'
foreach($array as $key => $value) {
  $first_value ??= $value; // won't be overwritten on next iteration (unless 1st value is NULL!)
  $counter ??= 0; // initialize counter
  $counter++;
  array_unshift($reverse_values,$value);
}
// $first_value === 'a', or 'b' if first value is NULL
// $counter === 3
// $reverse_values = array('c','b','a'), or array('c','b',NULL) if first value is null

If the first value is NULL, then $first_value will be initialized to NULL and then overwritten by the next non-NULL value. If the array has a lot of NULL values, $first_value will end up either as NULL or the first non-NULL after the last NULL. So this seems like a terrible idea.

I would still prefer doing something like this mainly because it's more clear, but also because it works with NULL as an array value:

$reverse_values = array();
$array = ['a','b','c']; // with [NULL, 'b', 'c'], $first_value === NULL
$counter = 0;
foreach($array as $key => $value) {
  $counter++;
  if($counter === 1) $first_value = $value; // does work with NULL first value
  array_unshift($reverse_values,$value);
}

Upvotes: 0

5d18
5d18

Reputation: 101

Null coalescing assignment operator chaining:

$a = null;
$b = null;
$c = 'c';

$a ??= $b ??= $c;

print $b; // c
print $a; // c

Example at 3v4l.org

Upvotes: 10

Nicholas Betsworth
Nicholas Betsworth

Reputation: 1793

The null coalescing assignment operator is a shorthand way of assigning the result of the null coalescing operator.

An example from the official release notes:

$array['key'] ??= computeDefault();
// is roughly equivalent to
if (!isset($array['key'])) {
    $array['key'] = computeDefault();
}

Upvotes: 9

Dmitry Leiko
Dmitry Leiko

Reputation: 4382

Example Docs:

$array['key'] ??= computeDefault();
// is roughly equivalent to
if (!isset($array['key'])) {
    $array['key'] = computeDefault();
}

Upvotes: 3

Related Questions