Условный оператор if в PHP — это конструкция, которая выполняет блок кода только тогда, когда выражение в скобках после приведения к типу bool дает true. Для второй ветки служит else, для цепочки проверок — elseif. Кроме if, в PHP есть еще четыре способа ветвления: тернарный оператор ? : (и его короткая форма ?:), оператор объединения с null ??, switch и выражение match (с PHP 8.0).
Содержание
- if, elseif, else: минимальный рабочий пример
- Что PHP считает истиной в условии
- == и ===: как сравнивать в условиях
- Три частые ошибки в if
- Тернарный оператор, ?: и ??
- switch и match: выбор по значению
- Условие на пользовательском вводе
- Что выбрать: сводная таблица
- Выводы
- Где применяется / связь с практикой
- FAQ
Важно различать две вещи, которые часто смешивают: конструкции ветвления (if, switch, match) выбирают, какой код выполнить, а операторы сравнения (==, ===, < и другие) вычисляют само условие. Ошибки в условиях PHP чаще всего живут именно во втором: в нестрогом сравнении и приведении к bool. Все примеры ниже запускались на PHP 8.4.25 и 8.5.10 (CLI) 24.09.2026, вывод одинаковый.
if, elseif, else: минимальный рабочий пример
Функция переводит балл экзамена в оценку. Скопируйте код в файл grade.php и запустите php grade.php. Во всех примерах ниже путь в сообщениях об ошибках (/app/...) будет вашим путем к файлу.
<?php
function grade(int $score): string
{
if ($score < 0 || $score > 100) {
return 'ошибка: балл вне диапазона 0-100';
} elseif ($score >= 85) {
return 'отлично';
} elseif ($score >= 70) {
return 'хорошо';
} elseif ($score >= 50) {
return 'зачет';
} else {
return 'незачет';
}
}
foreach ([92, 70, 51, 12, 140] as $s) {
echo $s, ' -> ', grade($s), PHP_EOL;
}
В консоли появится:
92 -> отлично
70 -> хорошо
51 -> зачет
12 -> незачет
140 -> ошибка: балл вне диапазона 0-100
Как это читается:
if (условие) { ... }— PHP вычисляет выражение в круглых скобках и приводит результат к bool. Еслиtrue, выполняется блок в фигурных скобках.elseifпроверяется только тогда, когда все условия выше далиfalse. Проверка идет сверху вниз и останавливается на первом истинном условии, поэтому порядок важен: для балла 92 истинны и>= 85, и>= 70, но выполнится только первая ветка.elseне имеет условия и срабатывает, если не сработало ничего выше. Он необязателен и не может стоять безif.- Проверку некорректного ввода удобно ставить первой: так остальные ветки работают уже с допустимыми данными.
Фигурные скобки при одной инструкции формально можно опустить, но это источник ошибок: else всегда привязывается к ближайшему if без пары, а не к тому, под которым он выровнен отступом.
<?php
$a = true; $b = false;
if ($a)
if ($b)
echo 'a и b', PHP_EOL;
else
echo 'этот else относится к if ($b)', PHP_EOL;
Результат — этот else относится к if ($b): отступ ничего не значит для интерпретатора. Исправление — всегда ставить фигурные скобки, тогда принадлежность else видна однозначно.
elseif и else if: есть ли разница
В обычном синтаксисе со скобками elseif и else if работают одинаково: второй вариант — это else, внутри которого вложен новый if. Разница появляется в альтернативном синтаксисе с двоеточием, который используют в HTML-шаблонах. Там допустим только elseif:
<?php $n = 2; ?>
<?php if ($n === 1): ?>
один
<?php else if ($n === 2): ?>
два
<?php endif; ?>
Parse error: syntax error, unexpected token "if", expecting ":" in /app/template.php on line 4
Рабочий шаблон с тем же синтаксисом выглядит так:
<?php $items = ['книга', 'ручка']; ?>
<?php if (count($items) === 0): ?>
<p>Корзина пуста</p>
<?php elseif (count($items) === 1): ?>
<p>В корзине 1 товар</p>
<?php else: ?>
<p>Товаров в корзине: <?= count($items) ?></p>
<?php endif; ?>
Вывод: <p>Товаров в корзине: 2</p>. Формулировка «Товаров: N» выбрана намеренно: вариант «В корзине N товара» дал бы «5 товара», а склонение по числу — отдельная задача с тем же if/elseif. Блоки закрываются словом endif;, а <?= ... ?> — короткая запись для echo.
Что PHP считает истиной в условии
Выражение в if не обязано быть сравнением: туда можно передать переменную любого типа, и PHP приведет ее к bool. Для скаляров, массивов и null ложными считаются только значения с результатом false в таблице ниже, все остальное — истина.
| Значение | В if |
Комментарий |
|---|---|---|
false, null |
false | |
0, 0.0 |
false | ноль любого числового типа |
'', '0' |
false | пустая строка и строка «0» |
[] |
false | пустой массив |
'0.0', ' ', 'false' |
true | непустые строки, кроме '0' |
[0] |
true | непустой массив, даже если внутри ноль |
-1 |
true | любое ненулевое число |
Все строки таблицы получены прогоном var_export($v) и $v ? 'true' : 'false'. Граница: у объектов свои правила (например, пустой объект SimpleXMLElement считается ложным), поэтому для объектов лучше проверять явно: $obj !== null, $obj instanceof Foo.
Из таблицы следует практическое правило: if ($x) подходит, когда нужно «что-нибудь непустое». Если 0 или '0' — допустимое значение (количество товаров, код ответа), пишите явную проверку: if ($count !== null) или if ($input !== '').
== и ===: как сравнивать в условиях
== (нестрогое равенство) перед сравнением приводит операнды к общему типу. === (строгое равенство) дает true, только если совпадают и тип, и значение. Результаты прогона на PHP 8.4/8.5:
| Выражение | == |
=== |
|---|---|---|
0 и 'abc' |
false | false |
'1' и '01' |
true | false |
'10' и '1e1' |
true | false |
100 и '1e2' |
true | false |
null и false |
true | false |
[] и false |
true | false |
1.0 и 1 |
true | false |
Две строки, похожие на числа, при == сравниваются как числа, поэтому '10' == '1e1'. Это опасно при проверке токенов, хешей и кодов подтверждения: для них используйте ===, а для секретов — hash_equals(), которая к тому же сравнивает за время, не зависящее от места первого различия.
Версионная оговорка: строка 0 == 'abc' дает false начиная с PHP 8.0. В PHP 7 сравнение числа с нечисловой строкой приводило строку к числу, и результат был true. Если поддерживаете старый код, это место стоит проверить отдельно.
Правило по умолчанию: в условиях пишите === и !==, а == оставляйте для случаев, где приведение типов нужно осознанно.
Три частые ошибки в if
Первая — присваивание вместо сравнения. Один знак = присваивает значение, и условие проверяет уже его:
<?php
$role = 'guest';
if ($role = 'admin') {
echo "доступ открыт, role = $role", PHP_EOL;
}
Результат: доступ открыт, role = admin — условие всегда истинно, а переменная испорчена. Исправление: if ($role === 'admin').
Вторая — проверка результата функции, которая может вернуть 0 как успех. strpos() возвращает позицию подстроки или false, а позиция в начале строки равна 0:
<?php
$pos = strpos('php', 'p');
if ($pos) {
echo 'найдено', PHP_EOL;
} else {
echo 'не найдено (а на самом деле pos = ', var_export($pos, true), ')', PHP_EOL;
}
if (strpos('php', 'p') !== false) {
echo 'strpos !== false: найдено', PHP_EOL;
}
if (str_contains('php', 'p')) {
echo 'str_contains: найдено', PHP_EOL;
}
не найдено (а на самом деле pos = 0)
strpos !== false: найдено
str_contains: найдено
Исправление — сравнивать строго с false или, если позиция не нужна, взять str_contains() (есть с PHP 8.0).
Третья — and/or вместо &&/||. У слов приоритет ниже, чем у присваивания: $r = true and false; сначала записывает true в $r, и var_dump($r) показывает bool(true). С && получится ожидаемый bool(false). В условиях используйте &&, || и !.
Тернарный оператор, ?: и ??
Тернарный оператор условие ? значение1 : значение2 — это выражение, а не инструкция: он возвращает значение, которое можно присвоить или вывести. Подходит, когда обе ветки короткие и нужно выбрать одно из двух значений.
<?php
$config = ['timeout' => 0, 'name' => ''];
$t1 = $config['timeout'] ?: 30;
$t2 = $config['timeout'] ?? 30;
$retries = $config['retries'] ?? 3;
echo "?: -> $t1, ?? -> $t2, retries -> $retries", PHP_EOL;
$age = 17;
$status = $age >= 18 ? 'взрослый' : 'несовершеннолетний';
echo $status, PHP_EOL;
$config['name'] ??= 'default';
$config['lang'] ??= 'ru';
echo var_export($config['name'], true), ' ', $config['lang'], PHP_EOL;
?: -> 30, ?? -> 0, retries -> 3
несовершеннолетний
'' ru
Здесь три разных оператора, и путать их нельзя:
| Запись | Когда берется значение справа | Предупреждение при отсутствии ключа |
|---|---|---|
$a ?: $b |
$a ложно (0, », null, [] …) |
да, если ключа нет |
$a ?? $b |
$a не существует или равно null |
нет |
$a ??= $b |
то же, что ??, и результат записывается в $a |
нет |
В примере таймаут 0 — осознанная настройка «без ограничения», и ?: ее молча заменил на 30, а ?? сохранил. Для значений по умолчанию из массивов, $_GET и конфигов берите ??.
Вложенный тернарный оператор без скобок с PHP 8.0 не компилируется:
<?php
$x = 5;
echo $x > 0 ? 'плюс' : $x < 0 ? 'минус' : 'ноль';
Fatal error: Unparenthesized `a ? b : c ? d : e` is not supported. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)` in /app/ternary.php on line 3
Исправление — скобки: $x > 0 ? 'плюс' : ($x < 0 ? 'минус' : 'ноль') выводит плюс. Если веток больше двух, читаемее if/elseif или match.
switch и match: выбор по значению
switch сравнивает одно выражение с набором case через нестрогое == и выполняет код, начиная с совпавшей метки, до ближайшего break. match (PHP 8.0+) — выражение: сравнивает строго через ===, возвращает значение и не «проваливается» в соседние ветки.
<?php
$code = '1';
switch ($code) {
case 1:
echo 'switch: создан', PHP_EOL;
case 2:
echo 'switch: обновлен', PHP_EOL;
break;
default:
echo 'switch: неизвестно', PHP_EOL;
}
echo match ((int) $code) {
1 => 'match: создан',
2 => 'match: обновлен',
default => 'match: неизвестно',
}, PHP_EOL;
echo match ($code) {
1 => 'match: создан',
2 => 'match: обновлен',
}, PHP_EOL;
switch: создан
switch: обновлен
match: создан
Fatal error: Uncaught UnhandledMatchError: Unhandled match case '1' in /app/switch_match.php:20
Stack trace:
#0 {main}
thrown in /app/switch_match.php on line 20
Разбор по строкам вывода. Строка '1' совпала с case 1 из-за нестрогого сравнения, а забытый break выполнил и ветку case 2. match с явным приведением (int) сработал правильно. Последний match получил строку '1', ни одна ветка строго не совпала, default нет — и PHP выбросил UnhandledMatchError. Это полезное поведение: непредусмотренное значение не проходит молча.
match также умеет объединять значения в одной ветке и проверять диапазоны через match (true):
<?php
$age = 25;
$result = match (true) {
$age < 14 => 'ребенок',
$age < 18 => 'подросток',
$age < 65 => 'взрослый',
default => 'пенсионер',
};
echo $result, PHP_EOL;
$method = 'PATCH';
echo match ($method) {
'GET', 'HEAD' => 'чтение',
'POST', 'PUT', 'PATCH' => 'запись',
'DELETE' => 'удаление',
default => 'метод не поддерживается',
}, PHP_EOL;
Вывод: взрослый и запись. В ветке match может стоять только одно выражение; если нужно несколько инструкций, вынесите их в функцию или используйте if/switch.
Условие на пользовательском вводе
Условия чаще всего проверяют данные из запроса, а там может прийти что угодно, включая массив вместо строки (?page[]=x). Надежная схема: значение по умолчанию через ??, проверка типа, затем выбор из белого списка через match:
<?php
function pageTitle(mixed $page): string
{
if (!is_string($page)) {
return '400 Bad Request';
}
return match ($page) {
'home' => 'Главная',
'courses' => 'Курсы',
'contacts' => 'Контакты',
default => '404 Not Found',
};
}
$page = $_GET['page'] ?? 'home';
echo pageTitle($page), PHP_EOL;
echo pageTitle('courses'), PHP_EOL;
echo pageTitle('../../etc/passwd'), PHP_EOL;
echo pageTitle(['courses']), PHP_EOL;
В CLI массив $_GET пуст, поэтому вывод: Главная, Курсы, 404 Not Found, 400 Bad Request. Строка с ../ не пройдет, потому что допустимы только перечисленные значения. Граница примера: это выбор по белому списку, а не полная защита страницы. Вывод данных в HTML все равно нужно экранировать (htmlspecialchars), а в SQL — передавать через параметры запроса.
Что выбрать: сводная таблица
| Задача | Конструкция | Почему |
|---|---|---|
| Выполнить или пропустить блок кода | if |
условие любого вида |
| Несколько условий-диапазонов по порядку | if/elseif/else |
проверка сверху вниз до первого совпадения |
| Выбрать одно из двух коротких значений | ? : |
выражение, можно присвоить |
| Значение по умолчанию для null или отсутствующего ключа | ??, ??= |
без предупреждений, не трогает 0 и » |
| Выбор значения по точному совпадению | match |
строгое ===, ошибка на неучтенный вариант |
| Несколько инструкций на каждый вариант, legacy-код | switch |
помнить про == и break |
| Условие в HTML-шаблоне | if: ... elseif: ... endif; |
читаемо среди разметки |
Выводы
if/elseif/elseпроверяет условия сверху вниз и выполняет только первую истинную ветку, поэтому порядок условий определяет результат.- В условии значение приводится к bool:
0,'0','',[],nullложны, а'0.0'и' '— истинны; где0допустим, нужна явная проверка. - В условиях по умолчанию используйте
===:==приводит типы, и'10' == '1e1'даетtrue. ??подставляет значение по умолчанию только для null и отсутствующего ключа,?:— для любого ложного значения; вложенный тернарный оператор без скобок с PHP 8.0 — ошибка.matchсравнивает строго, возвращает значение и падает на неучтенном варианте;switchсравнивает через==и требуетbreak.
Где применяется / связь с практикой
Освойте тему на практике
Условия — часть почти каждого обработчика на PHP: маршрутизация запросов, проверка прав доступа, валидация форм, выбор статуса ответа API. Ошибки из этой статьи (нестрогое сравнение токена, strpos без !== false, switch без break) регулярно встречаются на код-ревью и в уязвимостях реальных проектов. Системно выстроить работу с PHP от синтаксиса до фреймворков, тестов и архитектуры можно на курсе «PHP-разработчик. Продвинутый уровень». Попробовать формат обучения бесплатно можно на открытых уроках Otus.
FAQ
Можно ли написать if в одну строку?
Да: if ($x > 0) echo 'плюс'; работает, но при добавлении второй инструкции без скобок она выполнится всегда. Для присваивания одного из двух значений в одну строку удобнее тернарный оператор.
Чем isset($a) отличается от $a ?? null?
isset() возвращает bool (существует и не null), а ?? сразу возвращает само значение или запасное. Оба не выдают предупреждение о неопределенной переменной или ключе.
Есть ли в PHP оператор «космический корабль» и зачем он в условиях?
Оператор <=> возвращает -1, 0 или 1 в зависимости от того, меньше, равно или больше левое значение. В условиях он встречается редко, основное применение — функции сравнения для usort().



