Почему PHP считает 0 равным строке? - PullRequest
97 голосов
/ 27 июля 2011

У меня есть следующий фрагмент кода:

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

Он предназначен для инициализации цены товара до 0, а затем получения информации о нем. Если цена указана как 'e', ​​это означает обмен вместо продажи, который хранится в базе данных как отрицательное число.

Существует также возможность оставить цену равной 0, либо потому, что товар является бонусом, либо потому, что цена будет установлена ​​позднее.

Но всякий раз, когда цена не установлена, что оставляет ее с начальным значением 0, цикл if, указанный выше, оценивается как истина, а цена устанавливается в -1. То есть он считает 0 равным 'e'.

Как это можно объяснить?

Изменить: Когда цена указана как 0 (после инициализации), поведение ошибочно: иногда if оценивается как true, иногда - как false.

Ответы [ 8 ]

97 голосов
/ 27 июля 2011

вы делаете ==, который сортирует типы для вас.

0 - это int, поэтому в этом случае он будет приводить 'e' к int. Который не разбирается как единое целое и станет 0. Строка '0e' станет 0 и будет соответствовать!

использовать ===

45 голосов
/ 29 декабря 2011

Это связано с тем, как PHP выполняет операцию сравнения, которую == оператор сравнения обозначает:

Если вы сравниваете число со строкой или сравнение включает числовые строки, то каждая строка преобразуется в число, и сравнение выполняется численно. […] Преобразование типов не происходит, когда сравнение составляет === или !==, поскольку это включает сравнение как типа, так и значения.

Поскольку первый операнд - это число (0), а второй - строка ('e'), строка также преобразуется в число (см. Также таблица Сравнение с различными типами ). Страница руководства по строковому типу данных определила, как преобразование строки в число выполняется:

Когда строка оценивается в числовом контексте, результирующее значение и тип определяются следующим образом.

Если строка не содержит символов «.», «e» или «E» и числовое значение соответствует ограничениям целочисленного типа (как определено PHP_INT_MAX), Строка будет оценена как целое число. Во всех остальных случаях он будет оцениваться как число с плавающей запятой.

В этом случае строка будет 'e' и, таким образом, будет оцениваться как число с плавающей запятой:

Значение задается начальной частью строки. Если строка начинается с допустимых числовых данных, это будет используемое значение. В противном случае значение будет 0 (ноль). Допустимые числовые данные - это необязательный знак, за которым следуют одна или несколько цифр (необязательно содержащих десятичную точку), за которыми следует необязательный показатель степени. Экспонента представляет собой «e» или «E», за которыми следуют одна или несколько цифр.

Поскольку 'e' не начинается с допустимых числовых данных, оно вычисляется как плавающее 0.

14 голосов
/ 21 февраля 2018
"ABC" == 0

оценивает true, потому что first "ABC" преобразуется в целое число и становится 0 , затем по сравнению с 0.

Это нечетное поведение языка PHP: обычно можно ожидать, что 0 будет преобразован в строку "0", а затем сравнен с "ABC" с результатом false. Возможно, это то, что происходит в других языках, таких как JavaScript, где слабое сравнение "ABC" == 0 оценивает false.

Выполнение строгого сравнения решает проблему:

"ABC" === 0

оценивает false.

Но , что если мне нужно сравнить числа как строки с числами?

"123" === 123

оценивает false, потому что левый и правый члены имеют разные типы.

Что на самом деле нужно, так это слабое сравнение без ловушек в PHP-жонглировании.

Решение состоит в том, чтобы явным образом продвигать термины в строку, а затем проводить сравнение (строгий или слабый больше не имеет значения).

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

есть

true

, а

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

есть

false


Применительно к исходному коду:

$item['price'] = 0;
/*code to get item information goes in here*/
if((string)$item['price'] == 'e') {
    $item['price'] = -1;
}
8 голосов
/ 29 марта 2013

Ваша проблема - оператор двойного равенства, который приведёт правый член к типу левого. Используйте строгий, если вы предпочитаете.

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

Давайте вернемся к вашему коду (скопировано выше). В этом случае в большинстве случаев $ item ['price'] является целым числом (за исключением случаев, когда, очевидно, оно равно e). Таким образом, по законам PHP, PHP будет приводить тип "e" к целому числу, что дает int(0). (Не верите мне? <?php $i="e"; echo (int)$i; ?>).

Чтобы легко избежать этого, используйте оператор тройного равенства (точное сравнение), который проверит тип и не будет неявно преобразовывать тип.

P.S: забавный факт PHP: a == b не означает, что b == a. Возьмите свой пример и переверните его: if ("e" == $item['price']) никогда не будет выполнено при условии, что $ item ['price'] всегда является целым числом.

8 голосов
/ 27 июля 2011

Оператор == будет пытаться сопоставить значения, даже если они имеют разные типы. Например:

'0' == 0 will be true

Если вам также нужно сравнение типов, используйте оператор ===:

'0' === 0 will be false
6 голосов
/ 26 апреля 2012

В PHP есть довольно удобный метод для проверки комбинации "0", "false", "off" как == false и "1", "on", "true" как == true, который часто пропускается , Это особенно полезно для разбора аргументов GET / POST:

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

Это не совсем относится к этому варианту использования, но, учитывая сходство и факт, этот результат поиска имеет тенденцию находить, когда задает вопрос о проверке (строка) «0» как ложного, я думал, что это поможет другим.

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

5 голосов
/ 27 июля 2011

Вы должны использовать === вместо ==, потому что обычный оператор не сравнивает типы. Вместо этого он попытается выполнить типизацию предметов.

Между тем === принимает во внимание тип предметов.

  • === означает «равно»,
  • == означает "ээээ .. вроде как"
1 голос
/ 15 февраля 2019

Я думаю, что это лучше всего показать на примерах, которые я сделал, но столкнулся с тем же странным поведением.Посмотрите мой тестовый пример, и, надеюсь, он поможет вам лучше понять поведение:

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

Надеюсь, это поможет.

...