Skip to content

Commit c5c9422

Browse files
committed
Fix #69181: READ_CSV|DROP_NEW_LINE drops newlines within fields
One may argue that `DROP_NEW_LINE` does not make sense in combination with `READ_CSV`, but without `DROP_NEW_LINE`, `SKIP_EMPTY` does not skip empty lines at all. We could fix that, but do not for BC reasons. Instead we no longer drop newlines in `spl_filesystem_file_read()` when reading CSV, but handle that in `spl_filesystem_file_read_csv()` by treating lines with only (CR)LF as being empty as well.
1 parent 8a79668 commit c5c9422

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

ext/spl/spl_directory.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,7 +2045,7 @@ static int spl_filesystem_file_read(spl_filesystem_object *intern, int silent) /
20452045
intern->u.file.current_line = estrdup("");
20462046
intern->u.file.current_line_len = 0;
20472047
} else {
2048-
if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_DROP_NEW_LINE)) {
2048+
if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_DROP_NEW_LINE) && !SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV)) {
20492049
if (line_len > 0 && buf[line_len - 1] == '\n') {
20502050
line_len--;
20512051
if (line_len > 0 && buf[line_len - 1] == '\r') {
@@ -2125,14 +2125,24 @@ static int spl_filesystem_file_call(spl_filesystem_object *intern, zend_function
21252125
spl_filesystem_file_call(intern, func_ptr, pass_num_args, return_value, arg2); \
21262126
} /* }}} */
21272127

2128+
static int is_csv_line_empty(spl_filesystem_object *intern) /* {{{ */
2129+
{
2130+
char *current_line = intern->u.file.current_line;
2131+
size_t current_line_len = intern->u.file.current_line_len;
2132+
return current_line_len == 0
2133+
|| (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV) && SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_DROP_NEW_LINE)
2134+
&& (current_line_len == 1 && current_line[0] == '\n')
2135+
|| current_line_len == 2 && current_line[0] == '\r' && current_line[1] == '\n');
2136+
} /* }}} */
2137+
21282138
static int spl_filesystem_file_read_csv(spl_filesystem_object *intern, char delimiter, char enclosure, int escape, zval *return_value) /* {{{ */
21292139
{
21302140
int ret = SUCCESS;
21312141
zval *value;
21322142

21332143
do {
21342144
ret = spl_filesystem_file_read(intern, 1);
2135-
} while (ret == SUCCESS && !intern->u.file.current_line_len && SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_SKIP_EMPTY));
2145+
} while (ret == SUCCESS && is_csv_line_empty(intern) && SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_SKIP_EMPTY));
21362146

21372147
if (ret == SUCCESS) {
21382148
size_t buf_len = intern->u.file.current_line_len;

ext/spl/tests/bug69181.phpt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
Bug #69181 (READ_CSV|DROP_NEW_LINE drops newlines within fields)
3+
--FILE--
4+
<?php
5+
$filename = __DIR__ . "/bug69181.csv";
6+
$csv = <<<CSV
7+
"foo\nbar\nbaz",qux
8+
9+
"foo\nbar\nbaz",qux
10+
CSV;
11+
12+
file_put_contents($filename, $csv);
13+
14+
$file = new SplFileObject($filename);
15+
$file->setFlags(SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE | SplFileObject::READ_CSV);
16+
var_dump(iterator_to_array($file));
17+
?>
18+
--EXPECT--
19+
array(2) {
20+
[0]=>
21+
array(2) {
22+
[0]=>
23+
string(11) "foo
24+
bar
25+
baz"
26+
[1]=>
27+
string(3) "qux"
28+
}
29+
[2]=>
30+
array(2) {
31+
[0]=>
32+
string(11) "foo
33+
bar
34+
baz"
35+
[1]=>
36+
string(3) "qux"
37+
}
38+
}
39+
--CLEAN--
40+
<?php
41+
@unlink(__DIR__ . "/bug69181.csv");
42+
?>

0 commit comments

Comments
 (0)