Различные методы валидации массивов в PHP: от isset до array_diff

Раздел: Проверка данных -> Проверка множества

Раздел «Проверка множества» посвящён методам верификации данных, хранящихся в массивах. В PHP существует множество способов проверить, содержит ли массив нужные ключи или значения. В этой статье рассматриваются основные варианты, их преимущества и недостатки.

Основные приёмы и их применение

Наиболее эффективное решение для проверки наличия всех обязательных полей в массиве данных — использование функции array_diff в связке с array_keys. Этот способ позволяет одной строкой определить, каких ключей не хватает.

$required = ['name', 'email', 'password'];
$data = ['name' => 'John', 'email' => 'john@example.com'];
$missing = array_diff($required, array_keys($data));
if (empty($missing)) {
    echo 'Все поля присутствуют.';
} else {
    echo 'Отсутствуют поля: ' . implode(', ', $missing);
}

Php has many (php has many (проверка множества))

Отсутствуют поля: password

Цель:

Быстрая валидация формы или любого набора данных, когда заранее известен список ожидаемых ключей.

Проблема:

array_diff сравнивает только ключи верхнего уровня. Если необходимо проверить вложенные ключи, потребуется рекурсивный обход. Также array_keys возвращает ключи вне зависимости от значения (null тоже является допустимым ключом).

Решение:

Для вложенных проверок используйте рекурсию или функцию array_walk_recursive. Для игнорирования ключей со значением null примените фильтрацию: array_keys(array_filter($data, function($v) { return $v !== null; })).

Как проверить существование отдельного ключа, включая ключи со значением null?

Используйте array_key_exists. В отличие от isset, эта функция возвращает true даже если значение ключа равно null.

$arr = ['key' => null];
var_dump(isset($arr['key']));           // bool(false)
var_dump(array_key_exists('key', $arr)); // bool(true)
bool(false)
bool(true)

Типичная ошибка:

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

Решение:

Применять array_key_exists всякий раз, когда нужно убедиться, что ключ объявлен, независимо от его значения.

Как проверить, что массив содержит определённое значение?

Для поиска скалярного значения подходит функция in_array. Если нужно получить ключ первого вхождения, используйте array_search.

$array = ['apple', 'banana', 'cherry'];
if (in_array('banana', $array)) {
    echo 'Найдено';
}
$key = array_search('cherry', $array);
echo $key; // 2
Найдено
2

Проблема:

in_array выполняет линейный поиск и для больших массивов может быть медленным. Также он не работает с нескалярными значениями (например, объектами) без указания строгого режима (true).

Решение:

Для ускорения поиска применяйте array_flip в комбинации с isset, особенно если массив статичен или не меняется между запросами. Для нескалярных значений используйте строгий режим или напишите кастомную функцию сравнения.

Как проверить наличие хотя бы одного ключа из списка?

Используйте array_intersect для поиска пересечения ключей.

$data = ['title' => 'Hello', 'body' => 'World'];
$keysToCheck = ['title', 'author', 'date'];
$found = array_intersect($keysToCheck, array_keys($data));
if (!empty($found)) {
    echo 'Найдены ключи: ' . implode(', ', $found);
}
Найдены ключи: title

Ошибка:

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

Решение:

Применять array_intersect для быстрого получения множества совпадений.

Как проверить, что значения не пусты (не null, не пустая строка, не 0 по условию)?

Для фильтрации используйте array_filter с callback, затем сравните количество оставшихся элементов с исходным. Можно также комбинировать isset и strlen, но проще применить array_filter без аргументов (удаляет все ложные значения).

$data = ['name' => 'John', 'middle' => '', 'age' => 0, 'email' => 'john@example.com'];
$filtered = array_filter($data, function($value) {
    return $value !== null && $value !== '' && $value !== false;
});
if (count($filtered) === count($data)) {
    echo 'Все поля непустые.';
} else {
    echo 'Некоторые поля пусты.';
}
Некоторые поля пусты.

Проблема:

Простой array_filter без callback удаляет элементы со значением 0, '0', false, null, ''. Такое поведение может оказаться нежелательным (например, 0 — допустимый возраст).

Решение:

Используйте собственный callback, явно указывающий условия отбрасывания.

Как выполнить проверку в многомерном массиве?

Для вложенных ключей применяйте цепочку isset или array_key_exists на каждом уровне. В PHP 8 доступен оператор ?-> (nullsafe), но он предназначен для объектов. Для массивов пишите рекурсивную функцию.

