# `isset()` vs `empty()` в PHP: полный разбор

[← Tips](./index.md) · [← Оглавление](../index.md)

---

## Суть различия в одном предложении

- **`isset()`** проверяет, **существует ли переменная** и не равна ли она `null`.
- **`empty()`** проверяет, является ли переменная **«пустой»** в понимании PHP. Если переменной не существует — `empty()` просто считает её пустой и не генерирует предупреждение.

---

## Детальный разбор `isset()`

`isset()` возвращает `true`, только если переменная **определена** и её значение **не равно `null`**. Во всех остальных случаях — `false`.

**Вернёт `true`:**
- Переменная объявлена и равна чему угодно, кроме `null` — пустая строка `''`, `0`, `false`, пустой массив `[]` — всё это `true` для `isset()`.

**Вернёт `false`:**
- Переменная не объявлена.
- Переменной присвоено значение `null`.
- Переменная была удалена через `unset()`.

---

## Детальный разбор `empty()`

`empty()` возвращает `true`, если переменная **не существует** или её значение **приводится к логическому `false`**.

**`empty()` вернёт `true` для следующих значений:**

| # | Значение | Тип |
|---|----------|-----|
| 1 | `""` | пустая строка |
| 2 | `"0"` | строка с нулём — **частый источник ошибок!** |
| 3 | `0` | целое число |
| 4 | `0.0` | число с плавающей точкой |
| 5 | `false` | булево |
| 6 | `null` | null |
| 7 | `[]` | пустой массив |
| 8 | переменная не объявлена | — |

Все остальные значения (`true`, непустые строки, непустые массивы, отрицательные числа, строка `"false"` и т.д.) дадут `false`.

---

## Таблица сравнения

| Значение переменной `$var` | `isset($var)` | `empty($var)` | Комментарий |
|:---|:---:|:---:|:---|
| Переменная не объявлена | `false` | `true` | `empty()` не генерирует Warning — в отличие от прямого `if ($var)` |
| `$var = null;` | `false` | `true` | Для `isset()` — как будто переменной нет |
| `$var = true;` | `true` | `false` | |
| `$var = false;` | `true` | `true` | `false` считается пустым значением |
| `$var = 0;` | `true` | `true` | `0` считается пустым значением |
| `$var = 0.0;` | `true` | `true` | `0.0` считается пустым значением |
| `$var = "0";` | `true` | `true` | ⚠️ Строка `"0"` считается пустой |
| `$var = "";` | `true` | `true` | |
| `$var = " ";` | `true` | **`false`** | Пробел — не пустое значение для `empty()` |
| `$var = "false";` | `true` | **`false`** | ⚠️ Строка `"false"` — непустая, `empty()` вернёт `false` |
| `$var = [];` | `true` | `true` | |
| `$var = [0];` | `true` | `false` | Массив с одним элементом не пуст, даже если элемент `0` |
| `$var = new stdClass();` | `true` | `false` | Объект без свойств не считается пустым (PHP 5.5+) |

---

## Ключевые нюансы и подводные камни

### 1. Безопасность при обращении к несуществующей переменной

Обе конструкции не генерируют Warning при обращении к необъявленной переменной или несуществующему индексу массива — в отличие от прямого `if ($var)`.

```php
// Безопасно, Warning не будет, вернёт true
if (empty($nonExistentVariable)) {
    echo 'Переменной нет или она пуста';
}

// Безопасно, Warning не будет, вернёт false
if (isset($nonExistentVariable)) {
    // Сюда не попадём
}
```

---

### 2. ⚠️ Проблема строки `"0"`

Самый известный подводный камень `empty()`. Строка `"0"` считается пустой. Если строка `"0"` — валидное значение (например, количество товара), использование `empty()` приведёт к логической ошибке.

```php
$userInput = '0';

// empty() посчитает, что пользователь ничего не ввёл — это ошибка логики
if (empty($userInput)) {
    echo 'Вы ничего не ввели'; // ← выполнится, хотя "0" было введено
}

// Корректная проверка, когда "0" — валидное значение
if ($userInput !== '') {
    echo 'Спасибо за ввод!'; // ← правильное поведение
}
```

---

### 3. `isset()` проверяет несколько переменных сразу

`isset()` принимает несколько аргументов и вернёт `true` только если **все** переменные определены и не `null`. У `empty()` такой возможности нет.

