PHP 8.5: Filter: Выбрасывание исключений при сбоях валидации
Расширение 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 {}
}FilterException— базовый класс.FilterFailedException— используется приFILTER_THROW_ON_FAILURE.
Класс исключений 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(), стоит рассмотреть переход на новый подход уже сейчас.