Warum betrachtet PHP 0 als gleich einer Zeichenfolge?


111

Ich habe den folgenden Code:

$item['price'] = 0;
/* Code to get item information goes in here */
if($item['price'] == 'e') {
    $item['price'] = -1;
}

Es ist beabsichtigt, den Artikelpreis auf 0 zu initialisieren und dann Informationen darüber zu erhalten. Wenn der Preis als "e" angegeben wird, bedeutet dies einen Umtausch anstelle eines Verkaufs, der als negative Zahl in einer Datenbank gespeichert wird.

Es besteht auch die Möglichkeit, den Preis als 0 zu belassen, entweder weil der Artikel ein Bonus ist oder weil der Preis zu einem späteren Zeitpunkt festgelegt wird.

Wenn jedoch der Preis nicht festgelegt ist und der Anfangswert 0 bleibt, wird die ifoben angegebene Schleife als wahr ausgewertet und der Preis auf -1 festgelegt. Das heißt, es betrachtet 0 als gleich 'e'.

Wie kann das erklärt werden?

Wenn der Preis als 0 angegeben wird (nach der Initialisierung), ist das Verhalten unberechenbar: Manchmal wird das if als wahr bewertet, manchmal als falsch. *


1
Ich fand heraus, dass die Verwendung von Triple === anstelle von Double == das erwartete Verhalten ergibt. Aber es ist immer noch komisch.
Sérgio Domingues

2
(Referenz) ausreichend erklärt im PHP-Handbuch im Kapitel Typ Jonglieren und illustriert in der Typvergleichstabelle
Gordon

1
Wenn der einzig mögliche Zeichenfolgentyp 'e' ist, können Sie dann nicht einfach eine Prüfung auf is_string ($ item ["price"]) durchführen? Das wäre etwas effizienter als ===. [Zitat benötigt]
Jimmie Lin

Bei schwachen Vergleichen zwischen Zeichenfolge und Ganzzahl wird die Zeichenfolge in eine Ganzzahl konvertiert (anstatt dass die Ganzzahl zur Zeichenfolge "heraufgestuft" wird). if((string)$item['price'] == 'e')behebt das seltsame Verhalten. Siehe stackoverflow.com/a/48912540/1579327 für weitere Details
Paolo

Bitte beachten Sie einen anderen Fall in den Kommentaren unten von @Paolo, in dem 0 (Ganzzahl) einer anderen Zeichenfolge entspricht, wenn Sie den Operator double equals verwenden.
Haitham Sweilem

Antworten:


113

Sie tun, ==was die Typen für Sie aussortiert.

0ist ein int, also wird es in diesem Fall 'e'in ein int umgewandelt. Welches ist nicht als eins analysierbar und wird werden 0. Eine Saite '0e'würde werden 0und passen!

Verwenden ===


14
Ein weiterer Nachteil des lockeren Vergleichs.
MC Emperor

5
Ein schwieriges. Ich bin gerade auf diesen gestoßen und war erstaunt, warum string == 0. Ich muss mich daran erinnern.
Grzegorz

2
Ich kratzte mir auch hier am Kopf, als ich die String-Schlüssel durchlief, aber das Array hatte ein anfängliches 'Null'-Indexelement, das beim ersten Vergleich der String-Schlüssel immer zu true führte. Ich war wie was? Wie in der ... also, sicher genug, hat diese Antwort das geklärt! Ich bin überrascht, dass diese ganze Frage keine Antwort akzeptiert hat. Nur um zu zeigen, dass einige Fragesteller Idioten sind.
IncredibleHat

48

Dies liegt daran, wie PHP die vom Vergleichsoperator angegebene ==Vergleichsoperation ausführt:

Wenn Sie eine Zahl mit einer Zeichenfolge vergleichen oder der Vergleich numerische Zeichenfolgen umfasst, wird jede Zeichenfolge in eine Zahl konvertiert und der Vergleich numerisch durchgeführt. […] Die Typkonvertierung findet nicht statt, wenn der Vergleich erfolgt ===oder !==wenn dies den Vergleich des Typs sowie des Werts beinhaltet.

