¿Por qué PHP considera que 0 es igual a una cadena?

111

Tengo el siguiente código:

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

Su objetivo es inicializar el precio del artículo a 0 y luego obtener información sobre él. Si el precio se informa como 'e', ​​significa un intercambio en lugar de una venta, que se almacena en una base de datos como un número negativo.

También existe la posibilidad de dejar el precio en 0, ya sea porque el artículo es un bono o porque el precio se fijará en un momento posterior.

Pero, siempre que no se establece el precio, lo que lo deja con el valor inicial de 0, el ifciclo indicado arriba se evalúa como verdadero y el precio se establece en -1. Es decir, considera que 0 es igual a 'e'.

¿Cómo se puede explicar esto?

Cuando el precio se proporciona como 0 (después de la inicialización), el comportamiento es errático: a veces, si se evalúa como verdadero, a veces se evalúa como falso. *

Sérgio Domingues
fuente
1
Descubrí que usar triple === en lugar de double == da el comportamiento esperado. Pero todavía es extraño.
Sérgio Domingues
2
(referencia) suficientemente explicado en el Manual de PHP en el capítulo Malabarismo de tipos e ilustrado en la Tabla de comparación de tipos
Gordon
1
Si el único tipo de cadena posible es 'e', ​​¿no puede simplemente buscar una verificación is_string ($ item ["price"])? Eso sería un poco más eficiente que ===. [cita requerida]
Jimmie Lin
en comparaciones débiles entre cadena y entero, la cadena se convierte en entero (en lugar de "promocionar" el entero a cadena). if((string)$item['price'] == 'e')corrige el comportamiento extraño. Consulte stackoverflow.com/a/48912540/1579327 para obtener más detalles
Paolo
Tenga en cuenta otro caso en los comentarios a continuación de @Paolo, donde 0 (entero) es igual a cualquier otra cadena cuando se usa el operador doble igual.
Haitham Sweilem

Respuestas:

113

Está haciendo lo ==que clasifica los tipos por usted.

0es un int, por lo que en este caso se convertirá 'e'en un int. Que no se puede analizar como uno y se convertirá en 0. ¡Una cuerda '0e'se convertiría 0y coincidiría!

Utilizar ===

Nanne
fuente
14
Otra desventaja de la comparación imprecisa.
MC Emperor
5
Complicado. Me encontré con este y me sorprendió por qué string == 0. Tengo que recordar eso.
Grzegorz
2
También me estaba rascando la cabeza con esto cuando estaba haciendo un bucle en las claves de cadena, pero la matriz tenía un elemento de índice inicial 'cero' que seguía resultando en verdadero en la primera comparación de claves de cadena. Yo estaba como que? ¡Cómo diablos ... así que, efectivamente, esta respuesta lo aclaró! Me sorprende que toda esta pregunta no tenga UNA respuesta aceptada. Solo demuestra que algunos de los que preguntan son idiotas.
IncredibleHat
48

Esto se debe a cómo PHP realiza la operación de ==comparación que denota el operador de comparación :

Si compara un número con una cadena o si la comparación incluye cadenas numéricas, cada cadena se convierte en un número y la comparación se realiza numéricamente. […] La conversión de tipo no se produce cuando la comparación es ===o !==porque implica comparar el tipo y el valor.

Como el primer operando es un número ( 0) y el segundo es una cadena ( 'e'), la cadena también se convierte en un número (consulte también la tabla Comparación con varios tipos ). La página del manual sobre el tipo de datos de cadena define cómo se realiza la conversión de cadena a número :

Cuando se evalúa una cadena en un contexto numérico, el valor y el tipo resultantes se determinan de la siguiente manera.

Si la cadena no contiene ninguno de los caracteres ' .', ' e' o ' E' y el valor numérico se ajusta a los límites de tipo entero (según lo definido por PHP_INT_MAX), la cadena se evaluará como un número entero. En todos los demás casos, se evaluará como flotante.

En este caso, la cadena es 'e'y, por lo tanto, se evaluará como un flotante:

El valor viene dado por la parte inicial de la cadena. Si la cadena comienza con datos numéricos válidos, este será el valor utilizado. De lo contrario, el valor será 0(cero). Los datos numéricos válidos son un signo opcional, seguido de uno o más dígitos (que opcionalmente contienen un punto decimal), seguido de un exponente opcional. El exponente es un " eo E" seguido de uno o más dígitos.

