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

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

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

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

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

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

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

Когда цена указана как 0 (после инициализации), поведение неустойчиво: иногда if оценивается как true, иногда как false.*


person Sérgio Domingues    schedule 27.07.2011    source источник
comment
Я обнаружил, что использование тройного === вместо двойного == дает ожидаемое поведение. Но все равно странно.   -  person Sérgio Domingues    schedule 27.07.2011
comment
(ссылка) достаточно объяснено в Руководстве по PHP в главе Жонглирование типами и проиллюстрировано в Таблице сравнения типов   -  person Gordon    schedule 27.07.2011
comment
Если единственным возможным типом строки является 'e', ​​разве вы не можете просто проверить is_string($item[price])? Это было бы немного эффективнее, чем ===. [нужна цитата]   -  person Jimmie Lin    schedule 27.07.2011
comment
при слабых сравнениях между строкой и целым числом строка преобразуется в целое число (вместо преобразования целого числа в строку). if((string)$item['price'] == 'e') исправляет странное поведение. См. stackoverflow.com/a/48912540/1579327 для более подробной информации.   -  person Paolo    schedule 21.02.2018
comment
Обратите внимание на другой случай в комментариях @Paolo ниже, где 0 (целое число) равно любой другой строке при использовании оператора двойного равенства.   -  person Haitham Sweilem    schedule 24.06.2018
comment
В PHP‹8, когда строка сравнивается с числом, строка автоматически преобразуется в число, которое, если оно не числовое, становится равным 0. Однако, начиная с PHP 8, это уже не так из-за странного поведения, которое вы описываете. В PHP 8 0 преобразуется в строку, и сравнение не равно.   -  person PHP Guru    schedule 29.11.2020


Ответы (9)


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

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

Используйте ===

Из PHP.net:

Сравнение между строками и числами с использованием == и других операторов нестрогого сравнения в настоящее время работает путем приведения строки к числу и последующего сравнения целых чисел или чисел с плавающей запятой. Это приводит к множеству удивительных результатов сравнения, наиболее заметным из которых является то, что 0 == foobar возвращает true.

Однако это поведение было изменено в PHP 8.0:

При сравнении с числовой строкой PHP 8 использует сравнение чисел. В противном случае он преобразует число в строку и использует сравнение строк.

PHP 7

0 == 'foobar' // true
0 == '' // true
4 == '4e' // true (4e is cast as a number and becomes 4)

PHP 8 преобразует числа в строки перед сравнением

0 == 'foobar' // false
0 == '' // false
4 == '4e' // false ('4e' is considered non-numeric therefore 4 is cast as a string and becomes '4')

Это серьезное изменение, поэтому оно было реализовано в новой основной версии PHP. Это изменение нарушает обратную совместимость в сценариях, зависящих от старого поведения.

person Nanne    schedule 27.07.2011
comment
Еще один недостаток вольного сравнения. - person MC Emperor; 05.10.2014
comment
Сложный. Только что наткнулся на это и был поражен, почему string == 0. Должен запомнить это. - person Grzegorz; 28.09.2016
comment
Я тоже ломал голову над этим, когда зацикливался на строковых ключах, но в массиве был начальный «нулевой» элемент индекса, который продолжал возвращать значение true при первом сравнении строковых ключей. Я был как что? Как в... так что, конечно же, этот ответ прояснил это! Я удивлен, что на весь этот вопрос нет ОДНОГО принятого ответа. Просто показывает, что некоторые задающие вопросы - придурки. - person IncredibleHat; 24.03.2018

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

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

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

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

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

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

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

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

person Gumbo    schedule 29.12.2011
comment
php проектирует почти все так, чтобы его можно было легко сравнивать, а затем добавляет несколько ошибок, чтобы испортить нам день. Это не согласуется с остальной философией дизайна PHP. Разве обман есть философия??? - person user3338098; 30.07.2015
comment
тем более, что e приводит к истине и приводит к ложным - person user3338098; 20.08.2015

"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

is

true

пока

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

is

false


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

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

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

'0' == 0 will be true

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

'0' === 0 will be false
person Vincent Mimoun-Prat    schedule 27.07.2011

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

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'] всегда является целым числом.

person Sébastien Renauld    schedule 29.03.2013

В 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

person Jim Morrison    schedule 26.04.2012

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

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

  • === означает "равно",
  • == означает "ээээ... вроде похоже"
person tereško    schedule 27.07.2011
comment
Я понимаю. Теперь это работает (с приведением типов): if((string)$item['price']=='e'){ $item['price'] = -1; } - person Sérgio Domingues; 27.07.2011
comment
но вы не должны этого делать. просто используйте оператор === - person tereško; 27.07.2011

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

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

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

person Community    schedule 25.03.2020

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

// 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)
person Kosem    schedule 15.02.2019
comment
Отличный тест, я сделал то же самое, но сделал из него красивую таблицу. посмотри мой ответ - person ; 29.05.2020