Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ PHP NEWS
brigade. (ndossche, Matthias Goergens, Jakub Zelenka)
. Fixed bug GH-24175 (Use-after-free when pclose() closes a stream from its
user filter). (Edmond)
. Fixed bug GH-12899 (copy() target is blank). (Kamil Tekiela)

19 Nov 2026, PHP 8.4.28

Expand Down
35 changes: 35 additions & 0 deletions ext/standard/tests/file/gh12899.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
GH-12899 (copy() target is blank in PHP 8.3.0)
--SKIPIF--
<?php
if (PHP_OS_FAMILY !== 'Linux') die('skip Linux only');
if (!is_readable('/proc/version')) die('skip /proc/version is not readable');
?>
--FILE--
<?php
/* procfs files report a size of 0, so copy_file_range() can return 0 for them */
$src = '/proc/version';
$dest = __DIR__ . '/gh12899_dest.txt';
$expected = file_get_contents($src);
var_dump(strlen($expected) > 0);

var_dump(copy($src, $dest));
var_dump(file_get_contents($dest) === $expected);

$in = fopen($src, 'r');
$out = fopen($dest, 'w');
var_dump(stream_copy_to_stream($in, $out) === strlen($expected));
fclose($in);
fclose($out);
var_dump(file_get_contents($dest) === $expected);
?>
--CLEAN--
<?php
@unlink(__DIR__ . '/gh12899_dest.txt');
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
6 changes: 3 additions & 3 deletions main/streams/streams.c
Original file line number Diff line number Diff line change
Expand Up @@ -1763,9 +1763,9 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de

/* there may be more data; continue copying using the fallback code below */
} else if (result == 0) {
/* end of file */
*len = haveread;
return SUCCESS;
/* Nothing was copied yet, but this does not prove end-of-file: some kernels return 0
* for sources that are not empty, e.g. procfs files or VirtualBox shared folders.
* Fall back to classic copying, which detects end-of-file reliably. */
} else if (result < 0) {
switch (errno) {
case EINVAL:
Expand Down
Loading