diff --git a/NEWS b/NEWS index b7cc259571133..1c4e80e65e775 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.21 +- SPL: + . Fixed bug GH-19942 (iterator_count() on an SplFileObject looping + infinitely). (alexandre-daubois) 09 Apr 2026, PHP 8.4.20 diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index f83ea9f84acaf..1cb91bbe531ee 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -2224,9 +2224,17 @@ PHP_METHOD(SplFileObject, next) RETURN_THROWS(); } + bool had_current_line = (intern->u.file.current_line != NULL || !Z_ISUNDEF(intern->u.file.current_zval)); spl_filesystem_file_free_line(intern); if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) { spl_filesystem_file_read_line(ZEND_THIS, intern, true); + } else if (!had_current_line) { + /* If there was no current line before, we need to advance the stream position + * for iterator_count() and other iterator operations to work correctly. + * Read and immediately discard a line. */ + if (spl_filesystem_file_read_line(ZEND_THIS, intern, true) == SUCCESS) { + spl_filesystem_file_free_line(intern); + } } intern->u.file.current_line_num++; } /* }}} */ diff --git a/ext/spl/tests/SplFileObject_iterator_count_empty_file.phpt b/ext/spl/tests/SplFileObject_iterator_count_empty_file.phpt new file mode 100644 index 0000000000000..ac9075c8c7527 --- /dev/null +++ b/ext/spl/tests/SplFileObject_iterator_count_empty_file.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-19942 (iterator_count() on an empty SplFileObject should return 0) +--FILE-- + +--EXPECT-- +int(1) diff --git a/ext/spl/tests/SplFileObject_iterator_count_non_empty_file.phpt b/ext/spl/tests/SplFileObject_iterator_count_non_empty_file.phpt new file mode 100644 index 0000000000000..3e6298c13e7c0 --- /dev/null +++ b/ext/spl/tests/SplFileObject_iterator_count_non_empty_file.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-19942 (iterator_count() on a non-empty SplFileObject should return correct count) +--FILE-- + +--EXPECT-- +int(3) diff --git a/ext/spl/tests/SplFileObject_non_seekable_stream.phpt b/ext/spl/tests/SplFileObject_non_seekable_stream.phpt new file mode 100644 index 0000000000000..2c8d039360a09 --- /dev/null +++ b/ext/spl/tests/SplFileObject_non_seekable_stream.phpt @@ -0,0 +1,11 @@ +--TEST-- +SplFileObject::valid() with non-seekable streams should not hang +--FILE-- +valid()); +?> +--STDIN-- + +--EXPECT-- +bool(true)