Skip to content

Commit 9a530a1

Browse files
authored
Fix GH-21742: SplFileObject::fgets() throws at EOF in eof/fgets loop (#21853)
The SplFileObject iterator-desync fix in 08dad09 made spl_filesystem_file_read_ex throw "Cannot read from file" on the NULL-buffer path. SplFileObject::fgets() now throws inside the documented while (!$spl->eof()) $spl->fgets() idiom, because eof() returns false until a read attempt returns zero bytes. Keep the stricter semantics for next(), seek(), current(), fscanf(). Narrow fgets() to silent=true and return empty string on FAILURE, restoring the PHP-8.5 contract. Fixes GH-21742
1 parent c23efeb commit 9a530a1

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

ext/spl/spl_directory.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2099,7 +2099,11 @@ PHP_METHOD(SplFileObject, fgets)
20992099
spl_filesystem_file_free_line(intern);
21002100
intern->u.file.current_line_num++;
21012101
} else {
2102-
if (spl_filesystem_file_read_ex(intern, /* silent */ false, /* line_add */ 1, /* csv */ false) == FAILURE) {
2102+
if (spl_filesystem_file_read_ex(intern, /* silent */ true, /* line_add */ 1, /* csv */ false) == FAILURE) {
2103+
if (php_stream_eof(intern->u.file.stream)) {
2104+
RETURN_EMPTY_STRING();
2105+
}
2106+
spl_filesystem_file_cannot_read(intern);
21032107
RETURN_THROWS();
21042108
}
21052109
RETVAL_STR_COPY(intern->u.file.current_line);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
GH-21742 (SplFileObject::fgets() throws at EOF in while (!$spl->eof()) loop)
3+
--FILE--
4+
<?php
5+
$file = tempnam(sys_get_temp_dir(), 'spl');
6+
file_put_contents($file, "Line 0\nLine 1\nLine 2\nLine 3\nLine 4\n");
7+
8+
$spl = new SplFileObject($file, 'r');
9+
while (!$spl->eof()) {
10+
echo $spl->fgets();
11+
}
12+
echo "clean exit\n";
13+
14+
$empty = tempnam(sys_get_temp_dir(), 'spl');
15+
file_put_contents($empty, '');
16+
$spl2 = new SplFileObject($empty, 'r');
17+
$iter = 0;
18+
while (!$spl2->eof()) {
19+
$iter++;
20+
$spl2->fgets();
21+
if ($iter > 3) break;
22+
}
23+
echo "empty-file iters=$iter\n";
24+
25+
unlink($file);
26+
unlink($empty);
27+
?>
28+
--EXPECT--
29+
Line 0
30+
Line 1
31+
Line 2
32+
Line 3
33+
Line 4
34+
clean exit
35+
empty-file iters=1

0 commit comments

Comments
 (0)