Skip to content

Commit f5ae5ac

Browse files
authored
ext/standard: Throw ValueErrors in str_getcsv() for invalid inputs (#15365)
This was forgotten when adjusting the behaviour of other CSV functions
1 parent a4772a0 commit f5ae5ac

File tree

6 files changed

+46
-24
lines changed

6 files changed

+46
-24
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ PHP NEWS
9292
- fgetcsv()
9393
- str_getcsv()
9494
is now deprecated. (Girgias)
95+
. The str_getcsv() function now throws ValueErrors when the $separator and
96+
$enclosure arguments are not one byte long, or if the $escape is not one
97+
byte long or the empty string. This aligns the behaviour to be identical
98+
to that of fputcsv() and fgetcsv(). (Girgias)
9599

96100
- Streams:
97101
. Implemented GH-15155 (Stream context is lost when custom stream wrapper is

UPGRADING

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ PHP 8.4 UPGRADE NOTES
184184
PHP_ROUND_HALF_UP.
185185
. strcspn() with empty $characters now returns the length of the string instead
186186
of incorrectly stopping at the first NUL character. See GH-12592.
187+
. The str_getcsv() function now throws ValueErrors when the $separator and
188+
$enclosure arguments are not one byte long, or if the $escape is not one
189+
byte long or the empty string. This aligns the behaviour to be identical
190+
to that of fputcsv() and fgetcsv().
187191

188192
- Tidy:
189193
. Failures in the constructor now throw exceptions rather than emitting

ext/standard/string.c

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5446,23 +5446,36 @@ PHPAPI size_t php_strip_tags_ex(char *rbuf, size_t len, const char *allow, size_
54465446
PHP_FUNCTION(str_getcsv)
54475447
{
54485448
zend_string *str;
5449-
char delim = ',', enc = '"';
5449+
char delimiter = ',', enclosure = '"';
54505450
int escape = (unsigned char) '\\';
5451-
char *delim_str = NULL, *enc_str = NULL, *escape_str = NULL;
5452-
size_t delim_len = 0, enc_len = 0, escape_str_len = 0;
5451+
char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL;
5452+
size_t delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0;
54535453

54545454
ZEND_PARSE_PARAMETERS_START(1, 4)
54555455
Z_PARAM_STR(str)
54565456
Z_PARAM_OPTIONAL
5457-
Z_PARAM_STRING(delim_str, delim_len)
5458-
Z_PARAM_STRING(enc_str, enc_len)
5457+
Z_PARAM_STRING(delimiter_str, delimiter_str_len)
5458+
Z_PARAM_STRING(enclosure_str, enclosure_str_len)
54595459
Z_PARAM_STRING(escape_str, escape_str_len)
54605460
ZEND_PARSE_PARAMETERS_END();
54615461

5462-
delim = delim_len ? delim_str[0] : delim;
5463-
enc = enc_len ? enc_str[0] : enc;
5464-
5465-
// TODO ValueError for delimiter and enclosure string being longer than 1 byte
5462+
if (delimiter_str != NULL) {
5463+
/* Make sure that there is at least one character in string */
5464+
if (delimiter_str_len != 1) {
5465+
zend_argument_value_error(2, "must be a single character");
5466+
RETURN_THROWS();
5467+
}
5468+
/* use first character from string */
5469+
delimiter = delimiter_str[0];
5470+
}
5471+
if (enclosure_str != NULL) {
5472+
if (enclosure_str_len != 1) {
5473+
zend_argument_value_error(3, "must be a single character");
5474+
RETURN_THROWS();
5475+
}
5476+
/* use first character from string */
5477+
enclosure = enclosure_str[0];
5478+
}
54665479
if (escape_str != NULL) {
54675480
if (escape_str_len > 1) {
54685481
zend_argument_value_error(4, "must be empty or a single character");
@@ -5480,7 +5493,7 @@ PHP_FUNCTION(str_getcsv)
54805493
}
54815494
}
54825495

5483-
HashTable *values = php_fgetcsv(NULL, delim, enc, escape, ZSTR_LEN(str), ZSTR_VAL(str));
5496+
HashTable *values = php_fgetcsv(NULL, delimiter, enclosure, escape, ZSTR_LEN(str), ZSTR_VAL(str));
54845497
if (values == NULL) {
54855498
values = php_bc_fgetcsv_empty_line();
54865499
}

ext/standard/tests/strings/gh12151.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
GH-12151 (str_getcsv ending with escape zero segfualt)
33
--FILE--
44
<?php
5-
var_export(str_getcsv("y","","y","\000"));
5+
var_export(str_getcsv("y",",","y","\000"));
66
var_export(str_getcsv("\0yy","y","y","\0"));
77
?>
88
--EXPECTF--

ext/standard/tests/strings/str_getcsv_001.phpt

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ var_dump(str_getcsv('|f.|.|bar|.|-|-.|', '.', '|', '-'));
1616
print "-----\n";
1717
var_dump(str_getcsv('.foo..bar.', '.', '.', '.'));
1818
print "-----\n";
19-
var_dump(str_getcsv('.foo. .bar.', ' ', '.', '.'));
20-
print "-----\n";
2119
var_dump(str_getcsv('.foo . . bar .', ' ', '.', ''));
2220
print "-----\n";
2321
var_dump(str_getcsv('" "" "', ' '));
@@ -77,15 +75,6 @@ array(1) {
7775
string(7) "foo.bar"
7876
}
7977
-----
80-
81-
Deprecated: str_getcsv(): Passing a non-empty string to the $escape parameter is deprecated since 8.4 in %s on line %d
82-
array(2) {
83-
[0]=>
84-
string(3) "foo"
85-
[1]=>
86-
string(3) "bar"
87-
}
88-
-----
8978
array(2) {
9079
[0]=>
9180
string(5) "foo "

ext/standard/tests/strings/str_getcsv_errors.phpt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,23 @@ str_getcsv(): Invalid arguments
55

66
// string input[, string delimiter[, string enclosure[, string escape]]]
77
try {
8-
var_dump(str_getcsv('csv_string', ',', '"', 'enclosure'));
8+
var_dump(str_getcsv('csv_string', 'separator', '"', ''));
9+
} catch (Throwable $e) {
10+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
11+
}
12+
try {
13+
var_dump(str_getcsv('csv_string', ',', 'enclosure', ''));
14+
} catch (Throwable $e) {
15+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
16+
}
17+
try {
18+
var_dump(str_getcsv('csv_string', ',', '"', 'escape'));
919
} catch (Throwable $e) {
1020
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
1121
}
1222

1323
?>
1424
--EXPECT--
15-
ValueError: str_getcsv(): Argument #4 ($escape) must be empty or a single character
25+
ValueError: str_getcsv(): Argument #2 ($separator) must be a single character
26+
ValueError: str_getcsv(): Argument #3 ($enclosure) must be a single character
27+
ValueError: str_getcsv(): Argument #4 ($escape) must be empty or a single character

0 commit comments

Comments
 (0)