```php
$a = 1;
$b = null;
$c = 3;

var_dump(isset($a, $b, $c)); // false — $b === null

// Для empty нет аналогичного синтаксиса,
// пришлось бы писать: empty($a) || empty($b) || empty($c)
// — но это уже другая логика (ИЛИ вместо И)
```

---

### 4. `empty()` принимает выражения (начиная с PHP 5.5)

Начиная с **PHP 5.5** в `empty()` можно передавать не только переменные, но и произвольные выражения, включая вызовы функций:

```php
// Работает начиная с PHP 5.5 — никакой ошибки нет
if (empty(trim($name))) {
    echo 'Имя не указано';
}
```

> До PHP 5.5 это действительно вызывало Fatal error — но все актуальные версии (7.x, 8.x) поддерживают такой синтаксис без ограничений.

---

### 5. `isset()` и магический метод `__isset()` на объектах

При вызове `isset($object->property)` на недоступном (private/protected) или несуществующем свойстве PHP вызывает магический метод `__isset()`. Это важно при работе с ORM, ActiveRecord и Bitrix-сущностями:

```php
class Foo {
    public function __isset(string $name): bool {
        return $name === 'bar'; // управляем поведением вручную
    }
}

$foo = new Foo();
var_dump(isset($foo->bar)); // true  — отработал __isset()
var_dump(isset($foo->baz)); // false — __isset() вернул false
```

Если класс не реализует `__isset()`, обращение к недоступному свойству через `isset()` просто вернёт `false` без ошибок.

---

### 6. Внутренняя логика `empty()`

По сути, `empty($var)` — это полный безопасный эквивалент `!isset($var) || $var == false`. Обратите внимание: используется **нестрогое сравнение `==`**. Именно поэтому `"0"`, `0`, `0.0`, `""` и `[]` приравниваются к `false`.

---

## Примеры из практики: когда что использовать

### Сценарий 1: Проверка обязательного поля формы

Нужно убедиться, что поле существует и не пустое. При этом `"0"` — валидное значение (например, количество товара).

```php
// ❌ Плохо — строка "0" не пройдёт проверку
if (!empty($_POST['quantity'])) {
    $quantity = $_POST['quantity'];
}

// ✅ Хорошо — явная проверка на непустую строку + trim на случай пробелов
if (isset($_POST['quantity']) && trim($_POST['quantity']) !== '') {
    $quantity = $_POST['quantity'];
}
```

---

### Сценарий 2: Проверка чекбокса

Если HTML-чекбокс не отмечен, ключ в `$_POST` просто отсутствует. Нас интересует факт наличия, а не значение.

```php
// ✅ Идеально подходит isset() — точно выражает намерение
if (isset($_POST['agree_to_terms'])) {
    // Пользователь согласился с условиями
}
```

---

### Сценарий 3: Значение по умолчанию из конфига

```php
// PHP 7.0+ — оператор ?? (null coalescing)
// Вернёт $config['max_items'], если ключ существует и не null, иначе 10
$limit = $config['max_items'] ?? 10;

// PHP 7.4+ — оператор ??= (присваивает, только если null или не существует)
$config['max_items'] ??= 10;
```

Оператор `??` — это прямой синтаксический сахар для `isset()`. Предпочитайте его вместо тернарного оператора с `isset()`.

---

### Сценарий 4: Проверка пустого массива

```php
$items = [];

// ✅ empty() читается естественно — «если массив пуст»
if (empty($items)) {
    echo 'Нет элементов.';
}

// Многословный аналог — избыточно
// if (isset($items) && count($items) === 0) { ... }
```

---

## Итоговый алгоритм выбора

```
Нужно проверить существование переменной / ключа массива
и исключить null?
    → isset()  или оператор ??
    
Нужно проверить «пустоту» переменной (0, "", [], false — всё пустое),
и строка "0" НЕ является валидным значением?
    → empty()
    
Нужно проверить непустую строку, где "0" — валидное значение?
    → isset($var) && trim($var) !== ''
    (забудьте про empty() в этом случае)
    
Нужно присвоить значение по умолчанию, если переменной нет или она null?
    → $value = $var ?? 'default';
    или $var ??= 'default';  (PHP 7.4+)
```

---

## Краткая шпаргалка

```php
isset($var)          // существует И не null?
isset($a, $b, $c)    // все существуют И не null?
empty($var)          // не существует ИЛИ == false (включая "0", 0, [], "")?
$var ?? 'default'    // $var если isset(), иначе 'default'
$var ??= 'default'   // присвоить 'default' если !isset() или null (PHP 7.4+)
```