Da der erste Operand eine Zahl ( 0) und der zweite eine Zeichenfolge ( 'e') ist, wird die Zeichenfolge ebenfalls in eine Zahl konvertiert (siehe auch Tabelle Vergleich mit verschiedenen Typen ). Auf der Handbuchseite zum Datentyp der Zeichenfolge wurde definiert, wie die Konvertierung von Zeichenfolgen in Zahlen durchgeführt wird:

Wenn eine Zeichenfolge in einem numerischen Kontext ausgewertet wird, werden der resultierende Wert und Typ wie folgt bestimmt.

Wenn die Zeichenfolge keines der Zeichen ' .', ' e' oder ' E' enthält und der numerische Wert in die Grenzwerte für Ganzzahltypen (wie durch definiert PHP_INT_MAX) passt , wird die Zeichenfolge als Ganzzahl ausgewertet. In allen anderen Fällen wird es als Float ausgewertet.

In diesem Fall ist der String 'e'und wird daher als float ausgewertet:

Der Wert wird durch den Anfangsabschnitt der Zeichenfolge angegeben. Wenn die Zeichenfolge mit gültigen numerischen Daten beginnt, wird dieser Wert verwendet. Andernfalls ist der Wert 0(Null). Gültige numerische Daten sind ein optionales Vorzeichen, gefolgt von einer oder mehreren Ziffern (optional mit einem Dezimalpunkt), gefolgt von einem optionalen Exponenten. Der Exponent ist ein ' e' oder ' E', gefolgt von einer oder mehreren Ziffern.

Da 'e'es nicht mit gültigen numerischen Daten beginnt, wird es als float ausgewertet 0.


3
PHP entwirft fast alles so, dass es leicht vergleichbar ist, und wirft dann ein paar Fallstricke ein, um unseren Tag zu ruinieren. Dies passt nicht zur restlichen Designphilosophie von PHP. Es sei denn, Täuschung gibt es Philosophie?
user3338098

1
zumal "e" in true und "" in false umgewandelt wird
user3338098

20
"ABC" == 0

wird ausgewertet, trueweil zuerst "ABC" in eine Ganzzahl konvertiert wird und 0 dann wird es mit verglichen 0.

Dies ist ein merkwürdiges Verhalten der PHP-Sprache: Normalerweise würde man erwarten 0, zum String befördert zu werden"0" und dann "ABC"mit einem Ergebnis verglichen zu werden false. Vielleicht passiert das in anderen Sprachen wie JavaScript, wo der schwache Vergleich "ABC" == 0ausgewertet wird false.

Ein strenger Vergleich löst das Problem:

"ABC" === 0

bewertet false.

Aber was ist, wenn ich Zahlen als Zeichenfolgen mit Zahlen vergleichen muss?

"123" === 123

bewertet false weil der linke und der rechte Begriff vom unterschiedlichen Typ sind.

Was tatsächlich benötigt wird, ist ein schwacher Vergleich ohne die Fallstricke des PHP-Jonglierens.

Die Lösung besteht darin, die Begriffe explizit auf String zu setzen und dann einen Vergleich durchzuführen (streng oder schwach spielt keine Rolle mehr).

(string)"123" === (string)123

ist

true

während

(string)"123" === (string)0

ist

false


Auf den Originalcode angewendet:

$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
    $item['price'] = -1;
}

9

Der Operator == versucht, Werte abzugleichen, auch wenn sie unterschiedlichen Typs sind. Zum Beispiel:

'0' == 0 will be true

Wenn Sie auch einen Typvergleich benötigen, verwenden Sie den Operator ===:

'0' === 0 will be false

9

Ihr Problem ist der Double-Equal-Operator, der das rechte Element in den Typ des linken Typs umwandelt. Verwenden Sie streng, wenn Sie es vorziehen.

if($item['price'] == 'e') {
    $item['price'] = -1;
}

Kehren wir zu Ihrem Code zurück (oben kopiert). In diesem Fall ist $ item ['price'] in den meisten Fällen eine Ganzzahl (außer wenn sie offensichtlich gleich e ist). Nach den Gesetzen von PHP wird PHP als "e"Ganzzahl typisiert , was ergibt int(0). (Glaubst du mir nicht?<?php $i="e"; echo (int)$i; ?> )

