2010-07-18 7 views
12

Me encontré con un código PHP muy extraño. ¿Alguien podría explicar por qué sucede esto? ***** PUNTOS DE BONIFICACIÓN ***** si puede decirme por qué esto es útil.Comparaciones de valores php muy ilógicas

<?php 
if(0=='a'){ 
    print ord(0)." should NEVER equal ".ord('a')."<br>"; 
} 
if(false==0){ 
     print "false==0<br>"; 
} 
if('a'==false){ 
     print "a==false<br>"; 
} 
?> 

Y la salida resultante:

48 should NEVER equal 97 
false==0 
+1

Al igual que en Javascript, las comparaciones entre los diferentes tipos pueden no ser transitivas. Ese es el horror de la coerción tipo. – JAL

Respuesta

33

En PHP, 'a' no es el caracter ASCII a, sino la cadena a. En un contexto numérico, es igual a 0. Por ejemplo, intval('a') da como resultado un valor de 0.

Esto es útil porque PHP se usa principalmente para procesar texto, y uno podría querer probar la prueba (123 == '123'), que es verdadero. Y dado que un número en comillas simples (o dobles) se trata como el número, no tiene sentido que una cadena sin valor numérico sea tratada como 0.

Oh sí, una más cosa. 'a' en un contexto booleano es verdadero, no falso. Creo que esto hace que algunos tipos de procesamiento de texto sean más naturales, pero sinceramente, no puedo pensar en un ejemplo a esta hora tardía.

+0

Sí, es como tomar ''a '+ 0', ya que' a 'no es un número, simplemente se quita y se agrega a 0, lo que resulta en 0, ya que los enteros tienen una precedencia de conversión mayor que las cadenas. – animuson

+0

***** BONUS POINTS ***** otorgado. Gracias por la respuesta. – rook

3

ord() toma caracteres, por lo que PHP convierte en 0'0'. Y 0 es igual a false, aunque no es idéntico (===).

8

¡Bien, siempre está el PHP type cheat sheet para eso!

+0

+1 muy genial! – rook

+0

Wow, nunca me di cuenta de que empty() significaba isfalse() e is_null() significaba isnotset(). – aib

7

Este es un principio básico de los lenguajes de tipo débil/dinámicamente llamados type juggling. Los tipos se lanzarán a otros tipos en ciertas circunstancias. Cuando compara una cuerda con un número, la cuerda se convertirá en un número. Al comparar cualquier cosa con un booleano, ese valor se convertirá en booleano.

Therearerulesforeverytype en cuanto a cómo será echado en otro tipo o la forma en que compares a otros tipos. 'a' pasa a convertirse a 0 cuando se envía a un número (la única opción lógica, en realidad). Para evitar este tipo de conversión, pruebe no con el operador de igualdad ==, pero con el identity operator===.

Como James pointed out, esto es útil ya que PHP trata mucho con cadenas que realmente son números. Por ejemplo, los formularios HTML solo envían cadenas, incluso si el valor es un número. También permite un cierto código muy concisa, como:

$result = someOperation(); 
if (!$result) { 
    // $result may be null, false, 0, '' or array(), 
    // all of which we're not interested in 
    error(); 
} 

También significa que hay que tener mucho cuidado con lo que se debe comprobar en qué circunstancias, sin embargo, ya que un valor podría inesperadamente que en otra cosa. Y admitidamente, 'a' == 0 en sí mismo es realmente una trampa de malabarismo de tipo en lugar de útil. Es una de las situaciones en las que hay que tener cuidado y probar como if (is_numeric($var) && $var == 0).

1

Consulte la PHP type comparison tables del manual. Es muy útil tenerlo a mano hasta que lo hayas internalizado y haya sido inestimable para mi comprensión de qué es exactamente lo que se evaluará como verdadero y cuándo.

Otros ya han respondido al núcleo de la pregunta, pero creo que es importante decir que en PHP, la única cadena no vacía que no se evalúa como "verdadera" con el operador == es "0" como PHP trata cualquier cadena que contenga solo números como un entero o float.

La razón de esto es que PHP está bastante tipeado e intenta permitir que enteros, cadenas, flotantes y valores booleanos sean intercambiables. Un ejemplo real y extremadamente común de esto es que si está utilizando las funciones de mysql o PDO, las cadenas se devuelven para todo, incluso si la columna subyacente es un número entero.

Consideremos el siguiente código SQL:

CREATE TABLE `test`.`pants` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , 
`some_other_int` INT NOT NULL 
) ENGINE = InnoDB; 

INSERT INTO `test`.`pants` (`id`, `some_other_int`) 
VALUES ('1', '1'), ('2', '0'); 

Y el siguiente código: "! X campo no era una cadena"

<?php 

