Операторы сравнения, как это видно из названия, разрешают сравнивать между собой два значения. Интерес для знакомства также вызывают таблицы сравнения типов, поскольку показывают примеры сравнений, которые связаны с разными типами.
Пример | Название | Результат |
---|---|---|
$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
<?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 всегда больше |
Пример #1 Примеры сравнения 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
?>
Пример #2 Алгоритм сравнения обычных массивов
<?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). Поэтому лучше пользоваться операторами
===
и!==
, а не==
и!=
.
Хотя тождественные сравнения ===
и !==
применяют к произвольным значениям,
другие операторы сравнения лучше применять только к сравнимым значениям.
Результат сравнения несравнимых значений не определён и на него не нужно полагаться.
Список условных операторов дополняет тернарный оператор «?:».
Пример #3 Присваивание значения по умолчанию
<?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 тернарный оператор неассоциативен.
Пример #4 Неочевидное поведение тернарного оператора
<?php
// Кажется, что следующий код выведет "true"
echo (true ? 'true' : false ? 't' : 'f');
// Однако код выводит "t" до PHP 8.0.0.
// Причина состоит в том, что тернарные выражения левоассоциативны
// Следующая запись — более очевидная версия приведённого кода
echo ((true ? 'true' : false) ? 't' : 'f');
// Здесь видно, что первое выражение оценивается как строковое "true", которое
// оценивается как логическое (bool) true, поэтому возвращает истинную ветвь
// второго тернарного выражения
?>
Замечание:
Цепочка коротких тернарных операторов (
?:
), однако, стабильна и ведёт себя обоснованно. Она будет оценивать первый аргумент, который оценивается как не ложное значение. Обратите внимание, что неопределённые значения все равно вызовут предупреждение.Пример #5 Цепочка коротких тернарных операторов
<?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 coalescing).
Пример #6 Присваивание значения по умолчанию
<?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 разрешает простую вложенность:
Пример #7 Вложенный оператор null coalescing
<?php
$foo = null;
$bar = null;
$baz = 1;
$qux = 2;
echo $foo ?? $bar ?? $baz ?? $qux; // Выведет 1
?>