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

111

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 la forme d'un nombre négatif.

Il est également possible de laisser le prix à 0, soit parce que l'objet 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 ifboucle 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 cela peut-il être expliqué?

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. *

Sérgio Domingues
la source
1
J'ai trouvé que l'utilisation de triple === au lieu de double == donne le comportement attendu. Mais c'est toujours bizarre.
Sérgio Domingues
2
(référence) suffisamment expliqué dans le manuel PHP au chapitre Jonglerie de types et illustré dans le tableau de comparaison de types
Gordon
1
Si le seul type de chaîne possible est 'e', ​​ne pouvez-vous pas simplement faire une vérification is_string ($ item ["price"])? Ce serait un peu plus efficace que ===. [la citation nécessaire]
Jimmie Lin
dans les comparaisons faibles entre chaîne et entier, la chaîne est convertie en entier (au lieu que l'entier soit "promu" en chaîne). if((string)$item['price'] == 'e')corrige le comportement étrange. Voir stackoverflow.com/a/48912540/1579327 pour plus de détails
Paolo
Veuillez noter un autre cas dans les commentaires ci-dessous par @Paolo où 0 (entier) équivaut à toute autre chaîne lors de l'utilisation de l'opérateur double égal.
Haitham Sweilem

Réponses:

113

Vous faites ==ce qui trie les types pour vous.

0est un int, donc dans ce cas, il sera converti 'e'en un int. Ce qui n'est pas analysable en tant que tel et deviendra 0. Une chaîne '0e'deviendrait 0et correspondrait!

Utilisation ===

Nanne
la source
14
Un autre inconvénient de la comparaison lâche.
MC Emperor
5
Tricky one. Je viens de tomber sur celui-ci et j'ai été étonné de savoir pourquoi string == 0. Je dois m'en souvenir.
Grzegorz
2
Je me gratte la tête aussi quand je fais une boucle sur des clés de chaîne, mais le tableau avait un élément d'index initial «zéro» qui continuait à donner vrai sur la première comparaison de clé de chaîne. J'étais comme quoi? Comment dans le ... alors, bien sûr, cette réponse a clarifié cela! Je suis surpris que toute cette question n'ait pas une réponse acceptée. Cela montre simplement que certains poseurs de questions sont des imbéciles.
IncredibleHat
48

Cela est dû à la façon dont PHP effectue l'opération de comparaison que l' ==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 le second une chaîne ( 'e'), la chaîne est également convertie en nombre (voir aussi le tableau Comparaison avec divers types ). La page de manuel sur le type de données chaîne définit la façon dont la conversion 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 que 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 elle sera évaluée 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 commence pas avec des données numériques valides, il est évalué comme flottant 0.

Gombo
la source
3
php conçoit la plupart de tout pour être facilement comparable et ajoute ensuite quelques pièges juste pour gâcher notre journée. Cela ne correspond pas au reste de la philosophie de conception de PHP. A moins de tromperie y a-t-il de la philosophie ???
user3338098
1
d'autant plus que "e" passe à vrai et "" à faux
user3338098
20
"ABC" == 0

évalue trueparce que d' abord "ABC" est converti en entier et devient 0 ensuite comparé à 0.

C'est un comportement étrange du langage PHP: normalement, on s'attend 0à être promu en chaîne "0"puis comparé à "ABC"un résultat false. C'est peut-être ce qui se passe dans d'autres langages comme JavaScript, où la comparaison faible est "ABC" == 0évaluée false.

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

"ABC" === 0

évalue false.

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

"123" === 123

évalue falseparce que les termes gauche et droit sont de type différent.

Ce qui est réellement nécessaire, c'est une comparaison faible sans les pièges de la jonglerie de type PHP.

La solution est de promouvoir explicitement les termes en chaîne, puis de 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;
}
Paolo
la source
9

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 type, utilisez l'opérateur ===:

'0' === 0 will be false
Vincent Mimoun-Prat
la source
9

Votre problème est l'opérateur double égal, qui transtypera le membre de droite dans le 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 sera converti "e"en nombre entier, ce qui donne 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 implicitement typé.

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

Sébastien Renauld
la source
7

Il existe une méthode assez pratique en PHP pour valider un mélange de «0», «false», «off» comme == false et «1», «on», «true» comme == true, ce qui est souvent négligé. C'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, la recherche a tendance à trouver en posant 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

Jim Morrison
la source
6

Vous devez utiliser à la ===place 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 .. un peu ressemble à"
tereško
la source
1
Je vois. Cela fonctionne maintenant (avec un typage):if((string)$item['price']=='e'){ $item['price'] = -1; }
Sérgio Domingues
mais vous ne devriez pas faire ça. il suffit d'utiliser l' ===opérateur
tereško
3

Je pense qu'il est préférable de montrer par des exemples que j'ai faits, tout en rencontrant le même comportement étrange. Voir mon cas de test et j'espère qu'il 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). 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
la source
Excellent test, j'ai fait la même chose mais j'ai fait une belle table à partir de. voir ma réponse
IAMTHEBEST
0

En gros, utilisez toujours l' ===opérateur pour garantir la sécurité du type.

Entrez la description de l'image ici

Entrez la description de l'image ici

JE SUIS LE MEILLEUR
la source