PHP 8.0 : Nullsafe operator
15, August 2020

In today's programming world, usage of null is heavily discouraged. Reckless use of null can lead to unexpected behavior in software. But PHP being a web oriented language, null is still found in code every now and then. Null type is used to indicate lack of existence of value.

But, null in our code forces us to write more code to validate if our variable has any value to work with it. Like this:

<?php

$user = null;
// $user is loaded from different place, 
// so we don't know if it is null or not

if($user !== null){
  $permission = $user->getPermission();
  if($permission !== null) {
    $isAdmin = $permission->hasAdminRight();
  } else {
    $isAdmin = null;
  }
} else {
  $isAdmin = null;
}

In this code snippet, we try to check if $user is admin or not. But, lot of boilerplate code had to be written just to check nulls.

PHP 8.0 tries to improve this situation with nullsafe operator. Nullsafe operator lets us access class property or call class method without worrying about null checks because it will short-circuit if it finds any null value. Let's see how to do that:

$user = null;
// same as before

$isAdmin = $user?->permission?->hasAdminRight();

WOW! How clean is that? All those null checks are gone and our $isAdmin works just like before. So what is actually happening here?

In the line 4, We have used ?->, so PHP checks if $user is null. When PHP finds out it is null (line 1), PHP immediately returns null to $isAdmin. PHP does not execute anymore code on line 4. So, hasAdminRight() is never called because $user in null. This system is called as "short-circuit".

So, what if $user has correct value?

class User {
  public function __construct(public ?Permission $permission) {}
}

class Permission {
  public function hasAdminRight() {
    return true;
  }
}

$user = new User(new Permission());
// $user is admin now

$isAdmin = $user?->permission?->hasAdminRight();

var_dump($isAdmin);

$user2 = new User(null);
// $user has no permission

$isAdmin2 = $user2?->permission?->hasAdminRight();

var_dump($isAdmin2);

In here, We have two class, User and Permission. User class takes Permission (or null) in __construct. Check Class constructor property promotion if that syntax looks flunky.

Then we create a $user Which is a instance of User and we pass Permission to it.

On line 13, PHP first check if $user is null. As it is not, PHP access permission property. Then PHP checks again if permission is null. As it is not, PHP calls hasAdminRight() method on it and assign that value onto $isAdmin.


Next, We create another $user2 but this time, we pass null as permission. So on line 20, When PHP finds out that permission is null, it does not call hasAdminRight() on it, just assign null to $isAdmin.


We can use multiple nullsafe oprator chain in same line. PHP abort all child chain if parent chain resolves to null.

$foo = $class?->method($dependency?->othermethod()?->otherproperty);
//                     -------------------------------------- child chain
//     --------------- parent chain

Here, if value of the child chain is null, then PHP will pass null to method(). But, if $class is null, then PHP will abort all the chain and return/assign null to $foo.

$class->array[$keyObject?->keyName]?->method();

This code has lots of issue, But I just wanted to let you know that nullsafe operator can be mixed with normal operator (->). Here, $class should must have value because we didn't use nullsafe oprator on it.


Note:

We can't use this operator in write context (usually left side of =).

$foo?->bar = $value;
// Can't use nullsafe operator in write context

[$class?->key] = $bazz;
// Can't use nullsafe operator in write context


Nullsafe operator can't be used with reference. Reason of it is, this operator can return null but null value can't be referenced.

$x = &$foo?->bar;
// Fatal error: Cannot take reference of a nullsafe chain


Another thing to remember is, nullsafe operator does not suppress "Undefined variable" error.

$a = $b ?? 'c';
// even if $b does not exists, php reports no warning

$a = $b?->c;
// Warning: Undefined variable $b 


Personally, I suggests everyone to stay away from null as much as we can. Null always leads to "Oh, I didn't know it can do that" situation. But when null is unavoidable, nullsafe operator does come in handy.

RFC: https://wiki.php.net/rfc/nullsafe_operator