## Операторы сравнения

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

Операторы сравнения
Пример | Название | Результат
--- | --- | ---
$a == $b | Равно | Возвращается `true`, если значение переменной $a после преобразования типов равно значению переменной $b.
$a === $b | Тождественно равно | Возвращается `true`, если значение переменной $a равно значению переменной $b и имеет тот же тип.
$a != $b | Не равно | Возвращается `true`, если значение переменной $a после преобразования типов не равно значению переменной $b.
$a <> $b | Не равно | Возвращается `true`, если значение переменной $a после преобразования типов не равно значению переменной $b.
$a !== $b | Тождественно не равно | Возвращается `true`, если значение переменной $a не равно значению переменной $b или они разных типов.
$a < $b | Меньше | Возвращается `true`, если значение переменной $a строго меньше значения переменной $b.
$a > $b | Больше | Возвращается `true`, если значение переменной $a строго больше значения переменной $b.
$a <= $b | Меньше или равно | Возвращается `true`, если значение переменной $a меньше или равно значению переменной $b.
$a >= $b | Больше или равно | Возвращается `true`, если значение переменной $a больше или равно значению переменной $b.
$a <=> $b | Космический корабль (spaceship) | Целое число (int) меньше, больше или равное нулю, когда значение переменной $a меньше, больше или равно значению переменной $b.

Сравнение выполняется численно, если оба операнда — числовые строки, или один операнд — число, а другой — числовая строка. Эти правила также справедливы для оператора switch. Типы не преобразовываются при сравнениях вида `===` или `!==`, поскольку это включает сравнение значения и типа.

Внимание

До PHP 8.0.0, если строка (string) сравнивалась с числом или числовой строкой, то перед выполнением сравнения строка (string) преобразовывалась в число. Это иногда давало неожиданные результаты, как видно в следующем примере:

    <?php

    var_dump(0 == "a");
    var_dump("1" == "01");
    var_dump("10" == "1e1");
    var_dump(100 == "1e2");

    switch ("a") {
        case 0:
            echo "0";
            break;
        case "a":
            echo "a";
            break;
    }

    ?>

Результат выполнения приведённого примера в PHP 7:

    bool(true)
    bool(true)
    bool(true)
    bool(true)
    0

Результат выполнения приведённого примера в PHP 8:

    bool(false)
    bool(true)
    bool(true)
    bool(true)
    a

Пример #1 Операторы сравнения

    <?php

    // Целые числа
    echo 1 <=> 1, ' '; // 0
    echo 1 <=> 2, ' '; // -1
    echo 2 <=> 1, ' '; // 1

    // Числа с плавающей точкой
    echo 1.5 <=> 1.5, ' '; // 0
    echo 1.5 <=> 2.5, ' '; // -1
    echo 2.5 <=> 1.5, ' '; // 1

    // Строки
    echo "a" <=> "a", ' '; // 0
    echo "a" <=> "b", ' '; // -1
    echo "b" <=> "a", ' '; // 1

    echo "a" <=> "aa", ' ';  // -1
    echo "zz" <=> "aa", ' '; // 1

    // Массивы
    echo [] <=> [], ' ';               // 0
    echo [1, 2, 3] <=> [1, 2, 3], ' '; // 0
    echo [1, 2, 3] <=> [], ' ';        // 1
    echo [1, 2, 3] <=> [1, 2, 1], ' '; // 1
    echo [1, 2, 3] <=> [1, 2, 4], ' '; // -1

    // Объекты
    $a = (object) ["a" => "b"];
    $b = (object) ["a" => "b"];
    echo $a <=> $b, ' '; // 0

    $a = (object) ["a" => "b"];
    $b = (object) ["a" => "c"];
    echo $a <=> $b, ' '; // -1

    $a = (object) ["a" => "c"];
    $b = (object) ["a" => "b"];
    echo $a <=> $b, ' '; // 1

    // Сравниваются не только значения; ключи тоже должны совпадать
    $a = (object) ["a" => "b"];
    $b = (object) ["b" => "b"];
    echo $a <=> $b, ' '; // 1

    ?>