Como 'e'no comienza con datos numéricos válidos, se evalúa como flotante 0.

Gumbo
fuente
3
php diseña casi todo para que sea fácilmente comparable y luego agrega algunas trampas solo para arruinar nuestro día. Esto no encaja con el resto de la filosofía de diseño de PHP. A menos que haya engaño, ¿hay filosofía ???
user3338098
1
especialmente porque "e" se convierte en verdadero y "" en falso
user3338098
20
"ABC" == 0

evalúa trueporque primero "ABC" se convierte a entero y 0 luego se compara con 0.

Este es un comportamiento extraño del lenguaje PHP: normalmente uno esperaría 0ser promovido a cadena "0"y luego compararlo "ABC"con un resultado false. Quizás eso es lo que sucede en otros lenguajes como JavaScript, donde se "ABC" == 0evalúa la comparación débil false.

Hacer una comparación estricta resuelve el problema:

"ABC" === 0

evalúa false.

Pero, ¿qué pasa si necesito comparar números como cadenas con números?

"123" === 123

evalúa falseporque los términos izquierdo y derecho son de diferente tipo.

Lo que realmente se necesita es una comparación débil sin las trampas del malabarismo de tipo PHP.

La solución es promover explícitamente los términos a la cadena y luego hacer una comparación (estricto o débil ya no importa).

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

es

true

mientras

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

es

false


Aplicado al código original:

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

El operador == intentará hacer coincidir los valores incluso si son de diferentes tipos. Por ejemplo:

'0' == 0 will be true

Si también necesita una comparación de tipos, use el operador ===:

'0' === 0 will be false
Vincent Mimoun-Prat
fuente
9

Su problema es el operador doble igual, que encasillará al miembro derecho al tipo del izquierdo. Use estricto si lo prefiere.

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

Volvamos a su código (copiado arriba). En este caso, en la mayoría de los casos, $ item ['price'] es un número entero (excepto cuando es igual ae, obviamente). Como tal, según las leyes de PHP, PHP se convertirá "e"en entero, lo que cede int(0). (¿No me crees? <?php $i="e"; echo (int)$i; ?>).

Para alejarse fácilmente de esto, use el operador triple igual (comparación exacta), que verificará el tipo y no se encasillará implícitamente.

PD: un dato divertido de PHP: a == bno implica eso b == a. Tome su ejemplo e inviértalo: if ("e" == $item['price'])nunca se cumplirá realmente siempre que $ item ['price'] sea siempre un número entero.

Sébastien Renauld
fuente
7

Existe un método bastante útil en PHP para validar una combinación de "0", "falso", "apagado" como == falso y "1", "encendido", "verdadero" como == verdadero, que a menudo se pasa por alto. Es particularmente útil para analizar argumentos GET / POST:

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

No es totalmente relevante para este caso de uso, pero dada la similitud y el hecho de que este es el resultado que la búsqueda tiende a encontrar cuando se hace la pregunta de validar (cadena) "0" como falso, pensé que ayudaría a otros.

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

Jim Morrison
fuente
6

Debería usar en ===lugar de ==, porque el operador ordinario no compara los tipos. En su lugar, intentará encasillar los elementos.

Mientras tanto, se ===toma en consideración el tipo de artículos.

  • === significa "igual",
  • == significa "eeeeh ... un poco parece"
tereško
fuente
1
Veo. Esto ahora funciona (con un encasillado):if((string)$item['price']=='e'){ $item['price'] = -1; }
Sérgio Domingues
pero no deberías hacer eso. solo use el ===operador
tereško
3

Creo que es mejor mostrar con ejemplos que hice, mientras me encuentro con el mismo comportamiento extraño. Vea mi caso de prueba y espero que le ayude a comprender mejor el comportamiento:

// 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)
Kosem
fuente
Gran prueba, hice lo mismo pero hice una bonita mesa. vea mi respuesta
IAMTHEBEST
0

Básicamente, utilice siempre el ===operador, para garantizar la seguridad del tipo.

Ingrese la descripción de la imagen aquí

Ingrese la descripción de la imagen aquí

YO SOY EL MEJOR
fuente