$multi = ['user' => ['profile' => ['name' => 'Alice']]];
if (isset($multi['user']['profile']['name'])) {
    echo 'Имя: ' . $multi['user']['profile']['name'];
}
// Рекурсивная проверка наличия всех ключей:
function hasKeys(array $array, array $keys) : bool {
    foreach ($keys as $key => $subKeys) {
        if (!isset($array[$key])) return false;
        if (is_array($subKeys) && !hasKeys($array[$key], $subKeys)) return false;
    }
    return true;
}
$requiredNested = ['user' => ['profile' => ['name', 'email']]];
var_dump(hasKeys($multi, $requiredNested)); // bool(false) т.к. email отсутствует
Имя: Alice
bool(false)

Ошибка:

Многие пишут длинные цепочки isset без проверки промежуточных ключей, что вызывает предупреждение «Undefined index» в старых версиях PHP.

Решение:

Всегда оборачивать цепочки в isset или использовать рекурсию с проверкой существования каждого уровня.

Расширенные примеры с подробным кодом

Пример 1. Быстрый поиск значения с помощью array_flip

Если массив статичен и содержит уникальные значения, преобразование его в «маппинг» ускоряет проверку isset.

Пример
$countries = ['RU', 'US', 'CN', 'GB'];
$flip = array_flip($countries);
$check = 'CN';
if (isset($flip[$check])) {
    echo "$check найден";
} else {
    echo "$check отсутствует";
}
CN найден

Пример 2. Проверка, что один массив является подмножеством другого (по значениям)

Используйте array_diff дважды или array_intersect для нахождения общих значений.

Пример
$allowed = ['admin', 'editor', 'subscriber'];
$userRoles = ['editor', 'subscriber'];
$diff = array_diff($userRoles, $allowed);
if (empty($diff)) {
    echo 'Все роли разрешены';
} else {
    echo 'Недопустимые роли: ' . implode(', ', $diff);
}
Все роли разрешены

Пример 3. Проверка наличия хотя бы одного значения из списка (array_intersect)

Пример
$haystack = ['a' => 1, 'b' => 2, 'c' => 3];
$needles = [2, 5];
$intersect = array_intersect($haystack, $needles);
if (!empty($intersect)) {
    echo 'Найдены общие значения: ' . implode(', ', $intersect);
}
Найдены общие значения: 2

Пример 4. Валидация POST-данных с использованием compact и array_keys

Динамическая проверка полей формы.

Пример
$requiredFields = ['username', 'password', 'csrf_token'];
$postData = ['username' => 'user1', 'password' => 'secret', 'csrf_token' => 'token123'];
$missing = array_diff($requiredFields, array_keys($postData));
if (empty($missing)) {
    // дополнительно проверим непустоту значений
    $filled = array_filter($postData, function($v) { return $v !== '' && $v !== null; });
    if (count($filled) === count($requiredFields)) {
        echo 'Форма валидна';
    } else {
        echo 'Некоторые поля пусты';
    }
}
Форма валидна

Пример 5. Проверка вложенных ключей с помощью array_walk_recursive

Собираем все ключи многомерного массива и сравниваем с эталонным списком.

Пример
function getAllKeys(array $array, string $prefix = '') : array {
    $keys = [];
    foreach ($array as $key => $value) {
        $fullKey = $prefix ? $prefix . '.' . $key : $key;
        $keys[] = $fullKey;
        if (is_array($value)) {
            $keys = array_merge($keys, getAllKeys($value, $fullKey));
        }
    }
    return $keys;
}

$data = ['user' => ['name' => 'Alice', 'profile' => ['age' => 30]]];
$requiredPaths = ['user.name', 'user.profile.age', 'user.email'];
$existingPaths = getAllKeys($data);
$missing = array_diff($requiredPaths, $existingPaths);
echo 'Отсутствуют: ' . implode(', ', $missing);
Отсутствуют: user.email

Пример 6. Использование nullsafe-оператора в PHP 8 для объектов

Хотя оператор ?-> предназначен для объектов, он удобен при последовательной проверке глубоких структур. Для массивов используйте ??.

Пример
$config = ['database' => ['host' => 'localhost', 'port' => 3306]];
$host = $config['database']['host'] ?? null;
$port = $config['database']['port'] ?? null;
if ($host && $port) {
    echo "Соединение с $host:$port возможно.";
}
Соединение с localhost:3306 возможно.

Пример 7. Проверка всех значений на соответствие регулярному выражению

Комбинация array_filter с preg_match.

Пример
$emails = ['alice@example.com', 'bob@test', 'carol@site.com'];
$valid = array_filter($emails, function($email) {
    return preg_match('/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', $email);
});
if (count($valid) === count($emails)) {
    echo 'Все адреса корректны';
} else {
    echo 'Невалидные адреса: ' . implode(', ', array_diff_key($emails, $valid));
}
Невалидные адреса: bob@test

PHP has many (проверка множества) - comments

En
Php has many (php)