[php-src] master: Balance stream error ops on file_put_contents LOCK_EX URL reject (#23186)

Ilia Alshanetsky via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Committer: GitHub (web-flow)
Pusher: iliaal
Date: 2026-08-09T19:44:21-04:00

Commit: https://github.com/php/php-src/commit/0b7f50ad2e48161c3d4e8b3ddd6d547ea72ed691
Raw diff: https://github.com/php/php-src/commit/0b7f50ad2e48161c3d4e8b3ddd6d547ea72ed691.diff

Balance stream error ops on file_put_contents LOCK_EX URL reject (#23186)

file_put_contents begins a stream error operation then returns early
when LOCK_EX is used with a non-file URL, without ending the operation.
After ~1000 such failures the depth limit is exhausted and later stream
ops fail. Call php_stream_error_operation_end before that RETURN_FALSE.

Closes GH-23186

Changed paths:
  A  ext/standard/tests/file/file_put_contents_lock_ex_url.phpt
  M  NEWS
  M  ext/standard/file.c


Diff:

diff --git a/NEWS b/NEWS
index 39d5a948e26f..b091ea276142 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@ PHP                                                                        NEWS
 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 ?? ??? ????, PHP 8.6.0beta1
 
+- Streams:
+  . Fixed file_put_contents() LOCK_EX early return leaking stream error
+    operation depth. (iliaal)
+
 - Core:
   . Changed run-tests.php to run test subprocesses without a shell where
     possible. (NickSdot)
diff --git a/ext/standard/file.c b/ext/standard/file.c
index fd6f578e4ef0..d6a8b9f1d0ea 100644
--- a/ext/standard/file.c
+++ b/ext/standard/file.c
@@ -474,6 +474,7 @@ PHP_FUNCTION(file_put_contents)
 		if (php_memnstr(filename, "://", sizeof("://") - 1, filename + filename_len)) {
 			if (strncasecmp(filename, "file://", sizeof("file://") - 1)) {
 				php_error_docref(NULL, E_WARNING, "Exclusive locks may only be set for regular files");
+				php_stream_error_operation_end(context);
 				RETURN_FALSE;
 			}
 		}
diff --git a/ext/standard/tests/file/file_put_contents_lock_ex_url.phpt b/ext/standard/tests/file/file_put_contents_lock_ex_url.phpt
new file mode 100644
index 000000000000..6bd346f39f46
--- /dev/null
+++ b/ext/standard/tests/file/file_put_contents_lock_ex_url.phpt
@@ -0,0 +1,20 @@
+--TEST--
+file_put_contents LOCK_EX on non-file URL balances stream error operation
+--INI--
+allow_url_fopen=1
+--FILE--
+<?php
+for ($i = 0; $i < 1005; $i++) {
+    @file_put_contents('http://127.0.0.1/x', 'data', LOCK_EX);
+}
+set_error_handler(function (int $errno, string $errstr): bool {
+    if (str_contains($errstr, 'Stream error operation depth exceeded')) {
+        echo "depth_exceeded\n";
+    }
+    return true;
+});
+file_put_contents('http://127.0.0.1/x', 'data', LOCK_EX);
+echo "done\n";
+?>
+--EXPECT--
+done
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.