Четвертого апреля на stackoverflow появился вопрос, касающийся работы операторов сравнения в PHP. Почти сразу же на него поступил развернутый ответ. Наверняка для многих это является интересной темой.
Вопрос
PHP славится своим приведением типов. Я потратил много времени в поисках основ логики сравнения в нем.
Например: если $a > $b является истиной и $b > $c является истиной, значит ли это, что $a > $c также является истиной?
Руководствуясь простейшей логикой я могу предположить что это выражение также верно, однако я не очень доверяю PHP в этом в вопросе. Может кто-нибудь привести мне пример, в котором данное утверждение будет ложным?
Также мне интересна работа операторов «больше» и «меньше». Изменится ли результат сравнения при переворачивании выражения:
# precondition:
if ($a === $b) {
throw new Exception(
'both are strictly equal, can not compare strictly for greater or smaller'
);
}
($a > $b) !== ($b > $a)
Для большинства комбинаций типов работа операторов сравнения больше/меньше не документирована.
Ответ
Оператор сравнения в PHP в некоторых моментах отличается от канонического определения:
Отношение равенства должно быть рефлексивным, симметричным и транзитивным:
- Оператор
==в PHP не рефлексивен, т. е.$a == $aне всегда является истиной:var_dump(NAN == NAN); // bool(false)Примечание: То, что сравнение с участием NAN всегда является ложным не является особенностью PHP. Это поведение определено в стандарте IEEE 754 формата представления чисел с плавающей точкой (пояснения на stackoverflow);
- Оператор
==симметричен, т. е.$a == $bи$b == $aвсегда равны; - Оператор
==не транзитивен, т. е.$a == $bи$b == $cне означает, что$a == $c:var_dump(true == "a"); // bool(true) var_dump("a" == 0); // bool(true) var_dump(true == 0); // bool(false)
Отношение <=/>= должно быть не рефлексивным, антисимметричным и транзитивным:
- Оператор
<=в PHP не рефлексивен, т. е. выражение$a <= $aне всегда является истинным (см. пример для==); - Оператор
<=не антисимметричен, т. е. истинность выражений$a <= $bи$b <= $aне означает, что$a == $b:var_dump(NAN <= "foo"); // bool(true) var_dump("foo" <= NAN); // bool(true) var_dump(NAN == "foo"); // bool(false) - Оператор
<=не транзитивен, т. е. истинность выражений$a <= $bи$b <= $cне означает, что$a <= $c(пример такой же, как и для оператора==). - Оператор
<=не является полным, т. е. и$a <= $b, и$b <= $aмогут быть ложными:var_dump(new stdClass <= new DateTime); // bool(false) var_dump(new DateTime <= new stdClass); // bool(false)
Отношение строгого неравенства </> должно быть антирефлексивным, асимметричным и транзитивным:
- Оператор
<в PHP антирефлексивен, т. е.$a < $aвсегда является ложным. Это актуально начиная с версии PHP 5.4. В предыдущих версияхINF < INFявляется истинным; - Оператор
<не асимметричен, т. е. истинность выражения$a < $bне означает, что!($b < $a)является истинным (см. пример для<=); - Оператор
<не транзитивен, т. е. из истинности$a < $bи$b < $cне следует, что$a < $cтакже является истиной:var_dump(-INF < 0); // bool(true) var_dump(0 < TRUE); // bool(true) var_dump(-INF < TRUE); // bool(false) - Дополнительно: Оператор
<не трихотомичен, т. е. выражения$a < $b,$b < $aи$a == $bмогут быть ложными (пример такой же, как и для<=); - Дополнительно: Оператор
<может быть закольцованным, т. е. бывают случаи, когда$a < $b,$b < $cи$c < $aявляются истинными:var_dump(INF < []); // bool(true) var_dump([] < new stdClass); // bool(true) var_dump(new stdClass < INF); // bool(true)Примечание: Этот пример генерирует предупреждение «Object of class stdClass could not be converted to double» уровня notice.
Вы можете найти несколько восхитительных графиков в статье PHP Sadness 52 — Операторы сравнения.
И в конце я хочу отметить, что два равенства в PHP гарантированы (в отличии от почти всего остального) потому что интерпретатор приводит их к одному виду:
($a > $b) == ($b < $a)
($a >= $b) == ($b <= $a)
Автор: CultHero
