Versuch, einen Überblick über die verschiedenen Diskussionen und Antworten zu geben:
Es gibt keine einzige Antwort auf die Frage, die alle Verwendungsmöglichkeiten ersetzen isset
kann. Einige Anwendungsfälle werden von anderen Funktionen behandelt, während andere der Prüfung nicht standhalten oder über Code-Golf hinaus einen zweifelhaften Wert haben. Weit davon entfernt, "kaputt" oder "inkonsistent" zu sein, zeigen andere Anwendungsfälle, warum isset
die Reaktion auf null
das logische Verhalten ist.
Echte Anwendungsfälle (mit Lösungen)
1. Array-Schlüssel
Arrays können wie Sammlungen von Variablen behandelt werden, mit unset
und isset
behandelt sie , als ob sie waren. Da sie jedoch iteriert, gezählt usw. werden können, ist ein fehlender Wert nicht mit einem Wert identisch, dessen Wert ist null
.
Die Antwort in diesem Fall ist zu verwenden , array_key_exists()
stattisset()
.
Da das Array als Funktionsargument überprüft werden muss, gibt PHP weiterhin "Benachrichtigungen" aus, wenn das Array selbst nicht vorhanden ist. In einigen Fällen kann mit Recht argumentiert werden, dass jede Dimension zuerst initialisiert werden sollte, damit die Benachrichtigung ihre Aufgabe erfüllt. In anderen Fällen würde eine "rekursive" array_key_exists
Funktion, die nacheinander jede Dimension des Arrays prüft, dies vermeiden, wäre aber im Grunde dieselbe wie @array_key_exists
. Es ist auch etwas tangential zum Umgang mitnull
Werten.
2. Objekteigenschaften
In der traditionellen Theorie der "objektorientierten Programmierung" sind Kapselung und Polymorphismus Schlüsseleigenschaften von Objekten; in einer klassenbasierten OOP Implementierung wie PHP, werden die eingekapselten Eigenschaften als Teil der Klassendefinition deklarieren, und gegebenen Zugriffsebene ( public
, protected
oderprivate
).
Mit PHP können Sie einem Objekt jedoch auch dynamisch Eigenschaften hinzufügen, wie Sie es für ein Array tun würden, und einige Benutzer verwenden klassenlose Objekte (technisch gesehen Instanzen des integrierten Objekts) stdClass
, das keine Methoden oder privaten Funktionen enthält) in ähnlicher Weise Weg zu assoziativen Arrays. Dies führt zu Situationen, in denen eine Funktion möglicherweise wissen möchte, ob dem ihr zugewiesenen Objekt eine bestimmte Eigenschaft hinzugefügt wurde.
Wie bei Array-Schlüsseln ist in der Sprache eine Lösung zum Überprüfen von Objekteigenschaften enthalten, die vernünftigerweise aufgerufen wirdproperty_exists
.
Nicht vertretbare Anwendungsfälle mit Diskussion
3. register_globals
und andere Verschmutzung des globalen Namespace
Die register_globals
Funktion fügte dem globalen Bereich Variablen hinzu, deren Namen durch Aspekte der HTTP-Anforderung (GET- und POST-Parameter sowie Cookies) bestimmt wurden. Dies kann zu fehlerhaftem und unsicherem Code führen, weshalb er seit PHP 4.2, veröffentlicht im August 2000, standardmäßig deaktiviert und in PHP 5.4, veröffentlicht im März 2012, vollständig entfernt wurde . Es ist jedoch möglich, dass einige Systeme noch mit aktivierter oder emulierter Funktion ausgeführt werden. Es ist auch möglich, den globalen Namespace mit dem global
Schlüsselwort oder $GLOBALS
Array auf andere Weise zu "verschmutzen" .
Erstens register_globals
ist es unwahrscheinlich , dass selbst unerwartet eine null
Variable erzeugt wird, da die GET-, POST- und Cookie-Werte immer Zeichenfolgen sind (wobei ''
immer noch true
von zurückgegeben wird isset
) und die Variablen in der Sitzung vollständig unter der Kontrolle des Programmierers stehen sollten.
Zweitens ist die Verschmutzung einer Variablen mit dem Wert null
nur dann ein Problem, wenn dadurch eine vorherige Initialisierung überschrieben wird. Das "Überschreiben" einer nicht initialisierten Variablen mit null
wäre nur dann problematisch, wenn Code irgendwo anders zwischen den beiden Zuständen unterscheiden würde. Diese Möglichkeit allein ist also ein Argument gegen eine solche Unterscheidung.
4. get_defined_vars
undcompact
Mit einigen in PHP selten verwendeten Funktionen wie get_defined_vars
und compact
können Sie Variablennamen so behandeln, als wären sie Schlüssel in einem Array. Für globale Variablen ermöglicht das superglobale Array$GLOBALS
einen ähnlichen Zugriff und ist häufiger. Diese Zugriffsmethoden verhalten sich anders, wenn eine Variable nicht im entsprechenden Bereich definiert ist.
Sobald Sie sich entschieden haben, eine Reihe von Variablen mithilfe eines dieser Mechanismen als Array zu behandeln, können Sie dieselben Operationen wie bei jedem normalen Array ausführen. Folglich siehe 1.
Funktionen, die nur existierten, um vorherzusagen, wie sich diese Funktionen verhalten werden (z. B. "Gibt es einen Schlüssel 'foo' in dem von zurückgegebenen Array get_defined_vars
?") , Sind überflüssig, da Sie die Funktion einfach ausführen und ohne negative Auswirkungen herausfinden können.
4a. Variable Variablen ( $$foo
)
Obwohl dies nicht ganz mit Funktionen identisch ist, die eine Reihe von Variablen in ein assoziatives Array verwandeln, kann und sollte in den meisten Fällen die Verwendung von "Variablenvariablen" ("Zuweisen einer Variablen, die auf der Grundlage dieser anderen Variablen benannt ist") geändert werden, um stattdessen ein assoziatives Array zu verwenden .
Ein Variablenname ist im Grunde die Bezeichnung, die ein Wert vom Programmierer erhält. Wenn Sie es zur Laufzeit bestimmen, ist es nicht wirklich ein Label, sondern ein Schlüssel in einem Schlüsselwertspeicher. Praktischer gesagt, wenn Sie kein Array verwenden, verlieren Sie die Fähigkeit zu zählen, zu iterieren usw.; Es kann auch unmöglich werden, eine Variable "außerhalb" des Schlüsselwertspeichers zu haben, da sie möglicherweise von überschrieben wird $$foo
.
Nach der Änderung zur Verwendung eines assoziativen Arrays kann der Code für Lösung 1 verwendet werden. Der indirekte Zugriff auf Objekteigenschaften (z. B. $foo->$property_name
) kann mit Lösung 2 behandelt werden.
5. isset
ist so viel einfacher zu tippen alsarray_key_exists
Ich bin mir nicht sicher, ob dies wirklich relevant ist, aber ja, die Funktionsnamen von PHP können manchmal ziemlich langwierig und inkonsistent sein. Anscheinend haben prähistorische Versionen von PHP die Länge eines Funktionsnamens als Hash-Taste verwendet, daher hat Rasmus absichtlich Funktionsnamen so erfunden, htmlspecialchars
dass sie eine ungewöhnliche Anzahl von Zeichen haben würden ...
Zumindest schreiben wir kein Java, oder? ;)
6. Nicht initialisierte Variablen haben einen Typ
Die Handbuchseite zu Variablengrundlagen enthält folgende Anweisung:
Nicht initialisierte Variablen haben abhängig vom Kontext, in dem sie verwendet werden, einen Standardwert ihres Typs
Ich bin mir nicht sicher, ob es in der Zend Engine eine Vorstellung von "nicht initialisiertem, aber bekanntem Typ" gibt oder ob dies zu viel in die Aussage einliest.
Klar ist, dass es keinen praktischen Unterschied zu ihrem Verhalten macht, da die auf dieser Seite beschriebenen Verhaltensweisen für nicht initialisierte Variablen mit dem Verhalten einer Variablen identisch sind, deren Wert ist null
. Um ein Beispiel auszuwählen, werden beide $a
und $b
in diesem Code als Ganzzahl angezeigt 42
:
unset($a);
$a += 42;
$b = null;
$b += 42;
(Der erste wird einen Hinweis auf eine nicht deklarierte Variable auslösen, um Sie dazu zu bringen, besseren Code zu schreiben, aber es macht keinen Unterschied, wie der Code tatsächlich ausgeführt wird.)
99. Erkennen, ob eine Funktion ausgeführt wurde
(Behalte dieses letzte, da es viel länger ist als die anderen. Vielleicht werde ich es später bearbeiten ...)
Betrachten Sie den folgenden Code:
$test_value = 'hello';
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
Wenn zurückkehren some_function
kann null
, besteht die Möglichkeit, dass das echo
nicht erreicht wird, obwohl es some_test
zurückgegeben wird true
. Der Programmierer wollte erkennen, wann $result
noch nie eingestellt wurde, aber PHP erlaubt es ihnen nicht.
Es gibt jedoch andere Probleme mit diesem Ansatz, die deutlich werden, wenn Sie eine äußere Schleife hinzufügen:
foreach ( $list_of_tests as $test_value ) {
// something's missing here...
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
}
Da $result
es nie explizit initialisiert wird, nimmt es beim Bestehen des ersten Tests einen Wert an, sodass nicht festgestellt werden kann, ob nachfolgende Tests bestanden wurden oder nicht. Dies ist tatsächlich ein äußerst häufiger Fehler, wenn Variablen nicht richtig initialisiert werden.
Um dies zu beheben, müssen wir etwas in der Zeile tun, in der ich kommentiert habe, dass etwas fehlt. Die naheliegendste Lösung besteht darin $result
, einen "Endwert" some_function
festzulegen, der niemals zurückkehren kann. wenn das istnull
der Fall ist, funktioniert der Rest des Codes einwandfrei. Wenn es keinen natürlichen Kandidaten für einen Endwert gibt, weil er some_function
einen extrem unvorhersehbaren Rückgabetyp hat (was an sich wahrscheinlich ein schlechtes Vorzeichen ist) $found
, könnte stattdessen ein zusätzlicher boolescher Wert verwendet werden, z .
Gedankenexperiment eins: das very_null
Konstante
PHP könnte theoretisch eine spezielle Konstante - sowie null
- zur Verwendung als Endwert hier bereitstellen ; vermutlich wäre es illegal, dies von einer Funktion zurückzugeben, oder es würde dazu gezwungen werdennull
, und dasselbe würde wahrscheinlich für die Übergabe als Funktionsargument gelten. Das würde diesen sehr spezifischen Fall etwas einfacher machen, aber sobald Sie sich entschieden haben, den Code neu zu faktorisieren - zum Beispiel um die innere Schleife in eine separate Funktion zu setzen - würde er unbrauchbar werden. Wenn die Konstante zwischen Funktionen übergeben werden könnte, könnten Sie nicht garantieren, dass some_function
sie nicht zurückgegeben wird, sodass sie als universeller Terminalwert nicht mehr nützlich wäre.
Das Argument zum Erkennen nicht initialisierter Variablen in diesem Fall läuft auf das Argument für diese spezielle Konstante hinaus: Wenn Sie den Kommentar durch einen anderen ersetzen unset($result)
und diesen anders behandeln $result = null
, führen Sie einen "Wert" für ein$result
, der nicht weitergegeben werden kann und nur sein kann von bestimmten eingebauten Funktionen erkannt.
Gedankenexperiment zwei: Zuweisungszähler
Eine andere Art, darüber nachzudenken, was der Letzte if
fragt, ist: "Hat irgendetwas eine Aufgabe gemacht $result
?" Anstatt es als einen besonderen Wert von zu betrachten $result
, könnten Sie sich dies vielleicht als "Metadaten" über die Variable vorstellen, ähnlich wie Perls "Variable Tainting". Also anstatt isset
Sie es nennen könnte has_been_assigned_to
, und nicht unset
, reset_assignment_state
.
Aber wenn ja, warum bei einem Booleschen aufhören? Was ist, wenn Sie wissen möchten, wie oft der Test bestanden wurde ? Sie können Ihre Metadaten einfach auf eine Ganzzahl erweitern und haben get_assignment_count
undreset_assignment_count
...
Offensichtlich würde das Hinzufügen eines solchen Merkmals einen Kompromiss in Bezug auf Komplexität und Leistung der Sprache haben, so dass es sorgfältig gegen seinen erwarteten Nutzen abgewogen werden müsste. Wie bei einer very_null
Konstante wäre sie nur unter sehr engen Umständen nützlich und wäre ähnlich widerstandsfähig gegen Re-Factoring.
Die hoffentlich offensichtliche Frage ist, warum die PHP-Laufzeit-Engine im Voraus davon ausgehen sollte, dass Sie solche Dinge im Auge behalten möchten, anstatt sie explizit mit normalem Code ausführen zu lassen.