Verwenden Sie den Operator Triple Equal (exakter Vergleich), der den Typ überprüft und nicht implizit typisiert, um dies zu vermeiden.

PS: eine lustige PHP-Tatsache: a == bimpliziert das nicht b == a. Nehmen Sie Ihr Beispiel und kehren Sie es um: if ("e" == $item['price'])wird niemals tatsächlich erfüllt, vorausgesetzt, $ item ['price'] ist immer eine ganze Zahl.


7

In PHP gibt es eine ziemlich praktische Methode, um eine Mischung aus "0", "false", "off" als == false und "1", "on", "true" als == true zu validieren, die oft übersehen wird. Dies ist besonders nützlich, um GET / POST-Argumente zu analysieren:

filter_var( $item['price'], FILTER_VALIDATE_BOOLEAN );

Es ist nicht ganz relevant für diesen Anwendungsfall, aber angesichts der Ähnlichkeit und Tatsache, dass dies das Ergebnis ist, das die Suche häufig findet, wenn die Frage nach der Validierung (Zeichenfolge) "0" als falsch gestellt wird, dachte ich, es würde anderen helfen.

http://www.php.net/manual/en/filter.filters.validate.php


6

Sie sollten ===anstelle von verwenden== , da der normale Operator die Typen nicht vergleicht. Stattdessen wird versucht, die Elemente zu typisieren.

In der Zwischenzeit ===berücksichtigt die Art der Artikel.

  • === bedeutet "gleich",
  • == bedeutet "eeeeh .. sieht irgendwie aus wie"

1
Aha. Dies funktioniert jetzt (mit einem Typecast):if((string)$item['price']=='e'){ $item['price'] = -1; }
Sérgio Domingues

aber das solltest du nicht tun. Verwenden Sie einfach den ===Operator
Tereško

3

Ich denke, es ist am besten, anhand von Beispielen zu zeigen, die ich gemacht habe, während ich auf dasselbe seltsame Verhalten stoße. Sehen Sie sich meinen Testfall an und hoffentlich hilft er Ihnen dabei, das Verhalten besser zu verstehen:

// Normal comparison using the == Operator
echo (0 == "0"); // true
echo (0 == "a"); // true
echo (0 == "safta!"); // true
echo (1000 == "bla"); // false. It appears that PHP has a weird behavior only with the number / string 0 / "0" according to the past 3 examples.
echo (23 == "23"); // true. So as we said, PHP has a problem (not a problem but weird behavior) only when the number / string 0 (or "0") is present
echo (23 == "24"); // false. values aren't equal (unlike last example). The type is less relevant with the == operator as we can see.

// Now using the === and !== Operators
echo ("0" === 0); // false, since === requires both value and type to be the same. Here, type is different (int vs string)
echo ("0" !== 0); // true because they aren't the same in terms of === comparison (type is different and that's why it's true)
echo ("bla" === "blaa"); // false because the values are not the same. The type is the same, but === checks for both equal type and equal value.

//Now using casting and === Operator:
echo ((string)123 === "123"); // true. The casting of the int 123 to string changed it to "123" and now both variables have same value and are of same type
echo ((int)"123" === 123); // true. The casting of the string 123 to int, changed it to int, and now both variables are of same value and type (which is exactly what the === operator is looking for)

// Now using casting and == Operator. Basically, as we've seen above, the == care less for the
// type of var, but more to the value. So the casting is less relevant here, because even
// without casting, like we saw earlier, we can still compare string to int with the == operator
// and if their value is same, we'll get true. Either way, we will show that:
echo ((string)123 == "123"); // true. The casting of the int 123 to string changed it to "123" and now both vars have same value and are of same type
echo ((int)"123" == 123); // true. The casting of the string 123 to int, changed it to int, and now both vars are of same value and type (which is exactly what the === operator is looking for)

Toller Test, ich habe das gleiche gemacht, aber ich habe einen schönen Tisch daraus gemacht. siehe meine Antwort
IAMTHEBEST

0

Verwenden Sie grundsätzlich immer den ===Bediener, um die Typensicherheit zu gewährleisten.

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.