PHP 8.5: Filter: Support to throw exceptions on validation failures
The filter extension provides a series of functions to validate a given variable or a specified super global against a list of provided validation patterns and parameters.
When the validation fails, the filter_* functions return false, which can be configured to return null instead of false, by passing FILTER_NULL_ON_FAILURE flag to the filter_* functions.
In PHP 8.5, the filter_* functions can be configured to throw an exception when the validation fails. This simplifies the code structure for the callers that catch validation failures and throw exceptions on their own.
filter_var('foobar', FILTER_VALIDATE_EMAIL); // false
filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE); // null
This functionality only affects FILTER_VALIDATE_* filters. Functionality of FILTER_SANITIZE_* filters remains unaffected.
New FILTER_THROW_ON_FAILURE flag
In PHP 8.5, the filter_* functions accept a new FILTER_THROW_ON_FAILURE flag, which throws an exception if the validation fails:
filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_THROW_ON_FAILURE); // throws a Filter\FilterFailedException
Filter\FilterFailedException: filter validation failed: filter validate_email not satisfied by 'foobar'.
Cannot be used with FILTER_NULL_ON_FAILURE
FILTER_THROW_ON_FAILURE and FILTER_NULL_ON_FAILURE flags must not be used at the same time, and result in a ValueError exception even if the value is valid.
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.
Validation error messages
The exceptions thrown will contain the name of the filter, along with input value does not satisfy the validation.
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 ''.
A list of the validation names can be obtained by filter_list function. The filter constant to be used in the filter_var function can be obtained by the filter_id function.
Validating an array
When validating an array of values using the filter_var_array or filter_input_array functions, it does not mention the key of the array which failed the validation:
$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'.
Validating with FILTER_CALLBACK
The FILTER_CALLBACK validation, when used with the FILTER_THROW_ON_FAILURE flag, has no effect.
However, it is possible for the callback function to throw an exception on its own if necessary:
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
Validating objects
Soft type errors such as passing an object that does not implement __toString() method throws with an error message:
filter_var(new stdClass(), FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
Filter\FilterFailedException: filter validation failed: object of type stdClass has no __toString() method.
Validating with FILTER_REQUIRE_ARRAY
When the FILTER_REQUIRE_ARRAY flag is used, passing a non-array value fails the validation if the input value is not an array:
filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_REQUIRE_ARRAY);
// false
When the FILTER_THROW_ON_FAILURE flag is passed with FILTER_REQUIRE_ARRAY, the exception message indicates the error appropriately:
filter_var('foobar', FILTER_VALIDATE_EMAIL, FILTER_REQUIRE_ARRAY | FILTER_THROW_ON_FAILURE);
Filter\FilterFailedException: filter validation failed: not an array (got string).
New Exception Classes
As part of the changes, the filter extension introduces two new exception classes in the Filter namespace:
namespace Filter {
class FilterException extends \Exception {}
class FilterFailedException extends FilterException {}
}
Filter\FilterException Exception class
The new Filter\FilterException exception class is meant to be the parent class for all exceptions thrown by the Filter exception.
Currently, there are no cases that throw a Filter\FilterException exception directly.
Filter\FilterFailedException Exception class
Filter\FilterFailedException exception class extends Filter\FilterException, and is used in all exceptions thrown due to the FILTER_THROW_ON_FAILURE enforcement.
Backward Compatibility Impact
It is not possible to port this functionality to older PHP versions because the filter_* functions are built-in PHP functions that must support the exception behavior.
In case applications need to unserialize Filter\FilterException and Filter\FilterFailedException exceptions or to throw these exceptions (while it may not be the best idea), it is possible to declare these exception classes in user-land PHP.