Чому PHP вважає 0 рівним рядку?


111

У мене є такий код:

$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 == дає очікувану поведінку. Але це все одно дивно.
Sérgio Domingues

2
(посилання) достатньо пояснено в Посібнику з PHP у розділі Жунглінг типу та проілюстровано у Таблиці порівняння типів
Гордон,

1
Якщо єдиний можливий тип рядка - "e", чи не можете ви просто перейти на перевірку is_string ($ item ["price"])? Це було б трохи ефективніше, ніж ===. [потрібна цитата]
Джиммі Лін

при слабких порівняннях між рядком і цілим числом рядок перетворюється на ціле число (замість того, щоб ціле число "просувалося" в рядок). if((string)$item['price'] == 'e')фіксує дивну поведінку. Дивіться stackoverflow.com/a/48912540/1579327 для більш детальної інформації
Paolo

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

Відповіді:


113

Ви робите, ==які сортують типи для вас.

0є int, тому в цьому випадку він буде передаватися 'e'до int. Який не проаналізується як один і стане 0. Рядок '0e'став 0би і відповідав би!

Використовуйте ===


14
Ще один недолік в'ялого порівняння.
MC Імператор

5
Хитрий один. Щойно наткнувся на цей і дивувався, чому рядок == 0. Треба пам’ятати це.
Гжегож

2
Був чухати мою голову і на цьому, коли я лупив на рядових клавішах, але в масиві був початковий елемент "нуль" індексу, який зберігав, що призводить до істинного в порівнянні першого строкового ключа. Я був як що? Як у ... так, напевне, ця відповідь прояснила це! Я здивований, на все це запитання немає жодної прийнятої відповіді. Просто іде показати, що запитуючі запитання - це ривки.
IncredibleHat

48

Це пов'язано з тим, як PHP виконує операцію ==порівняння, яку позначає оператор порівняння :

Якщо ви порівнюєте число з рядком або порівняння включає чисельні рядки, то кожна рядок перетворюється в число, а порівняння виконується числовим числом. […] Перетворення типів не відбувається, коли порівнюється ===або, !==як це передбачає порівняння типу, а також значення.

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

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

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

У цьому випадку рядок є, 'e'і, таким чином, вона буде оцінюватися як поплавок:

Значення задається початковою частиною рядка. Якщо рядок починається з дійсних числових даних, це буде використовуване значення. В іншому випадку значення буде 0(нуль). Дійсні числові дані - необов'язковий знак, за яким слідує одна чи більше цифр (необов'язково містить десяткову точку), за якими слідує необов'язковий показник. Експонент - це ' e' або ' E', за яким слідує одна або кілька цифр.

Оскільки 'e'не починається з дійсних числових даних, він оцінює плавання 0.


3
php проектує більшість всього, щоб це було легко порівняти, а потім кидає по кілька готчей, аби зруйнувати наш день. Це не відповідає решті філософії дизайну PHP. Якщо тільки обман є філософія ???
користувач3338098

1
тим більше, що "e"
передає

20
"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;
}

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.