Perché PHP considera 0 uguale a una stringa?


111

Ho il seguente pezzo di codice:

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

Ha lo scopo di inizializzare il prezzo dell'articolo su 0 e quindi ottenere informazioni su di esso. Se il prezzo è indicato come "e", significa uno scambio invece di una vendita, che viene memorizzato in un database come numero negativo.

C'è anche la possibilità di lasciare il prezzo a 0, sia perché l'articolo è un bonus, sia perché il prezzo verrà fissato in un secondo momento.

Ma, ogni volta che il prezzo non è impostato, che lo lascia con il valore iniziale di 0, il ifciclo sopra indicato viene valutato come vero e il prezzo è impostato a -1. Cioè, considera 0 uguale a "e".

Come si spiega questo?

Quando il prezzo viene fornito come 0 (dopo l'inizializzazione), il comportamento è irregolare: a volte l'if viene valutato come vero, a volte come falso. *


1
Ho scoperto che l'uso di triple === invece di double == dà il comportamento previsto. Ma è ancora strano.
Sérgio Domingues

2
(riferimento) sufficientemente spiegato nel manuale PHP al capitolo Type Juggling e illustrato nella Type Comparison Table
Gordon

1
Se l'unico tipo di stringa possibile è 'e', ​​non puoi semplicemente fare un controllo is_string ($ item ["price"])? Sarebbe un po 'più efficiente di ===. [citazione necessaria]
Jimmie Lin

nei confronti deboli tra stringa e numero intero la stringa viene convertita in numero intero (invece del numero intero "promosso" in stringa). if((string)$item['price'] == 'e')risolve il comportamento strano. Vedi stackoverflow.com/a/48912540/1579327 per maggiori dettagli
Paolo

Si prega di notare un altro caso nei commenti sotto di @Paolo dove 0 (intero) è uguale a qualsiasi altra stringa quando si utilizza l'operatore doppio uguale.
Haitham Sweilem

Risposte:


113

Stai facendo ciò ==che seleziona i tipi per te.

0è un int, quindi in questo caso verrà eseguito il cast 'e'a un int. Che non è analizzabile come uno e lo diventerà 0. Una stringa '0e'diventerebbe 0e corrisponderebbe!

Uso ===


14
Un altro svantaggio del confronto approssimativo.
MC Emperor

5
Tricky one. Mi sono imbattuto in questo e sono rimasto sorpreso dal motivo per cui string == 0. Devo ricordarlo.
Grzegorz,

2
Mi stavo grattando la testa anche su questo quando stavo eseguendo il loop delle chiavi di stringa, ma l'array aveva un elemento di indice "zero" iniziale che continuava a risultare vero nel primo confronto di chiavi di stringa. Ero come cosa? Come in ... quindi, abbastanza sicuro, questa risposta lo ha chiarito! Sono sorpreso che l'intera domanda non abbia UNA risposta accettata. Mostra solo che alcune domande che fanno le domande sono dei cretini.
IncredibleHat

48

Ciò è dovuto al modo in cui PHP esegue l'operazione di confronto che l' ==operatore di confronto indica:

Se si confronta un numero con una stringa o il confronto coinvolge stringhe numeriche, ogni stringa viene convertita in un numero e il confronto viene eseguito numericamente. […] La conversione del tipo non avviene quando il confronto è ===o !==poiché ciò implica il confronto del tipo e del valore.

Poiché il primo operando è un numero ( 0) e il secondo è una stringa ( 'e'), anche la stringa viene convertita in un numero (vedere anche la tabella Confronto con vari tipi ). La pagina di manuale sul tipo di dati stringa definisce come viene eseguita la conversione da stringa a numero :

Quando una stringa viene valutata in un contesto numerico, il valore e il tipo risultanti vengono determinati come segue.

Se la stringa non contiene nessuno dei caratteri " .", " e" o " E" e il valore numerico rientra nei limiti di tipo intero (come definito da PHP_INT_MAX), la stringa verrà valutata come un numero intero. In tutti gli altri casi verrà valutato come un float.

In questo caso la stringa è 'e'e quindi verrà valutata come un float:

Il valore è dato dalla parte iniziale della stringa. Se la stringa inizia con dati numerici validi, questo sarà il valore utilizzato. Altrimenti, il valore sarà 0(zero). I dati numerici validi sono un segno facoltativo, seguito da una o più cifre (eventualmente contenenti un punto decimale), seguito da un esponente facoltativo. L'esponente è un " e" o " E" seguito da una o più cifre.

Poiché 'e'non inizia con un dato numerico valido, restituisce float 0.


3
php progetta quasi tutto per essere facilmente confrontabile e poi aggiunge alcuni trucchi solo per rovinare la nostra giornata. Questo non è in linea con il resto della filosofia di progettazione di PHP. A meno che non ci sia inganno la filosofia ???
user3338098

1
soprattutto perché "e"
restituisce

20
"ABC" == 0

valuta trueperché prima "ABC" viene convertito in numero intero e diventa 0 quindi viene confrontato con 0.

Questo è uno strano comportamento del linguaggio PHP: normalmente ci si aspetterebbe 0di essere promosso a stringa "0"e quindi confrontato "ABC"con un risultato false. Forse è quello che succede in altri linguaggi come JavaScript, dove il confronto debole "ABC" == 0valuta false.

Fare un confronto rigoroso risolve il problema:

"ABC" === 0

valuta false.

Ma cosa succede se devo confrontare i numeri come stringhe con i numeri?

"123" === 123

valuta falseperché i termini sinistro e destro sono di tipo diverso.

Ciò che è effettivamente necessario è un confronto debole senza le insidie ​​della giocoleria di tipo PHP.

La soluzione è promuovere esplicitamente i termini in stringa e quindi fare un confronto (rigoroso o debole non importa più).

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

è

true

mentre

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

è

false


Applicato al codice originale:

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

9

L'operatore == proverà a far corrispondere i valori anche se sono di tipi diversi. Per esempio:

'0' == 0 will be true

Se hai bisogno anche del confronto del tipo, usa l'operatore ===:

'0' === 0 will be false

9

Il tuo problema è l'operatore doppio uguale, che tipizzerà il membro destro al tipo di sinistra. Usa rigoroso se preferisci.

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

Torniamo al tuo codice (copiato sopra). In questo caso, nella maggior parte dei casi, $ item ['prezzo'] è un numero intero (tranne quando è uguale a e, ovviamente). In quanto tale, per le leggi di PHP, PHP verrà convertito "e"in un intero, che restituisce int(0). (Non mi credi?<?php $i="e"; echo (int)$i; ?> ).

Per sfuggire facilmente a questo, usa l'operatore triplo uguale (confronto esatto), che controllerà il tipo e non implicitamente typecast.

PS: un fatto divertente PHP: a == bnon implica questo b == a. Prendi il tuo esempio e invertilo: if ("e" == $item['price'])non sarà mai effettivamente soddisfatto a condizione che $ item ['price'] sia sempre un numero intero.


7

C'è un metodo piuttosto pratico in PHP per convalidare una combinazione di "0", "false", "off" come == false e "1", "on", "true" come == true che viene spesso trascurato. È particolarmente utile per analizzare gli argomenti GET / POST:

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

Non è del tutto rilevante per questo caso d'uso, ma data la somiglianza e il fatto che questo è il risultato che la ricerca tende a trovare quando si pone la domanda di validare (stringa) "0" come falso, ho pensato che avrebbe aiutato gli altri.

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


6

Dovresti usare ===invece di== , perché l'operatore ordinario non confronta i tipi. Invece tenterà di eseguire il typecast degli elementi.

Nel frattempo ===prende in considerazione il tipo di articoli.

  • === significa "uguale",
  • == significa "eeeeh .. sembra quasi"

1
Vedo. Questo ora funziona (con un typecast):if((string)$item['price']=='e'){ $item['price'] = -1; }
Sérgio Domingues

ma non dovresti farlo. basta usare l' ===operatore
tereško

3

Penso che sia meglio mostrare con esempi che ho fatto, mentre mi imbattevo nello stesso strano comportamento. Guarda il mio test case e spero che ti aiuti a capire meglio il comportamento:

// 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)

Grande prova, ho fatto lo stesso ma ho fatto un bel tavolo. vedi la mia risposta
IAMTHEBEST

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.