Для различных типов сравнение происходит в соответствии со следующей таблицей (по порядку).

Сравнение типов
Тип операнда 1 | Тип операнда 2 | Результат
--- | --- | ---
null или string | string | Значение `null` преобразовывается в пустую строку (""), числовое или лексическое сравнение
bool или null | значение любого типа | Преобразуется в bool, `false` < `true`
object | object | Встроенные классы могут определять свои правила сравнения, объекты разных классов не сравниваются, про сравнение объектов одного класса рассказано в разделе «Сравнение объекта»
string, resource, int или float | string, resource, int или float | Строки и ресурсы переводятся в числа, обычная математика
array | array | Массив с меньшим количеством элементов меньше, если PHP не нашёл ключ из первого массива во втором массиве — массивы несравнимы, иначе идёт сравнение значения за значением (смотрите пример ниже)
array | значение любого типа | Тип array всегда больше
object | значение любого типа | Тип object всегда больше

Пример #2 Примеры сравнения boolean- и null-значений со значениями других типов

    <?php

    // Логические значения и null сравниваются как логические
    var_dump(1 == TRUE); // TRUE  — то же, что и (bool) 1 == TRUE
    var_dump(0 == FALSE); // TRUE  — то же, что и (bool) 0 == FALSE
    var_dump(100 < TRUE); // FALSE  — то же, что и (bool) 100 < TRUE
    var_dump(-10 < FALSE); // FALSE  — то же, что и (bool) -10 < FALSE
    var_dump(min(-100, -10, NULL, 10, 100)); // NULL  — (bool) NULL < (bool) -100 это FALSE < TRUE

    ?>

Пример #3 Транскрипция стандартного сравнения массивов

    <?php

    // Массивы сравниваются как в этом примере — со стандартными операторами сравнения, и оператором «космический корабль» (spaceship).
    function standard_array_compare($op1, $op2)
    {
        if (count($op1) < count($op2)) {
            return -1; // $op1 < $op2
        } elseif (count($op1) > count($op2)) {
            return 1; // $op1 > $op2
        }

        foreach ($op1 as $key => $val) {
            if (!array_key_exists($key, $op2)) {
                return 1;
            } elseif ($val < $op2[$key]) {
                return -1;
            } elseif ($val > $op2[$key]) {
                return 1;
            }
        }

        return 0; // $op1 == $op2
    }

Внимание

# Сравнение чисел с плавающей точкой

Из-за внутреннего представления чисел с плавающей точкой (float) не нужно проверять два числа с плавающей точкой (float) на равенство.

Подробнее об этом рассказывает в документации к типу float.

> Замечание: Когда пишут код, помнят, что жонглирование типами в PHP не всегда даёт предсказуемый результат при сравнении значений разных типов, особенно при сравнении целых чисел (int) с логическими значениями (bool) или целых чисел (int) со строками (string). Поэтому лучше пользоваться операторами `===` и `!==`, а не `==` и `!=`.

### Несравнимые значение

Хотя тождественные сравнения `===` и `!==` применяют к произвольным значениям, другие операторы сравнения лучше применять только к сравнимым значениям. Результат сравнения несравнимых значений не определён и на него не нужно полагаться.

### Смотрите также

- strcasecmp()
- strcmp()
- Операторы работы с массивами
- Типы

### Тернарный оператор

Список условных операторов дополняет тернарный оператор «?:».

Пример #4 Присваивание значения по умолчанию

    <?php

    // Пример выражения с тернарным оператором
    $action = (empty($_POST['action'])) ? 'default' : $_POST['action'];

    // Код выше аналогичен блоку с конструкциями if/else
    if (empty($_POST['action'])) {
        $action = 'default';
    } else {
        $action = $_POST['action'];
    }

    ?>

Выражение `(expr1) ? (expr2) : (expr3)` интерпретируется как expr2, если expr1 равно `true`, или как expr3, если expr1 равно `false`.

Можно не писать среднюю часть тернарного оператора. Выражение `expr1 ?: expr3` оценивается как результат выражения expr1, если оно оценивается как `true`, иначе как результат выражения expr3. Выражение expr1 оценивается только один раз.

