Skip to content

Commit aff46d7

Browse files
committedAug 28, 2023
Fix GH-11982: str_getcsv returns null byte for unterminated quoted string
Closes GH-12047
1 parent b07a2d4 commit aff46d7

File tree

5 files changed

+72
-0
lines changed

5 files changed

+72
-0
lines changed
 

‎NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ PHP NEWS
1717

1818
- Standard:
1919
. Added $before_needle argument to strrchr(). (HypeMC)
20+
. Fixed GH-11982 (str_getcsv returns null byte for unterminated enclosure).
21+
(Jakub Zelenka)
2022

2123
17 Aug 2023, PHP 8.3.0beta3
2224

‎UPGRADING

+3
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@ PHP 8.3 UPGRADE NOTES
352352
got silently ignored and the number got rounded to zero decimal places.
353353
. The $before_needle argument added to strrchr() which works in the same way
354354
like its counterpart in strstr() or stristr().
355+
. str_getcsv() and fgetcsv() return empty string instead of a string with
356+
a single zero byte for the last field which contains only unterminated
357+
enclosure.
355358

356359
========================================
357360
6. New Functions

‎ext/standard/file.c

+12
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,14 @@ PHPAPI HashTable *php_fgetcsv(php_stream *stream, char delimiter, char enclosure
20172017
memcpy(tptr, line_end, line_end_len);
20182018
tptr += line_end_len;
20192019

2020+
/* nothing can be fetched if stream is NULL (e.g. str_getcsv()) */
20202021
if (stream == NULL) {
2022+
/* the enclosure is unterminated */
2023+
if (bptr > limit) {
2024+
/* if the line ends with enclosure, we need to go back by
2025+
* one character so the \0 character is not copied. */
2026+
--bptr;
2027+
}
20212028
goto quit_loop_2;
20222029
}
20232030

@@ -2028,6 +2035,11 @@ PHPAPI HashTable *php_fgetcsv(php_stream *stream, char delimiter, char enclosure
20282035
* assign all the data from the start of
20292036
* the enclosure to end of data to the
20302037
* last element */
2038+
if (bptr > limit) {
2039+
/* if the line ends with enclosure, we need to go back by
2040+
* one character so the \0 character is not copied. */
2041+
--bptr;
2042+
}
20312043
goto quit_loop_2;
20322044
}
20332045

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
fgetcsv() with unterminated enclosure at the end of file
3+
--FILE--
4+
<?php
5+
$contents = <<<EOS
6+
"cell1","cell2"
7+
"cell1","
8+
EOS;
9+
$stream = fopen('php://memory', 'w+');
10+
fwrite($stream, $contents);
11+
rewind($stream);
12+
while (($data = fgetcsv($stream)) !== false) {
13+
var_dump($data);
14+
}
15+
fclose($stream);
16+
?>
17+
--EXPECT--
18+
array(2) {
19+
[0]=>
20+
string(5) "cell1"
21+
[1]=>
22+
string(5) "cell2"
23+
}
24+
array(2) {
25+
[0]=>
26+
string(5) "cell1"
27+
[1]=>
28+
string(0) ""
29+
}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
GH-11982 (str_getcsv returns null byte for unterminated quoted string)
3+
--FILE--
4+
<?php
5+
var_dump(str_getcsv('"'));
6+
var_dump(str_getcsv('"field","'));
7+
var_dump(str_getcsv('"","a'));
8+
?>
9+
--EXPECT--
10+
array(1) {
11+
[0]=>
12+
string(0) ""
13+
}
14+
array(2) {
15+
[0]=>
16+
string(5) "field"
17+
[1]=>
18+
string(0) ""
19+
}
20+
array(2) {
21+
[0]=>
22+
string(0) ""
23+
[1]=>
24+
string(1) "a"
25+
}
26+

0 commit comments

Comments
 (0)