Очень нелогичные сравнения значений php

Я наткнулся на очень странный бит PHP-кода. Может ли кто-нибудь объяснить, почему это происходит? ***** BONUS POINTS *****, если вы можете сказать, почему это полезно.

<?php if(0=='a'){ print ord(0)." should NEVER equal ".ord('a')."<br>"; } if(false==0){ print "false==0<br>"; } if('a'==false){ print "a==false<br>"; } ?> 

И результат:

 48 should NEVER equal 97 false==0 

В PHP «a» не является символом ASCII a, а строкой a. В числовом контексте оно равно 0. Например, intval('a') приводит к значению 0 .

Это полезно, потому что PHP в основном используется для обработки текста, и можно попробовать тест (123 == '123'), который является истинным. И учитывая, что число в одиночных (или двойных) кавычках рассматривается как число, это не имеет смысла для строки без числового значения, которое должно рассматриваться как что-либо, кроме 0.

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

Ну, для этого всегда есть чит-лист типа PHP !

Это основной принцип слабо / динамически типизированных языков, называемых жонглированием типов . В определенных обстоятельствах типы будут передаваться другим типам. Когда вы сравниваете строку с номером, строка будет передана в число. При сравнении чего-либо с логическим значением это значение будет передано в логическое значение.

Существуют правила для каждого типа о том, как он будет преобразовываться в другой тип или как он сравнивается с другими типами. 'a' преобразуется в 0 при нажатии на число (на самом деле это единственный логический выбор). Чтобы избежать этого типа, проверяйте не с помощью оператора равенства == , а с помощью оператора идентификации === .

Как отметил Джеймс , это полезно, поскольку PHP очень много использует строки, которые действительно являются числами. Например, HTML-формы представляют только строки, даже если это число. Он также позволяет использовать некоторые действительно краткий код, например:

 $result = someOperation(); if (!$result) { // $result may be null, false, 0, '' or array(), // all of which we're not interested in error(); } 

Это также означает, что вы должны быть очень осторожны в том, что нужно проверить, в каких обстоятельствах, хотя значение может неожиданно привести к чему-то другому. И, по общему признанию, 'a' == 0 сам по себе действительно является ловушкой типа жонглирования, а не полезным. Это одна из ситуаций, когда вам нужно быть осторожными и проверять, как if (is_numeric($var) && $var == 0) .

ord() принимает символы, поэтому PHP превращает 0 в '0' . И 0 равно false , хотя он не идентичен ( === ).

Ознакомьтесь с таблицами сравнения типов PHP из руководства. Очень удобно иметь под рукой, пока вы не усвоили ее, и было бесценно для моего понимания того, что будет оцениваться как истинное и когда.

Другие уже ответили на суть вопроса, но я думаю, что важно указать, что в PHP единственная непустая строка, которая не оценивает значение «true» с оператором ==, равна «0», поскольку PHP обрабатывает любую строку, содержащую только числа как целое число или float.

Обоснованием этого является то, что PHP довольно слабо набирается и пытается разрешить взаимозаменяемость целых чисел, строк, float и логических значений. Реальный и чрезвычайно распространенный пример этого – если вы используете функции mysql или PDO, строки возвращаются для всего , даже если базовый столбец является целым числом.

Рассмотрим следующий sql:

 CREATE TABLE `test`.`pants` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , `some_other_int` INT NOT NULL ) ENGINE = InnoDB; INSERT INTO `test`.`pants` (`id`, `some_other_int`) VALUES ('1', '1'), ('2', '0'); 

И следующий код:

 <?php $c = mysql_connect('127.0.0.1', 'user', 'password'); mysql_select_db('test', $c); $r = mysql_query('SELECT * FROM pants', $c); while ($row = mysql_fetch_assoc($r)) { var_dump($row); foreach($row as $k=>$v) { if (!is_string($v)) echo "field {$v} was not a string!\n"; } } 

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

 <?php $id = 2; $r = mysql_query(sprintf('SELECT * FROM pants WHERE id=%s', mysql_real_esacpe_string($id)), $c); $row = mysql_fetch_assoc($r); // this is the important bit if (0 == $row['some_other_int']) { echo "It was zero!"; } 

Если строка «0» не рассматривалась как целое число 0 для сравнения, вышеуказанный код никогда не печатал бы «Это было ноль!». Программист должен будет взять на себя ответственность за жонглирование типа значения, которое выходит из базы данных. Это нежелательно для слабо типизированного языка.

Строгое равенство, включая тип, проверяется с помощью оператора «Действительно, действительно, честного с богом, равным», который представлен символом «===».

Я не вижу, как ('a' == 0) полезно

 $var = '123abc'; if (123 == $var) { echo 'Whoda thunk it?'; } 

Это сводится к правилам неявного преобразования PHP.

Я не думаю о практическом примере, но это основная причина, по которой вы видите это поведение.

Расширение:

В вашем примере «a» преобразуется в 0 (ноль) для сравнения. Представьте, что для целей сравнения это эквивалентно '0a'. (Это нулевая цифра, а не буква «o»).

Дальнейшее расширение:

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

PHP – это, прежде всего, веб-язык, а не язык сценариев общего назначения. Поскольку Web не печатается, и все это строка, мне пришлось сделать что-то немного по-другому на раннем этапе, чтобы заставить PHP делать то, что ожидали люди. В частности, «123» == 123 должно быть истинным, чтобы не было необходимости вводить буквенные обозначения каждого отдельного пользовательского ввода.

http://bugs.php.net/bug.php?id=48012

Это точно не отвечает на вопрос, но указывает на общее направление.

PHP – это свободно типизированный язык и позволяет сравнивать значения разных типов без ошибок, что делает его очень простым в использовании, но, как вы нашли, может вызвать некоторые странные, но логические выходы.

Ваш первый пример:

 if(0=='a'){ print ord(0)." should NEVER equal ".ord('a')."<br>"; } 

Когда сравниваются два разных типа значений, одно значение сначала преобразуется в тот же тип, что и другой, через литой, а затем сравнивается. В примере Int и String строка преобразуется в Int. Когда PHP превращает букву в строку, она принимает все первые числовые символы, а затем отбивные остальных: т.е. «123123afraa» становится 123123, «9a9» равно 9. Если строка не начинается с цифр, ей присваивается значение 0 ,

Поэтому ваш пример действительно: 0 === (string) 'a', который действительно 0 === 0, так как 'a' не начинается с числового. Я думаю, вы ожидали, что PHP вернет значение «a» в ASCII, которого нет! Это действительно полезно для дезинфекции строк, php редко требует рассмотрения значений ascii, для этого это слишком высокий уровень. (Для создания сайтов!)

Когда строка сравнивается с логическим значением, значение '' или '0' ложно, все остальные значения являются истинными. Это полезно, поэтому вы можете проверить, является ли значение «пустым»:

url http://domain.com/?foo=

if ($ _GET ['foo'])) {// что-то делать}

Когда целое число сравнивается с логическим значением, значение 0 равно false, другие значения истинны, это довольно стандартно.

Итак, в целом вам нужно понять, что происходит, когда разные типы переменных сравниваются с оператором ==. Также, вероятно, разумно осознавать, что == почти никогда не то, что вы хотите, и использование === (которое не будет выводить ваши значения) ALOT безопаснее.

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