> Замечание: Обратите внимание, что тернарный оператор — это выражение, и он оценивается не как переменная, а как результат выражения. Это важно, если нужно вернуть переменную по ссылке. Выражение `return $var == 42 ? $a : $b;` не будет работать в функции, возвращающей значение по ссылке, а в более поздних версиях PHP также будет выдано предупреждение.

> Замечание:

> Рекомендовано избегать «нагромождения» тернарных выражений. Поведение PHP при указании более чем одного тернарного оператора без скобок в одном выражении неочевидно в сравнении с другими языками. Впрямь, до PHP 8.0.0 троичные выражения оценивались как левоассоциативные, а не правоассоциативные, как в большей части других языков программирования. Опора на левую ассоциативность устарела начиная с PHP 7.4.0. Начиная с PHP 8.0.0 тернарный оператор неассоциативен.

> Пример #5 Неочевидное поведение тернарного оператора

>     <?php

>     // Кажется, что следующий код выведет "true"
>     echo (true ? 'true' : false ? 't' : 'f');

>     // Однако код выводит "t" до PHP 8.0.0.
>     // Причина состоит в том, что тернарные выражения левоассоциативны

>     // Следующая запись — более очевидная версия приведённого кода
>     echo ((true ? 'true' : false) ? 't' : 'f');

>     // Здесь видно, что первое выражение оценивается как строковое "true", которое
>     // оценивается как логическое (bool) true, поэтому возвращает истинную ветвь
>     // второго тернарного выражения

>     ?>

> Замечание:

> Цепочка коротких тернарных операторов (`?:`), однако, стабильна и ведёт себя обоснованно. Она будет оценивать первый аргумент, который оценивается как не ложное значение. Обратите внимание, что неопределённые значения все равно вызовут предупреждение.

> Пример #6 Цепочка коротких тернарных операторов

>     <?php

>     echo 0 ?: 1 ?: 2 ?: 3, PHP_EOL; // 1
>     echo 0 ?: 0 ?: 2 ?: 3, PHP_EOL; // 2
>     echo 0 ?: 0 ?: 0 ?: 3, PHP_EOL; // 3

>     ?>

### Оператор объединения с null

Другой полезный сокращённый оператор — это оператор объединения с NULL — «??» (null coalescing).

Пример #7 Присваивание значения по умолчанию

    <?php

    // Пример работы с оператором нулевого слияния
    $action = $_POST['action'] ?? 'default';

    // Пример выше аналогичен этому выражению с if/else
    if (isset($_POST['action'])) {
        $action = $_POST['action'];
    } else {
        $action = 'default';
    }

Выражение `(expr1) ?? (expr2)` вычисляется так: expr2, если expr1 равно `null`, иначе expr1.

Этот оператор не вызывает предупреждения или ошибки, если левый операнд не существует, точно как языковая конструкция isset(). Это очень полезно для ключей массива.

> Замечание: Обратите внимание, оператор объединения с NULL — это выражение, и он оценивается не как переменная, а как результат вычисления выражения. Это важно, если нужно вернуть значение по ссылке. Выражение `return $foo ?? $bar;` в функции, возвращающей ссылку, будет не работать, а выводить предупреждение.

> Замечание:

> У оператора объединения с NULL низкий приоритет. То есть при смешивании его с другими операторами (например, с операторами конкатенации строк или арифметическими операторами), скорее всего, потребуются круглые скобки.

>     <?php

>     // Вызывает предупреждение о том, что переменную $name не определили
>     print 'Mr. ' . $name ?? 'Anonymous';

>     // Выведет "Mr. Anonymous"
>     print 'Mr. ' . ($name ?? 'Anonymous');

>     ?>

> Замечание:

> Обратите внимание, оператор объединения с NULL разрешает простую вложенность:

> Пример #8 Вложенный оператор null coalescing

>     <?php

>     $foo = null;
>     $bar = null;
>     $baz = 1;
>     $qux = 2;

>     echo $foo ?? $bar ?? $baz ?? $qux; // Выведет 1

>     ?>

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open