Commit ee739e842b5 for php

commit ee739e842b5b250a03cd3e48f24e4e315dd331c2
Author: Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
Date:   Mon Sep 28 15:36:16 2026 +0200

    ext/standard: enforce max_filter_count (#23344)

    Using more than 16 filters in a php://filter URL was deprecated in 8.6, and will fail in PHP 8.7.

    RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains

diff --git a/NEWS b/NEWS
index a6e87d987ca..da657a2611a 100644
--- a/NEWS
+++ b/NEWS
@@ -21,5 +21,7 @@ PHP                                                                        NEWS
 - Standard:
   . Improved performance of array_splice() when inserting without removing
     elements. (mehmetcansahin)
+  . Enforce max_filter_count: limit the number of filters that can be chained
+    in a php://filter URL. (Sjoerd Langkemper)

 <<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
diff --git a/UPGRADING b/UPGRADING
index 8c9bc4dac3f..5d551005042 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -19,6 +19,11 @@ PHP 8.7 UPGRADE NOTES
 1. Backward Incompatible Changes
 ========================================

+- Standard:
+  . The number of filters that can be chained in a php://filter URL is limited
+    to 16 by default. Set the stream context option max_filter_count to change
+    this.
+
 ========================================
 2. New Features
 ========================================
diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c
index cca9445801f..5d24c30727f 100644
--- a/ext/standard/php_fopen_wrapper.c
+++ b/ext/standard/php_fopen_wrapper.c
@@ -152,12 +152,10 @@ static zend_result php_stream_apply_filter_list(php_stream *stream, char *filter
 	php_stream_filter *temp_filter;

 	zend_long max_filter_count = max_filter_count_default;
-	bool max_filter_count_configured = false;
 	if (context != NULL) {
 		zval *option_val = php_stream_context_get_option(context, "filter", "max_filter_count");
 		if (option_val) {
 			max_filter_count = zval_get_long(option_val);
-			max_filter_count_configured = true;
 		}
 	}

@@ -167,13 +165,7 @@ static zend_result php_stream_apply_filter_list(php_stream *stream, char *filter
 		zend_long write_count = write_chain ? stream->writefilters.num_filters : 0;

 		if (read_count == max_filter_count || write_count == max_filter_count) {
-			if (max_filter_count_configured) {
-				return FAILURE;
-			} else {
-				// No max_filter_count configured; raise deprecation error if over default
-				zend_error(E_DEPRECATED, "Using more than " ZEND_LONG_FMT " filters in a php://filter URL is deprecated, "
-					"set this limit using the stream context option max_filter_count, or use stream_filter_append", max_filter_count_default);
-			}
+			return FAILURE;
 		}

 		php_url_decode(p, strlen(p));
diff --git a/ext/standard/tests/filters/max_filter_chain.phpt b/ext/standard/tests/filters/max_filter_chain.phpt
index b93407eee97..40a0efb6b51 100644
--- a/ext/standard/tests/filters/max_filter_chain.phpt
+++ b/ext/standard/tests/filters/max_filter_chain.phpt
@@ -90,24 +90,30 @@ function createFilterChains($n, $resource) {
 int(1)
 # file_get_contents on 17 filters

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-string(9) "SEVENTEEN"
+Warning: file_get_contents(): Failed to open stream: too many filters in %s on line %d
+bool(false)

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-string(9) "SEVENTEEN"
+Warning: file_get_contents(): Failed to open stream: too many filters in %s on line %d
+bool(false)

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-string(9) "SEVENTEEN"
+Warning: file_get_contents(): Failed to open stream: too many filters in %s on line %d
+bool(false)
 # include on 17 filters

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-int(1)
+Warning: include(): Failed to open stream: too many filters in %s on line %d

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-int(1)
+Warning: include(): Failed opening %s
+bool(false)

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-int(1)
+Warning: include(): Failed to open stream: too many filters in %s on line %d
+
+Warning: include(): Failed opening %s
+bool(false)
+
+Warning: include(): Failed to open stream: too many filters in %s on line %d
+
+Warning: include(): Failed opening %s
+bool(false)
 # file_get_contents on 3 filters, max_filter_count=2

 Warning: file_get_contents(): Failed to open stream: too many filters in %s on line %d
@@ -124,18 +130,18 @@ function createFilterChains($n, $resource) {
 string(8) "NINETEEN"
 # warning is only given once, even when we add two filters over the limit

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-string(8) "EIGHTEEN"
+Warning: file_get_contents(): Failed to open stream: too many filters in %s on line %d
+bool(false)

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-string(8) "EIGHTEEN"
+Warning: file_get_contents(): Failed to open stream: too many filters in %s on line %d
+bool(false)

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-string(8) "EIGHTEEN"
+Warning: file_get_contents(): Failed to open stream: operation failed in %s on line %d
+bool(false)
 # warn on too many write filters, even when number of read filters is OK

-Deprecated: Using more than 16 filters in a php://filter URL is deprecated, set this limit using the stream context option max_filter_count, or use stream_filter_append in %smax_filter_chain.php on line %d
-bool(true)
+Warning: fopen(): Failed to open stream: too many filters in %s on line %d
+bool(false)
 # setting max_filter_count to -1 disables warning
 string(6) "TWENTY"
 string(6) "TWENTY"