Abschnitt 3 · Lektion 6
Vergleichen
Zwei Werte vergleichen klingt nach der einfachsten Sache der Welt. In PHP ist es die Stelle, an der die meisten Anfängerfehler entstehen, und es ist die einzige Eigenheit der Sprache, die es bis in Sicherheitsberichte geschafft hat.
Der Grund ist eine Entscheidung aus den neunziger Jahren: PHP wollte hilfsbereit sein und Werte, die nur ähnlich aussehen, als gleich durchgehen lassen.
Zwei Gleichheitszeichen, oder drei
<?php
$eingabe = "5";
$erwartet = 5;
echo 'mit == : ', var_export($eingabe == $erwartet, true), "\n";
echo 'mit === : ', var_export($eingabe === $erwartet, true), "\n";
var_dump($eingabe);
var_dump($erwartet); == fragt: Sind diese Werte gleich, wenn ich sie vorher aneinander angleiche? === fragt: Sind
sie dasselbe, Wert und Sorte?
Im Beispiel steht in $eingabe der Text "5" und in $erwartet die Zahl 5. Mit == gilt das als
gleich, mit === nicht. Die beiden var_dump darunter zeigen, warum: string(1) "5" gegen
int(5), das sind zwei verschiedene Dinge mit demselben Aussehen. Ohne Datei geht dieselbe Frage
im Terminal: php -r 'var_dump("5" == 5, "5" === 5);' antwortet mit bool(true) und bool(false).
Das ist übrigens der Grund, warum du in diesem Kurs schon so oft var_dump gesehen hast. Ein echo
zeigt dir bei beiden Werten eine 5 und verschweigt genau die Information, um die es hier geht.
Neu in diesem Beispiel ist var_export. Es zeigt denselben Wert wie var_dump, gibt ihn aber
zurück, statt ihn auszugeben, wenn du true als zweites Argument mitgibst. Aus einem
Wahrheitswert wird damit der Text true oder false, und den kannst du mitten in eine Zeile setzen.
Dasselbe Verhältnis wie bei printf und sprintf aus Lektion 3.4: das eine schreibt, das andere
liefert.
Was aufgeräumt wurde, und was geblieben ist
<?php
echo '0 == "abc" ergibt ', var_export(0 == "abc", true), "\n";
echo '"1" == "01" ergibt ', var_export("1" == "01", true), "\n";
echo '"10" == "1e1" ergibt ', var_export("10" == "1e1", true), "\n";
echo '"1" == " 1" ergibt ', var_export("1" == " 1", true), "\n";
echo 'null == false ergibt ', var_export(null == false, true), "\n";
echo '"" == null ergibt ', var_export("" == null, true), "\n";
echo '0.1 + 0.2 == 0.3 ergibt ', var_export(0.1 + 0.2 == 0.3, true), "\n"; Bis PHP 7 galt 0 == "abc" als wahr. PHP machte aus dem Text eine Zahl, was bei "abc" eine Null
ergab, und verglich dann zwei Nullen. Damit war jede Prüfung „ist diese Nummer null” für beliebigen
Text offen. Seit PHP 8 ist dieser Fall erledigt: Vergleicht man eine Zahl mit Text, der keine Zahl
ist, wird stattdessen die Zahl zu Text gemacht, und "0" ist nun einmal nicht "abc".
Geblieben ist alles, was sich in beide Richtungen als Zahl lesen lässt, und das sind mehr Fälle,
als man denkt. "1" und "01" gelten als gleich. "10" und "1e1" ebenfalls, denn 1e1 ist die
wissenschaftliche Schreibweise für zehn. Und "1" ist gleich " 1" mit führendem Leerzeichen, was
bei einer Eingabe aus einem Formular schneller vorkommt, als dir lieb ist.
Dazu die beiden Fälle mit null, die aus Lektion 2.5 hinüberreichen: null == false und "" == null
sind beide wahr. Mit === ist keiner davon wahr, und das ist auch die ehrlichere Antwort, denn „hier
ist nichts” und „hier ist ein leerer Text” sind zwei verschiedene Zustände.
Die letzte Zeile schließt einen Faden aus Lektion 2.3. 0.1 + 0.2 == 0.3 ist falsch, obwohl echo
bei beiden 0.3 anzeigt. Deshalb der Satz von damals: Prüf zwei gebrochene Zahlen nie auf Gleichheit.
Bei Geld rechnest du in Cent, und dann vergleichst du ganze Zahlen, bei denen die Frage gar nicht
auftaucht.
Warum daraus eine Lücke wird
<?php
$gespeichert = "0e462097431906509019562988736854";
$geraten = "0e830400451993494058024219903391";
echo "Zwei voellig verschiedene Zeichenketten.\n";
echo "Mit == gleich? ", var_export($gespeichert == $geraten, true), "\n";
echo "Mit === gleich? ", var_export($gespeichert === $geraten, true), "\n"; Sieh dir die beiden Zeichenketten im Beispiel an. Sie haben 32 Stellen, und nach dem 0e am Anfang
sind es zwei verschiedene Ziffernfolgen. Trotzdem sagt ==, dass sie gleich sind.
Der Grund ist die Regel von oben, und er steckt genau in diesen zwei Zeichen: Beide lassen sich als
Zahl lesen, 0e462... heißt „null mal zehn hoch irgendwas”, und das ist null. Also vergleicht PHP
zwei Nullen.
So etwas steht nicht nur in Lehrbüchern. Solche Zeichenketten kommen aus Hash-Funktionen, und wer
einen gespeicherten Wert mit == gegen eine Eingabe prüft, hat unter Umständen eine Anmeldung
gebaut, die sich mit dem falschen Passwort öffnen lässt. Man muss dafür Glück haben und die richtige
Sorte Eingabe finden, aber genau danach sucht jemand, der es darauf anlegt.
Die Regel für diesen Kurs, und für alles, was du danach schreibst: Nimm ===. Wenn du ==
brauchst, weißt du in dem Moment genau warum, und dann schreibst du einen Kommentar dazu. In
Abschnitt 15 kommt für Passwörter noch ein eigenes Werkzeug dazu, das auch die Vergleichsdauer
gleich hält. Bis dahin sind drei Gleichheitszeichen die Antwort.
Für ungleich gilt dasselbe in Grün: != gleicht an, !== nicht. Nimm !==.
Zwei Werkzeuge nebenbei
<?php
echo "3 <=> 5 ergibt ", 3 <=> 5, "\n";
echo "5 <=> 3 ergibt ", 5 <=> 3, "\n";
echo "3 <=> 3 ergibt ", 3 <=> 3, "\n";
echo "Vorgabe statt Warnung: ", $gibtEsNicht ?? "unbekannt", "\n";
$leer = "";
echo "Ein leerer Text ist gesetzt: [", $leer ?? "unbekannt", "]\n"; <=> vergleicht zwei Werte und liefert eine Zahl statt eines Wahrheitswerts: -1, wenn der linke
kleiner ist, 1, wenn er größer ist, 0 bei Gleichstand. Alleine wirkt das nutzlos, und in Abschnitt
5 wird daraus die Zeile, mit der du eine Liste nach eigenen Regeln sortierst.
?? hat mit Vergleichen streng genommen nichts zu tun, gehört aber hierher, weil es dieselbe Sorte
Verwirrung auflöst. $wert ?? "unbekannt" heißt: nimm $wert, außer es gibt ihn nicht oder er ist
null, dann nimm das andere. Anders als ein echo auf eine unbekannte Variable erzeugt das keine
Warnung, und das macht es zum Standardgriff für alles, was von außen kommt.
Wichtig dabei: ?? sieht nur auf „nicht vorhanden oder null”. Ein leerer Text kommt durch, wie die
letzte Zeile im Beispiel zeigt. Wer auch den abfangen will, braucht die Werkzeuge aus Abschnitt 4.
Zum Mitnehmen
Zwei Gleichheitszeichen fragen, ob zwei Werte ähnlich genug sind. Drei fragen, ob sie dasselbe sind. Der Unterschied hat schon echte Anmeldungen geöffnet, und deshalb hat er hier eine eigene Lektion.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.
Was in dieser Lektion steckt
-
Artikel mit 4 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe, dein Code läuft auf einem Server
Öffnet sich mit dem Basis Konto.