Ifyou've used the null coalescing operator in the past, you probably also noticed its shortcomings: null coalescing doesn't work on method calls. Instead you need intermediate checks, or rely on optional helpers provided by some frameworks:
Here we have an Order object which has an optional relation to an Invoice object. Now imagine we'd want to get the invoice's number (if the invoice isn't null). You could do this both with the null coalescing operator and the nullsafe operator:
So what's the difference? While you could use both operators to achieve the same result in this example, they also have specific edge cases only one of them can handle. For example, you can use the null coalescing operator in combination with array keys, while the nullsafe operator can't handle them:
Sometimes you could use either the null coalescing or nullsafe operator, and other times you'd need to use a specific one. The difference is that the nullsafe operator uses a form of "short circuiting": writing ?-> will cause PHP to look at whats on the lefthand side of this operator, if it's null then the righthand side will simply be discarded. The null coalescing operator is actually an isset call in disguise on its lefthand operand, which doesn't support short circuiting.
Noticed a tpyo? You can submit a PR to fix it. If you want to stay up to date about what's happening on this blog, you can subscribe to my mailing list: send an email to
bre...@stitcher.io, and I'll add you to the list.
The nullsafe operator is definitely a missing piece of the puzzle finally added in PHP. Given its dynamic nature, it feels good to have a smooth way of dealing with null. The difference and overlap between the nullsafe operator and null coalescing operator feels a bit confusing at first, but I'm sure we'll get used to it.
This RFC proposes full short circuiting. When the evaluation of one element in the chain fails the execution of the entire chain is aborted and the entire chain evaluates to null. The following elements are considered part of the chain.
It was previously suggested to allow the nullsafe operator in the left hand side of assignments and skip the assignment if the left hand side of the nullsafe operator was null. However, due to technical difficulties this is not a part of this RFC. It might be addressed in a later RFC. It is also not completely clear whether the right hand side of the assignment should always be evaluated or not.
Taking the reference of a nullsafe chain is not allowed. This is because references require l-values (memory locations, like variables or properties) but the nullsafe operator can sometimes return the r-value null.
Since PHP 7.4 a notice is emitted on array access on null (null["foo"]). Thus the operator ?[] could also be useful ($foo?["foo"]). Unfortunately, this code introduces a parser ambiguity because of the ternary operator and short array syntax ($foo?["foo"]:["bar"]). Because of this complication the ?[] operator is not part of this RFC.
In the given example we show multiple examples of valid arbitrary expressions that produce a class name. This shows a call to a function, string concatenation, and the ::class constant.
Note: The class name resolution using ::class is a compile time transformation. That means at the time the class name string is created no autoloading has happened yet. As a consequence, class names are expanded even if the class does not exist. No error is issued in that case.
Note: The nullsafe operator is best used when null is considered a valid and expected possible value for a property or method return. For indicating an error, a thrown exception is preferable.
Let's be honest, null values suck. They've been called the billion dollar mistake, but programmers in most languages still have to deal with it. (Except for those working in Rust.) They especially suck when you have a variable that could be an object or null. If it's an object, presumably you know it's type and what it can do; if it's null, you can't do much of anything with it.
In an ideal world, we could structure our code so that null is impossible. Sadly, we're not living in an ideal world, especially when we have to deal with someone else's code. For that reason, a number of languages have what is called a "nullsafe operator." And, as of version 8.0, PHP is one of them.
The nullsafe operator is really a modifier on object access, either properties or method calls. If either of those are preceded by a ?, it has the same effect as wrapping the access in an if (!is_null)) check. The example above, for instance, becomes:
In this example, the value returned by get_user() is either a User object or null. The value returned by User::getAddress() is either an Address object or null. If get_user() returns null, the ? catches that and returns null, ignoring the entire rest of the line. The same happens for getAddress(). At the end, $state is either a valid state value pulled from the state property of the address, or it's null.
If $catalog is null, get_seasonable_type() won't be called at all. The entire chain stops after detecting that $catalog is null and sets $bestSaleItem to null. Similarly, if getProducts() returns null, then mostPopular() is never called. However, that does not extend to arrays. If mostPopular() is still called and it returns null, you'll still get a Trying to access array offset on value of type null error.
There are a few limitations to the nullsafe operator, of course. It only works for reading properties or methods, not for writing to values. It also doesn't deal with references, because references require real values. (This is consistent with other languages that have a nullsafe operator.)
It also provides no indication of which step in the chain returned null.; If $bestSaleItem is null, it could be because $catalog was null, or getProducts() returned null, or mostPopular() returned null. There's no way to tell. Sometimes that's fine. If you do need to tell the difference, though, nullsafe won't help you. For that you really need an Either monad, but that's a topic for a very different time...
3a8082e126