diff --git a/NEWS b/NEWS index 3564ba75e24..84bbf47ee39 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ PHP NEWS - Standard: . Added $before_needle argument to strrchr(). (HypeMC) + . Fixed GH-11982 (str_getcsv returns null byte for unterminated enclosure). + (Jakub Zelenka) 17 Aug 2023, PHP 8.3.0beta3 diff --git a/UPGRADING b/UPGRADING index 8464356a262..42059a6e090 100644 --- a/UPGRADING +++ b/UPGRADING @@ -352,6 +352,9 @@ PHP 8.3 UPGRADE NOTES got silently ignored and the number got rounded to zero decimal places. . The $before_needle argument added to strrchr() which works in the same way like its counterpart in strstr() or stristr(). + . str_getcsv() and fgetcsv() return empty string instead of a string with + a single zero byte for the last field which contains only unterminated + enclosure. ======================================== 6. New Functions diff --git a/ext/standard/file.c b/ext/standard/file.c index 5e835239a8c..edaba57748c 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -2017,7 +2017,14 @@ PHPAPI HashTable *php_fgetcsv(php_stream *stream, char delimiter, char enclosure memcpy(tptr, line_end, line_end_len); tptr += line_end_len; + /* nothing can be fetched if stream is NULL (e.g. str_getcsv()) */ if (stream == NULL) { + /* the enclosure is unterminated */ + if (bptr > limit) { + /* if the line ends with enclosure, we need to go back by + * one character so the \0 character is not copied. */ + --bptr; + } goto quit_loop_2; } @@ -2028,6 +2035,11 @@ PHPAPI HashTable *php_fgetcsv(php_stream *stream, char delimiter, char enclosure * assign all the data from the start of * the enclosure to end of data to the * last element */ + if (bptr > limit) { + /* if the line ends with enclosure, we need to go back by + * one character so the \0 character is not copied. */ + --bptr; + } goto quit_loop_2; } diff --git a/ext/standard/tests/file/fgetcsv_variation33.phpt b/ext/standard/tests/file/fgetcsv_variation33.phpt new file mode 100644 index 00000000000..7dfb1437e9e --- /dev/null +++ b/ext/standard/tests/file/fgetcsv_variation33.phpt @@ -0,0 +1,29 @@ +--TEST-- +fgetcsv() with unterminated enclosure at the end of file +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + string(5) "cell1" + [1]=> + string(5) "cell2" +} +array(2) { + [0]=> + string(5) "cell1" + [1]=> + string(0) "" +} diff --git a/ext/standard/tests/strings/gh11982.phpt b/ext/standard/tests/strings/gh11982.phpt new file mode 100644 index 00000000000..9b500a63c93 --- /dev/null +++ b/ext/standard/tests/strings/gh11982.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-11982 (str_getcsv returns null byte for unterminated quoted string) +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + string(0) "" +} +array(2) { + [0]=> + string(5) "field" + [1]=> + string(0) "" +} +array(2) { + [0]=> + string(0) "" + [1]=> + string(1) "a" +} +