Почему PHP считает 0 равным строке?


112

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

$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 оценивается как истина, иногда как ложь. *


1
Я обнаружил, что использование triple === вместо double == дает ожидаемое поведение. Но все равно странно.
Серхио Домингес

2
(ссылка) достаточно подробно объяснено в Руководстве по PHP в главе « Подбор типов» и проиллюстрировано в таблице сравнения типов
Гордон

1
Если единственным возможным типом строки является 'e', ​​не могли бы вы просто пройти проверку is_string ($ item ["price"])? Это было бы немного эффективнее, чем ===. [необходима цитата]
Джимми Лин

при слабом сравнении между строкой и целым числом строка преобразуется в целое число (вместо того, чтобы целое число «повышалось» до строки). if((string)$item['price'] == 'e')исправляет странное поведение. См. Stackoverflow.com/a/48912540/1579327 для получения более подробной информации
Паоло

Обратите внимание на другой случай в комментариях @Paolo ниже, где 0 (целое число) равно любой другой строке при использовании оператора двойного равенства.
Haitham Sweilem

Ответы:


114

Вы делаете то, ==что сортируете для вас.

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

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


14
Еще один недостаток вольного сравнения.
MC Emperor

5
Хитрый. Просто наткнулся на это и был поражен, почему string == 0. Надо это запомнить.
Grzegorz

2
Я тоже почесал голову над этим, когда перебирал строковые ключи, но у массива был начальный «нулевой» элемент индекса, который продолжал приводить к true при первом сравнении строковых ключей. Я был похож на что? Как в ... итак, этот ответ прояснил это! Я удивлен, что на весь этот вопрос нет ОДНОГО принятого ответа. Просто показывает, что некоторые задающие вопросы придурки.
IncredibleHat

48

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

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

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

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

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

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

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

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


3
php разрабатывает почти все, чтобы их было легко сопоставить, а затем добавляет несколько ошибок, чтобы испортить наш день. Это не согласуется с остальной философией дизайна PHP. Разве обман есть философия ???
user3338098

1
тем более, что "e" принимает значение true, а "" - false
user3338098

21
"ABC" == 0

вычисляет, trueпотому что сначала "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;
}

этот метод намного лучше! спасибо
Pooya Behravesh

9

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

'0' == 0 will be true

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

'0' === 0 will be false

9

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

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

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

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

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


7

В 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


6

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

При этом ===учитывается тип предметов.

  • === означает "равно",
  • == означает "ээээ ... вроде как"

1
Понимаю. Теперь это работает (с приведением типа):if((string)$item['price']=='e'){ $item['price'] = -1; }
Серхио Домингес

но вы не должны этого делать. просто воспользуйтесь ===оператором
tereško

3

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

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

Отличный тест, я сделал то же самое, но сделал красивую таблицу из. см. мой ответ
IAMTHEBEST

0

По сути, всегда используйте ===оператор, чтобы гарантировать безопасность типа.

Введите описание изображения здесь

Введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.