Commit f74c740c6ca for php.net
commit f74c740c6ca6e5e1a8ebd1d275c49ec66214fd70
Author: Louis-Arnaud <la.catoire@gmail.com>
Date: Mon Aug 31 14:21:14 2026 +0200
ext/filter: Narrow the return type of filter_var_array() to array|false (#23403)
The stub declares array|false|null, but null is unreachable. The function
has two exits: RETURN_FALSE for an unknown filter id, and the array handler,
which establishes an array on both of its branches before doing anything
else. Its remaining exits throw.
The sibling filter_input_array() is declared identically and does return
null, deliberately, because its source superglobal may not exist. That case
has no equivalent here, where the source is a required array parameter.
null remains an ordinary element value in the returned array, which is
likely where the wider union came from; that is the value type, not the
return type.
diff --git a/UPGRADING b/UPGRADING
index 5a5cafc0234..b6bf7cd0249 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -639,6 +639,12 @@ PHP 8.6 UPGRADE NOTES
5. Changed Functions
========================================
+- Filter:
+ . filter_var_array() return type has been narrowed from array|false|null to
+ array|false. The function always establishes an array before filtering, so
+ null was never returned. filter_input_array() is unaffected: it still
+ returns null when the requested superglobal does not exist.
+
- GMP:
. gmp_fact() now throws a ValueError if $num does not fit into an unsigned
long.
diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h
index cfe758db796..b14de7888a9 100644
--- a/Zend/Optimizer/zend_func_infos.h
+++ b/Zend/Optimizer/zend_func_infos.h
@@ -95,7 +95,7 @@ static const func_info_t func_infos[] = {
F1("finfo_buffer", MAY_BE_STRING|MAY_BE_FALSE),
F1("mime_content_type", MAY_BE_STRING|MAY_BE_FALSE),
F1("filter_input_array", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_FALSE|MAY_BE_NULL),
- F1("filter_var_array", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_FALSE|MAY_BE_NULL),
+ F1("filter_var_array", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_FALSE),
F1("filter_list", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
F1("ftp_raw", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_NULL),
F1("ftp_nlist", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE),
diff --git a/ext/filter/filter.stub.php b/ext/filter/filter.stub.php
index 4332f9261e9..16a6fe5c940 100644
--- a/ext/filter/filter.stub.php
+++ b/ext/filter/filter.stub.php
@@ -310,7 +310,7 @@ function filter_var(mixed $value, int $filter = FILTER_DEFAULT, array|int $optio
function filter_input_array(int $type, array|int $options = FILTER_DEFAULT, bool $add_empty = true): array|false|null {}
/** @refcount 1 */
-function filter_var_array(array $array, array|int $options = FILTER_DEFAULT, bool $add_empty = true): array|false|null {}
+function filter_var_array(array $array, array|int $options = FILTER_DEFAULT, bool $add_empty = true): array|false {}
/**
* @return array<int, string>
diff --git a/ext/filter/filter_arginfo.h b/ext/filter/filter_arginfo.h
index 4e24ede41a6..89164736299 100644
Binary files a/ext/filter/filter_arginfo.h and b/ext/filter/filter_arginfo.h differ