$c = mysql_connect('127.0.0.1', 'user', 'password'); 
mysql_select_db('test', $c); 
$r = mysql_query('SELECT * FROM pants', $c); 

while ($row = mysql_fetch_assoc($r)) { 
    var_dump($row); 
    foreach($row as $k=>$v) { 
     if (!is_string($v)) 
      echo "field {$v} was not a string!\n"; 
    } 
} 

El mensaje es nunca impreso, aunque cada columna en la base de datos es un número entero. Supongamos que quiere usar realmente la segunda fila en esa tabla.

<?php 
$id = 2; 
$r = mysql_query(sprintf('SELECT * FROM pants WHERE id=%s', mysql_real_esacpe_string($id)), $c); 
$row = mysql_fetch_assoc($r); 

// this is the important bit 
if (0 == $row['some_other_int']) { 
    echo "It was zero!"; 
} 

Si la cadena "0" no se ha entendido como el número entero 0 para la comparación, el código anterior nunca se imprime "Fue cero!". El programador debería asumir la responsabilidad de hacer malabares con el tipo del valor que sale de la base de datos. Esto no es deseable para un lenguaje poco tipado.

La igualdad estricta, incluido el tipo, se prueba con el operador "Es realmente, verdaderamente, honesto con Dios igual a", que se representa con el símbolo "===".

1

No veo cómo ('a' == 0) es útil

$var = '123abc'; 

if (123 == $var) 
{ 
    echo 'Whoda thunk it?'; 
} 

Todo se reduce a reglas de conversión implícitas de PHP.

Estoy fallando al pensar en un ejemplo práctico, pero esa es la razón básica por la que está viendo ese comportamiento.

de expansión:

En su ejemplo, 'a' se convierte en 0 (cero), para la comparación. Imagine que para el propósito de la comparación, es equivalente a '0a'. (Ese es el número cero, no la letra 'O'.)

La extensión adicional:

pensé que era un buen caso de este ejemplo, el uso en el manual, pero no pudieron encontrarlo. Lo que encontré debería ayudar a arrojar algo de luz sobre esta situación "ilógica".

PHP es ante todo un lenguaje para la web , no un lenguaje de programación de propósito general . Dado que la Web es no tipada y todo es una cadena, tuve que hacer las cosas ligeramente de manera diferente desde el principio para hacer que PHP hiciera lo que esperaban las personas. Específicamente, "123" == 123 debe ser verdadero en el orden para no tener que escribir cada entrada de usuario numérico.

http://bugs.php.net/bug.php?id=48012

que no responde exactamente a la pregunta, pero apunta en la dirección general.

1

PHP es un lenguaje vagamente tipado, y le permite comparar valores de diferentes tipos sin cometer errores, lo que lo hace muy fácil de usar, pero como ha encontrado puede causar algunas salidas raras pero lógicas.

Su primer ejemplo:

if(0=='a'){ 
    print ord(0)." should NEVER equal ".ord('a')."<br>"; 
} 

Cuando se comparan dos tipos diferentes de valores, un valor es convertido primero en el mismo tipo como otro a través de un molde y luego se comparan. En el ejemplo de Int y String, la cadena se convierte en Int. Cuando PHP convierte una letra en una cadena toma todos los primeros caracteres numéricos y luego corta el resto: es decir, '123123afraa' pasa a 123123, '9a9' se convierte en 9. Si la cadena no comienza con números, se le da el valor de 0

Por lo tanto, su ejemplo es realmente: 0 === (cadena) 'a' que es realmente 0 === 0 ya que 'a' no comienza con un número. ¡Creo que esperabas que PHP devolviera el valor de 'a' en ASCII que no lo hace! Esto es realmente útil para desinfectar cadenas, php raramente necesita lidiar con valores ascii, es demasiado alto para eso. (¡Es para hacer sitios web!)

Cuando una cadena se compara con un valor booleano '' o '0' son falsos, todos los demás valores son verdaderos. Esto es útil para que pueda comprobar si un valor es 'vacío':

url http://domain.com/?foo=

if ($ _GET [ 'foo'])) { // hacer algo}

Cuando un entero se compara con un booleano, los valores de 0 son falsos, otros valores son verdaderos, esto es bastante estándar.

Por lo tanto, en general, debe comprender lo que sucede cuando se comparan diferentes tipos de variables con el operador ==. También es probable que sea sabio darse cuenta de que == casi nunca es lo que quiere y usar === (que no encasillará sus valores) es MUCHO más seguro.

0

El código parece emanar de una prueba de unidad con el fin de detectar fallas, de ahí las comparaciones aparentemente extrañas. De la misma manera, puede ser preparatorio para la prueba de la unidad principal para confirmar que el operador == está funcionando correctamente, como debería.

Cuestiones relacionadas