Особенности логического сравнения в PHP

в 16:57, , рубрики: php, особенности, перевод, переводы, Песочница, метки: , ,

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

Источник

Поделиться

* - обязательные к заполнению поля