PHP 8.5: Filter: Выбрасывание исключений при сбоях валидации

В PHP 8.5 функции filter_* могут выбрасывать исключения при ошибках валидации. Рассказываем, как работает новый флаг FILTER_THROW_ON_FAILURE и как использовать его на практике.

Расширение filter в PHP давно используется для быстрой и безопасной валидации входных данных. Однако до выхода PHP 8.5 этот механизм имел один ощутимый недостаток: функции filter_* возвращали false или null, и разработчику приходилось вручную генерировать исключения, усложняя обработку ошибок.

В PHP 8.5 появилось важное нововведение в filter — флаг FILTER_THROW_ON_FAILURE. Это изменение делает валидацию более предсказуемой, улучшает контроль ошибок и упрощает код. Ниже разберём, как работает новый флаг, чем он отличается от предыдущих подходов и какие особенности появились в PHP 8.5.

Что изменилось в PHP 8.5: новое в filter

Ранее функции filter_var() и другие filter_* возвращали false при провале валидации. С флагом FILTER_NULL_ON_FAILURE можно было вернуть null. Теперь в PHP 8.5 добавлен ещё один вариант поведения — выбрасывание исключений при ошибке валидации.

Пример стандартного поведения:

filter_var('foobar', FILTER_VALIDATE_EMAIL); // false
filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE); // null

Новый флаг добавляет возможность выброса исключения сразу при сбое:

filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_THROW_ON_FAILURE);
// Filter\FilterFailedException: filter validation failed: filter validate_email not satisfied by 'foobar'.

Важно: новое поведение касается только FILTER_VALIDATE_* — фильтры FILTER_SANITIZE_* не изменились.

Новый флаг FILTER_THROW_ON_FAILURE

Флаг FILTER_THROW_ON_FAILURE позволяет отказаться от ручной проверки результата после вызова filter_var() и сразу получить чёткое исключение Filter\FilterFailedException.

filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_THROW_ON_FAILURE); // выбрасывает исключение Filter\FilterFailedException
// Filter\FilterFailedException: filter validation failed: filter validate_email not satisfied by 'foobar'.

Флаг особенно полезен в функциях, где важно мгновенно прерывать выполнение при недопустимом значении — например, при обработке внешних данных API.

Не может использоваться с FILTER_NULL_ON_FAILURE

Попытка использовать оба флага одновременно приведёт к исключению ValueError:

filter_var('1', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE | FILTER_NULL_ON_FAILURE);

// ValueError: filter_var(): Argument #3 ($options) cannot use both FILTER_NULL_ON_FAILURE and FILTER_THROW_ON_FAILURE.

Следует выбирать один из двух флагов FILTER_NULL_ON_FAILURE или FILTER_THROW_ON_FAILURE.

Сообщения об ошибках валидации

Теперь исключение содержит подробное сообщение:

filter_var('foobar', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
// filter validation failed: filter int not satisfied by 'foobar'.
filter_var('', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
// filter validation failed: filter int not satisfied by ''.

Полезно при логировании и отладке.

Список имён валидации можно получить с помощью функции filter_list. Константу фильтра, используемую в функции filter_var, можно получить с помощью функции filter_id.

Валидация массива

При обработке массивов через filter_var_array() или filter_input_array() исключение не указывает ключ массива — только общее сообщение о провале валидации:

$data = [
'myNumber' => '15',
];

$filters = [
'component' => [
'filter' => FILTER_VALIDATE_INT,
'flags' => FILTER_THROW_ON_FAILURE,
'options' => [
'min_range' => 1,
'max_range' => 10,
],
],
];

filter_var_array($data, $filters);

// Filter\FilterFailedException: filter validation failed: filter int not satisfied by '15'.

Валидация с FILTER_CALLBACK

Флаг FILTER_THROW_ON_FAILURE не влияет на FILTER_CALLBACK.

Но сама callback-функция может выбросить исключение:

filter_var(
'hello',
FILTER_CALLBACK,
[
'flags' => FILTER_THROW_ON_FAILURE,
'options' => static function(string $value): void {
if (strlen($value) > 3) {
throw new Filter\FilterFailedException('Value must not be longer than 3 bytes');
}
},
]
);

// Filter\FilterFailedException: Value must not be longer than 3 bytes

Это позволяет реализовать кастомные сценарии валидации.

Валидация объектов

Если объект не реализует метод __toString(), filter сообщит об ошибке:

filter_var(new stdClass(), FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);

// Filter\FilterFailedException: filter validation failed: object of type stdClass has no __toString() method.

Валидация с FILTER_REQUIRE_ARRAY

При использовании флага FILTER_REQUIRE_ARRAY передача значения, не являющегося массивом, приводит к сбою валидации, если передаваемое значение не является массивом:

filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_REQUIRE_ARRAY);
// false

Когда флаг FILTER_THROW_ON_FAILURE передаётся вместе с FILTER_REQUIRE_ARRAY, сообщение об исключении соответствующим образом указывает на ошибку:

filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_REQUIRE_ARRAY | FILTER_THROW_ON_FAILURE);

// Filter\FilterFailedException: filter validation failed: not an array (got string).

Новые классы исключений

В Filter появились два новых класса:

namespace Filter {
class FilterException extends \Exception {}
class FilterFailedException extends FilterException {}
}

Класс исключений Filter\FilterException

Новый класс исключений Filter\FilterException предназначен для использования в качестве родительского класса для всех исключений, выбрасываемых исключением Filter.

В данный момент нет случаев, когда исключение Filter\FilterException выбрасывается напрямую.

Класс исключений Filter\FilterFailedException

Класс исключений Filter\FilterFailedException расширяет класс Filter\FilterException и используется во всех исключениях, выбрасываемых в результате применения правила FILTER_THROW_ON_FAILURE.

Влияние на обратную совместимость

Функциональность нельзя перенести на старые версии PHP, так как поведение встроенных функций filter_* изменить нельзя.

Если проекту нужно десериализовать или генерировать исключения Filter\FilterException и Filter\FilterFailedException , классы можно объявить вручную в в пользовательском пространстве PHP — но это только эмуляция, не полноценная замена.

Заключение

Флаг FILTER_THROW_ON_FAILURE — одно из практичных и полезных изменений в PHP 8.5. Он делает работу с валидацией проще, безопаснее и чище:

Если вы активно используете filter_var() или filter_input_array(), стоит рассмотреть переход на новый подход уже сейчас.

Комментарии


Дополнительные материалы

Предыдущая Статья

PHP 8.5: Оператор pipe (|>) — примеры, ограничения, руководство

Следующая Статья

PHP 8.5: Новая INI-директива max_memory_limit