Четвертого апреля на 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