web-dev-qa-db-fra.com

Pourquoi PHP considère-t-il que 0 est égal à une chaîne?

J'ai le morceau de code suivant:

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

Il est destiné à initialiser le prix de l'article à 0, puis à obtenir des informations à ce sujet. Si le prix est indiqué par "e", cela signifie un échange au lieu d'une vente, qui est stocké dans une base de données sous forme de nombre négatif.

Il est également possible de laisser le prix à 0, soit parce que l'article est un bonus, soit parce que le prix sera fixé ultérieurement.

Mais, chaque fois que le prix n'est pas défini, ce qui lui laisse la valeur initiale de 0, la boucle if indiquée ci-dessus est évaluée comme vraie et le prix est défini sur -1. Autrement dit, il considère 0 comme égal à "e".

Comment peut-on l'expliquer?

Modifier: lorsque le prix est fourni à 0 (après l'initialisation), le comportement est erratique: parfois le if est évalué comme vrai, parfois il est évalué comme faux.

101
Sérgio Domingues

tu es en train de faire == qui trie les types pour vous.

0 est un entier, dans ce cas, il va transtyper 'e' à un int. Ce qui n'est pas analysable comme un, et deviendra 0. Un string '0e' deviendrait 0, et correspondrait!

utilisation ===

101
Nanne

Cela est dû à la façon dont PHP effectue l'opération de comparaison que le == opérateur de comparaison désigne:

Si vous comparez un nombre avec une chaîne ou si la comparaison implique des chaînes numériques, chaque chaîne est convertie en nombre et la comparaison est effectuée numériquement. […] La conversion de type n'a pas lieu lorsque la comparaison est === ou !== car cela implique de comparer le type ainsi que la valeur.

Comme le premier opérande est un nombre (0) et la seconde est une chaîne ('e'), la chaîne est également convertie en nombre (voir aussi tableau Comparaison avec différents types ). La page de manuel sur le type de données de chaîne a défini comment la conversion de chaîne en nombre est effectuée:

Lorsqu'une chaîne est évaluée dans un contexte numérique, la valeur et le type résultants sont déterminés comme suit.

Si la chaîne ne contient aucun des caractères '. ',' e 'ou' E 'et la valeur numérique correspond aux limites de type entier (telles que définies par PHP_INT_MAX), la chaîne sera évaluée comme un entier. Dans tous les autres cas, il sera évalué comme un flottant.

Dans ce cas, la chaîne est 'e' et donc il sera évalué comme un flottant:

La valeur est donnée par la partie initiale de la chaîne. Si la chaîne commence par des données numériques valides, ce sera la valeur utilisée. Sinon, la valeur sera 0 (zéro). Les données numériques valides sont un signe facultatif, suivi d'un ou plusieurs chiffres (contenant éventuellement un point décimal), suivi d'un exposant facultatif. L'exposant est un 'e' ou 'E' suivi d'un ou plusieurs chiffres.

Comme 'e' ne démarre pas avec des données numériques valides, il évalue à flotter 0.

46
Gumbo
"ABC" == 0

évalue true parce que premier"ABC" est converti en entier et devient 0alors il est comparé à 0.

Il s'agit d'un comportement impair du langage PHP: normalement, on peut s'attendre à ce que 0 à promouvoir en chaîne "0" puis comparé à "ABC" avec un résultat false. C'est peut-être ce qui se passe dans d'autres langages comme JavaScript où la faible comparaison "ABC" == 0 évalue false.

Faire une comparaison stricte résout le problème:

"ABC" === 0

évalue false.

Mais et si j'ai besoin de comparer des nombres sous forme de chaînes avec des nombres?

"123" === 123

évalue false car les termes gauche et droit sont de type différent.

Ce qui est réellement nécessaire est une comparaison faible sans les pièges du jonglage de type PHP).

La solution consiste à promouvoir explicitement les termes en chaîne, puis à faire une comparaison (strict ou faible n'a plus d'importance).

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

est

true

tandis que

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

est

false


Appliqué au code d'origine:

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

Votre problème est l'opérateur double égal, qui transtypera le membre droit en type de gauche. Utilisez strict si vous préférez.

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

Revenons à votre code (copié ci-dessus). Dans ce cas, dans la plupart des cas, $ item ['price'] est un entier (sauf quand il est égal à e, évidemment). En tant que tel, selon les lois de PHP, PHP transtypera "e" En entier, ce qui donnera int(0). (Ne me croyez pas? <?php $i="e"; echo (int)$i; ?>).

Pour vous en sortir facilement, utilisez l'opérateur triple égal (comparaison exacte), qui vérifiera le type et ne sera pas transtypé implicitement.

PS: a PHP fait amusant: a == b N'implique pas que b == a. Prenez votre exemple et inversez-le: if ("e" == $item['price']) ne sera jamais réellement être remplie à condition que $ item ['price'] soit toujours un entier.

8
Sébastien Renauld

L'opérateur == essaiera de faire correspondre les valeurs même si elles sont de types différents. Par exemple:

'0' == 0 will be true

Si vous avez également besoin d'une comparaison de types, utilisez l'opérateur ===:

'0' === 0 will be false
8
Vincent Mimoun-Prat

Il y a une méthode plutôt pratique dans PHP pour valider un mélange de "0", "false", "off" as == false et "1", "on", "true" as = = true qui est souvent ignoré. Il est particulièrement utile pour analyser les arguments GET/POST:

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

Ce n'est pas tout à fait pertinent pour ce cas d'utilisation, mais étant donné la similitude et le fait que c'est le résultat que la recherche a tendance à trouver lorsque je pose la question de valider (chaîne) "0" comme faux, je pensais que cela aiderait les autres.

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

6
Jim Morrison

Tu devrais utiliser === au lieu de ==, car l'opérateur ordinaire ne compare pas les types. Au lieu de cela, il tentera de transtyper les éléments.

Pendant ce temps, le === prend en considération le type d'éléments.

  • === signifie "égal",
  • == signifie "eeeeh .. ressemble un peu"
5
tereško

Je pense qu'il est préférable de montrer par des exemples que j'ai fait, tout en ayant le même comportement étrange. Voir mon cas de test et j'espère que cela vous aidera à mieux comprendre le comportement:

//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). 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 its true)
echo ("bla" === "blaa"); //false because the values are not the same. The type is the same but === check 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 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)

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

J'espère que ça aide.

1
Kosem