Seien wir ehrlich: Nullwerte sind ätzend. Sie wurden als der Milliarden-Dollar-Fehlerbezeichnet , aber Programmierer müssen sich in den meisten Sprachen immer noch damit herumschlagen. (Mit Ausnahme derer, die in Rust arbeiten .) Besonders ätzend sind sie, wenn man eine Variable hat, die ein Objekt oder null sein könnte. Wenn sie ein Objekt ist, wissen Sie vermutlich ihren Typ und was sie tun kann; wenn sie null ist, können Sie nicht viel mit ihr anfangen.
Der Standardweg, mit dieser Situation umzugehen, ist die Verzweigung mit einer bedingten Prüfung, dem gleichnamigen "if null"-Muster:
<?php $user = get_user($id); if (!is_null($user)) { $address = $user->getAddress(); if (!is_null($address)) { $state = $address->state; If (!is_null($state)) { // And so on. } } }
Das ist natürlich grobschlächtig und schwer zu lesen, aber notwendig, wenn man den gefürchteten " Call to a member function on null
error" vermeiden will.
In einer idealen Welt könnten wir unseren Code so strukturieren, dass null unmöglich ist. Leider leben wir nicht in einer idealen Welt, vor allem dann nicht, wenn wir es mit dem Code eines anderen zu tun haben. Aus diesem Grund gibt es in einer Reihe von Sprachen einen so genannten "nullsicheren Operator". Und seit Version 8.0 ist PHP eine davon.
Der Nullsafe-Operator ist eigentlich ein Modifikator für Objektzugriffe, entweder für Eigenschaften oder für Methodenaufrufe. Wenn einem der beiden ein ?
vorangestellt ist, hat dies den gleichen Effekt wie die Einbettung des Zugriffs in eine if (!is_null))
Prüfung. Aus dem obigen Beispiel wird dann zum Beispiel:
<?php $state = get_user($id)?->getAddress()?->state;
In diesem Beispiel ist der von get_user()
zurückgegebene Wert entweder ein User-Objekt
oder null. Der von User::getAddress()
zurückgegebene Wert ist entweder ein Address-Objekt
oder null. Wenn get_user()
den Wert null zurückgibt, fängt das ?
das ab und gibt null zurück und ignoriert den gesamten Rest der Zeile. Dasselbe geschieht für getAddress()
. Am Ende ist $state
entweder ein gültiger Statuswert, der aus der State-Eigenschaft
der Adresse gezogen wird, oder er ist null.
Der nullsafe-Operator schließt den Rest der Zeile kurz, wenn das erste Mal ein Nullwert auftritt. Das bedeutet, dass selbst dynamische Aufrufe oder fehlerhafte Aufrufe später im Prozess nicht mehr stattfinden. Zum Beispiel:
<?php $bestSaleItem = $catalog?->getProducts(get_seasonal_type())?->mostPopular(10)[5];
Wenn $catalog
null ist, wird get_seasonable_type()
überhaupt nicht aufgerufen. Die gesamte Kette hält an, nachdem sie festgestellt hat, dass $catalog
null ist, und setzt $bestSaleItem
auf null. Wenn getProducts()
den Wert null
zurückgibt, wird mostPopular()
ebenfalls nicht aufgerufen. Dies gilt jedoch nicht für Arrays. Wenn mostPopular()
trotzdem aufgerufen wird und null zurückgibt, erhalten Sie immer noch den Fehler "Trying to access array offset on value of type null
".
Der nullsafe-Operator unterliegt natürlich einigen Einschränkungen. Er funktioniert nur beim Lesen von Eigenschaften oder Methoden, nicht beim Schreiben von Werten. Er kann auch nicht mit Referenzen umgehen, da Referenzen reale Werte erfordern. (Dies ist konsistent mit anderen Sprachen, die einen Nullsafe-Operator haben.)
Es gibt auch keinen Hinweis darauf, welcher Schritt in der Kette null zurückgegeben hat; wenn $bestSaleItem
null ist, könnte es daran liegen, dass $catalog
null war, oder getProducts()
null zurückgegeben hat, oder mostPopular()
null zurückgegeben hat. Es gibt keine Möglichkeit, das festzustellen. Manchmal ist das in Ordnung. Wenn Sie jedoch den Unterschied feststellen müssen, hilft Ihnen nullsafe nicht weiter. Dafür braucht man wirklich eine Either-Monade, aber das ist ein Thema für ein ganz anderes Mal...
Nichtsdestotrotz kann nullsafe dabei helfen, die Menge an Boilerplate-Code in Ihren objektorientierten Methodenketten zu reduzieren. Wir müssen Ilija Tovilo für den nullsafe RFCdanken .
Bleiben Sie dran für unseren nächsten Teil, in dem wir eine Reihe kleinerer Verbesserungen behandeln, die das Leben mit PHP 8.0 allgemein angenehmer machen werden.