Joseph Scott

Take a quick glance at this PHP code:

$stuff = (string) $things ?? 'nothing';
var_dump( $stuff );

If this is all the code you have, then $things does not exist, so you expect $stuff to be set to the string nothing. But that is not what happens. The output is string(0) "". On top of that, it will generate a warning.

$stuff = (string) $things ?? 'nothing';
var_dump( $stuff );
 
# OUTPUT:
# Warning: Undefined variable $things in ...
# string(0) ""

I did this myself a few times before I realized what was going on. The type casting to a (string) takes precedence over the null coalescing operator ( ?? ). It is as if you wrote this code:

$stuff = ( (string) $things ) ?? 'nothing';

It turns out that ?? has very low precedence in PHP, just above ?: ( ternary ). The type casts are at the other end of the table, just below clone, new, and **.

You also end up with ?? 'nothing' being dead code. Casting to (string) always gives you back a string; it can never be null. The right side of the ?? is unreachable no matter what $things holds. Static analysis tools will call this out for you: PHPStan reports ( at level 4+ ) Expression on left side of ?? is not nullable.

Normally ?? takes care of undefined variable warnings, which is part of what makes it so useful. But that only works when the variable is sitting directly to the left of it. Here the cast gets to $things first, and the warning happens right there, before ?? ever comes into play.

var_dump( $nope ?? 'nothing' );          # string(7) "nothing", no warning
var_dump( (string) $nope ?? 'nothing' ); # Warning: Undefined variable $nope, then string(0) ""

Going back to the original example, to keep both the cast and the null coalescing, wrap the null coalescing in parentheses.

$stuff = (string) ( $things ?? 'nothing' );
var_dump( $stuff );
 
# OUTPUT:
# string(7) "nothing"

You can see both versions in action at https://3v4l.org/mInAG.

I've seen multiple devs with lots of PHP experience get hit by this recently, so I figured it was worth writing a note about it. The writing process also helps me remember it :)

I think this is one of those things that isn't immediately obvious just glancing at the code, unless you've run into it already.