1
0
mirror of https://github.com/php/doc-ru.git synced 2026-04-25 16:28:19 +02:00
Files
archived-doc-ru/language/operators/comparison.xml
T

608 lines
23 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: 43d07782b514d0c7a8487f2c74063739f302df8d Maintainer: sergey Status: ready -->
<!-- Reviewed: no -->
<sect1 xml:id="language.operators.comparison">
<title>Операторы сравнения</title>
<titleabbrev>Сравнение</titleabbrev>
<simpara>
Операторы сравнения, как это видно из их названия, разрешают сравнивать между собой
два значения. Могут также оказаться интересными для знакомства
<link linkend="types.comparisons">таблицы сравнения типов</link>,
поскольку в них показаны примеры сравнений, связанных с разными типами.
</simpara>
<table>
<title>Операторы сравнения</title>
<tgroup cols="3">
<thead>
<row>
<entry>Пример</entry>
<entry>Название</entry>
<entry>Результат</entry>
</row>
</thead>
<tbody>
<row>
<entry>$a == $b</entry>
<entry>Равно</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
после преобразования типов равно
значению переменной <varname>$b</varname>.
</entry>
</row>
<row>
<entry>$a === $b</entry>
<entry>Тождественно равно</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
равно значению переменной <varname>$b</varname> и имеет тот же тип.
</entry>
</row>
<row>
<entry>$a != $b</entry>
<entry>Не равно</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
после преобразования типов
не равно значению переменной <varname>$b</varname>.
</entry>
</row>
<row>
<entry>$a &lt;&gt; $b</entry>
<entry>Не равно</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
после преобразования типов
не равно значению переменной <varname>$b</varname>.
</entry>
</row>
<row>
<entry>$a !== $b</entry>
<entry>Тождественно не равно</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
не равно значению переменной <varname>$b</varname>
или они разных типов.
</entry>
</row>
<row>
<entry>$a &lt; $b</entry>
<entry>Меньше</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
строго меньше значения переменной <varname>$b</varname>.
</entry>
</row>
<row>
<entry>$a &gt; $b</entry>
<entry>Больше</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
строго больше значения переменной <varname>$b</varname>.
</entry>
</row>
<row>
<entry>$a &lt;= $b</entry>
<entry>Меньше или равно</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
меньше или равно значению переменной <varname>$b</varname>.
</entry>
</row>
<row>
<entry>$a &gt;= $b</entry>
<entry>Больше или равно</entry>
<entry>
Возвращается &true;, если значение переменной <varname>$a</varname>
больше или равно значению переменной <varname>$b</varname>.
</entry>
</row>
<row>
<entry>$a &lt;=&gt; $b</entry>
<entry>Космический корабль (spaceship)</entry>
<entry>
Целое число (<type>int</type>) меньше, больше или равное нулю, когда
значение переменной <varname>$a</varname> меньше, больше или равно
значению переменной <varname>$b</varname>.
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Если оба операнда —
<link linkend="language.types.numeric-strings">строки, содержащие числа</link>,
или один операнд — число, а другой — <link linkend="language.types.numeric-strings">строка, содержащая числа</link>,
то сравнение выполняется численно.
Эти правила также справедливы для оператора <link linkend="control-structures.switch">switch</link>.
Тип не преобразовывается при сравнениях вида
<literal>===</literal> или <literal>!==</literal>, поскольку это включает сравнение
типа, а также значения.
</para>
<warning>
<para>
До PHP 8.0.0, если строка (<type>string</type>) сравнивалась с числом
или строкой, содержащей число, то строка (<type>string</type>) преобразовывалась
в число перед выполнением сравнения. Это могло привести к неожиданным
результатам, что можно увидеть на следующем примере:
<informalexample>
<programlisting role="php">
<![CDATA[
<?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;
}
]]>
</programlisting>
&example.outputs.7;
<screen>
<![CDATA[
bool(true)
bool(true)
bool(true)
bool(true)
0
]]>
</screen>
&example.outputs.8;
<screen>
<![CDATA[
bool(false)
bool(true)
bool(true)
bool(true)
a
]]></screen>
</informalexample>
</para>
</warning>
<para>
<informalexample>
<programlisting role="php"><![CDATA[
<?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
?>
]]>
</programlisting>
</informalexample>
</para>
<para>
Для различных типов сравнение происходит в соответствии со следующей
таблицей (по порядку).
</para>
<table xml:id="language.operators.comparison.types">
<title>Сравнение типов</title>
<tgroup cols="3">
<thead>
<row>
<entry>Тип операнда 1</entry>
<entry>Тип операнда 2</entry>
<entry>Результат</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<type>null</type> или <type>string</type>
</entry>
<entry>
<type>string</type>
</entry>
<entry>&null; преобразуется в пустую строку (""), числовое или лексическое сравнение</entry>
</row>
<row>
<entry>
<type>bool</type> или <type>null</type>
</entry>
<entry>что угодно</entry>
<entry>Преобразуется в <type>bool</type>, &false; &lt; &true;</entry>
</row>
<row>
<entry>
<type>object</type>
</entry>
<entry>
<type>object</type>
</entry>
<entry>
Встроенные классы могут определять свои правила сравнения,
объекты разных классов не сравниваются,
про сравнение объектов одного класса рассказано в разделе «<link linkend="language.oop5.object-comparison">Сравнение объекта</link>»
</entry>
</row>
<row>
<entry>
<type>string</type>, <type>resource</type>, <type>int</type> или <type>float</type>
</entry>
<entry>
<type>string</type>, <type>resource</type>, <type>int</type> или <type>float</type>
</entry>
<entry>Строки и ресурсы переводятся в числа, обычная математика</entry>
</row>
<row>
<entry>
<type>array</type>
</entry>
<entry>
<type>array</type>
</entry>
<entry>
Массив с меньшим числом элементов меньше,
если ключ из первого массива не найден во втором массиве —
массивы не могут сравниваться, иначе идёт сравнение
значений (смотрите пример ниже)
</entry>
</row>
<row>
<entry>
<type>array</type>
</entry>
<entry>что угодно</entry>
<entry>Тип <type>array</type> всегда больше</entry>
</row>
<row>
<entry>
<type>object</type>
</entry>
<entry>что угодно</entry>
<entry>Тип <type>object</type> всегда больше</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
<example>
<title>Сравнение boolean/null</title>
<programlisting role="php">
<![CDATA[
<?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
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Алгоритм сравнения обычных массивов</title>
<programlisting role="php">
<![CDATA[
<?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
}
]]>
</programlisting>
</example>
</para>
<warning>
<title>Сравнение чисел с плавающей точкой</title>
<para>
По причинам, связанным со способом внутреннего представления чисел с плавающей точкой (<type>float</type>),
не нужно проверять два числа с плавающей точкой (<type>float</type>) на равенство.
</para>
<para>
Подробнее об этом можно узнать в документации по типу <type>float</type>.
</para>
</warning>
<note>
<simpara>
Когда пишут код, помнят, что жонглирование типами в PHP
не всегда даёт предсказуемый результат при сравнении значений разных типов,
особенно при сравнении целых чисел (&integer;) с логическими значениями (&boolean;)
или целых чисел (&integer;) со строками (&string;).
Поэтому лучше пользоваться операторами <literal>===</literal> и <literal>!==</literal>,
а не <literal>==</literal> и <literal>!=</literal>.
</simpara>
</note>
<sect2 xml:id="language.operators.comparison.incomparable">
<title>Несравнимые значение</title>
<simpara>
Хотя тождественные сравнения (<literal>===</literal> и <literal>!==</literal>)
можно применять к произвольным значениям,
другие операторы сравнения лучше применять только к сравнимым значениям.
Результат сравнения несравнимых значений не определён и на него не нужно полагаться.
</simpara>
</sect2>
<sect2 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>strcasecmp</function></member>
<member><function>strcmp</function></member>
<member><link linkend="language.operators.array">Операторы, работающие с массивами</link></member>
<member><link linkend="language.types">Типы</link></member>
</simplelist>
</para>
</sect2>
<sect2 xml:id="language.operators.comparison.ternary">
<title>Тернарный оператор</title>
<para>
Ещё один условный оператор — тернарный оператор «?:».
<example>
<title>Присваивание значения по умолчанию</title>
<programlisting role="php">
<![CDATA[
<?php
// Пример выражения с тернарным оператором
$action = (empty($_POST['action'])) ? 'default' : $_POST['action'];
// Код выше аналогичен блоку с конструкциями if/else
if (empty($_POST['action'])) {
$action = 'default';
} else {
$action = $_POST['action'];
}
]]>
</programlisting>
</example>
Выражение <literal>(expr1) ? (expr2) : (expr3)</literal>
интерпретируется как <replaceable>expr2</replaceable>, если
<replaceable>expr1</replaceable> равно &true;, или как
<replaceable>expr3</replaceable>, если
<replaceable>expr1</replaceable> равно &false;.
</para>
<para>
Можно не писать среднюю часть тернарного оператора.
Выражение <literal>expr1 ?: expr3</literal> оценивается
как результат выражения <replaceable>expr1</replaceable>,
если оно оценивается как &true;,
иначе как результат выражения <replaceable>expr3</replaceable>.
Выражение <replaceable>expr1</replaceable> оценивается только один раз.
</para>
<note>
<simpara>
Обратите внимание, что тернарный оператор — это выражение,
и он оценивается не как переменная, а как результат выражения.
Это важно, если нужно вернуть переменную по ссылке.
Выражение <literal>return $var == 42 ? $a : $b;</literal> не будет
работать в функции, возвращающей значение по ссылке, а в более поздних
версиях PHP также будет выдано предупреждение.
</simpara>
</note>
<note>
<para>
Рекомендовано избегать «нагромождения» тернарных выражений.
Поведение PHP при указании более чем одного тернарного оператора без скобок
в одном выражении неочевидно в сравнении с другими языками.
Впрямь, до PHP 8.0.0 троичные выражения оценивались как левоассоциативные,
а не правоассоциативные, как в большей части других языков программирования.
Опора на левую ассоциативность устарела начиная с PHP 7.4.0.
Начиная с PHP 8.0.0 тернарный оператор неассоциативен.
<example>
<title>Неочевидное поведение тернарного оператора</title>
<programlisting role="php">
<![CDATA[
<?php
// кажется, что следующий код выведет «true»
echo (true ? 'true' : false ? 't' : 'f');
// однако он выводит «t» до PHP 8.0.0
// это потому, что тернарные выражения левоассоциативны
// следующая запись — более очевидная версия того же кода, который показан выше
echo ((true ? 'true' : false) ? 't' : 'f');
// здесь видно, что первое выражение оценивается как строковое «true», которое
// оценивается как логическое (bool) true, поэтому возвращает истинную ветвь
// второго тернарного выражения.
]]>
</programlisting>
</example>
</para>
</note>
<note>
<para>
Цепочка коротких тернарных операторов (<literal>?:</literal>), однако, стабильна и ведёт себя обоснованно.
Она будет оценивать первый аргумент, который оценивается как не ложное значение.
Обратите внимание, что неопределённые значения все равно вызовут предупреждение.
<example>
<title>Цепочка коротких тернарных операторов</title>
<programlisting role="php">
<![CDATA[
<?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
?>
]]>
</programlisting>
</example>
</para>
</note>
</sect2>
<sect2 xml:id="language.operators.comparison.coalesce">
<title>Оператор объединения с null</title>
<para>
Другой полезный сокращённый оператор — это оператор объединения с NULL — «??» (null coalescing).
<example>
<title>Присваивание значения по умолчанию</title>
<programlisting role="php">
<![CDATA[
<?php
// Пример работы с оператором нулевого слияния
$action = $_POST['action'] ?? 'default';
// Пример выше аналогичен этому выражению с if/else
if (isset($_POST['action'])) {
$action = $_POST['action'];
} else {
$action = 'default';
}
]]>
</programlisting>
</example>
Выражение <literal>(expr1) ?? (expr2)</literal> вычисляется так:
<replaceable>expr2</replaceable>, если <replaceable>expr1</replaceable> равно
&null;, иначе <replaceable>expr1</replaceable>.
</para>
<para>
Этот оператор не вызывает предупреждения или ошибки, если левый операнд
не существует, точно как языковая конструкция <function>isset</function>.
Это очень полезно для ключей массива.
</para>
<note>
<simpara>
Обратите внимание, оператор объединения с NULL — это выражение,
и он оценивается не как переменная, а как результат вычисления выражения.
Это важно, если нужно
вернуть значение по ссылке. Выражение <literal>return $foo ?? $bar;</literal> в
функции, возвращающей ссылку, будет не работать, а выводить предупреждение.
</simpara>
</note>
<note>
<para>
У оператора объединения с NULL низкий приоритет. То есть при смешивании его с другими операторами
(например, с операторами конкатенации строк или арифметическими операторами), скорее всего, потребуются круглые скобки.
</para>
<programlisting role="php">
<![CDATA[
<?php
// Вызывает предупреждение о том, что $name не определено.
print 'Mr. ' . $name ?? 'Anonymous';
// Выведет "Mr. Anonymous"
print 'Mr. ' . ($name ?? 'Anonymous');
]]>
</programlisting>
</note>
<note>
<para>
Обратите внимание, оператор объединения с NULL разрешает простую вложенность:
<example>
<title>Вложенный оператор null coalescing</title>
<programlisting role="php">
<![CDATA[
<?php
$foo = null;
$bar = null;
$baz = 1;
$qux = 2;
echo $foo ?? $bar ?? $baz ?? $qux; // выведет 1
]]>
</programlisting>
</example>
</para>
</note>
</sect2>
</sect1>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->