10000 Fix #42357: fputcsv() has an optional parameter for line endings · php/php-src@707caf9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 707caf9

Browse files
committed
Fix #42357: fputcsv() has an optional parameter for line endings
fputcsv does not terminate lines correctly as per RFC 41801[1]. After adding a new parameter fputcsv may now use a user defined line ending,. In order to maintain backwards compatibility fputcsv() still terminates lines with "\n" by default. Also fixes: #46367[2], #62770[3] Ref: #42357[4] [1] <https://tools.ietf.org/html/rfc4180> [2] <https://bugs.php.net/bug.php?id=46367> [3] <https://bugs.php.net/bug.php?id=62770> [4] <https://bugs.php.net/bug.php?id=42357>
1 parent 21594ed commit 707caf9

10 files changed

+95
-20
lines changed

ext/spl/spl_directory.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2384,15 +2384,16 @@ PHP_METHOD(SplFileObject, fputcsv)
23842384
spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS);
23852385
char delimiter = intern->u.file.delimiter, enclosure = intern->u.file.enclosure;
23862386
int escape = intern->u.file.escape;
2387-
char *delim = NULL, *enclo = NULL, *esc = NULL;
2388-
size_t d_len = 0, e_len = 0, esc_len = 0;
2387+
char *delim = NULL, *enclo = NULL, *esc = NULL, *eol = NULL;
2388+
size_t d_len = 0, e_len = 0, esc_len = 0, eol_len;
23892389
zend_long ret;
23902390
zval *fields = NULL;
23912391

2392-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|sss", &fields, &delim, &d_len, &enclo, &e_len, &esc, &esc_len) == SUCCESS) {
2392+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|ssss", &fields, &delim, &d_len, &enclo, &e_len, &esc, &esc_len, &eol, &eol_len) == SUCCESS) {
23932393

23942394
switch(ZEND_NUM_ARGS())
23952395
{
2396+
case 5:
23962397
case 4:
23972398
switch (esc_len) {
23982399
case 0:
@@ -2424,7 +2425,8 @@ PHP_METHOD(SplFileObject, fputcsv)
24242425
case 0:
24252426
break;
24262427
}
2427-
ret = php_fputcsv(intern->u.file.stream, fields, delimiter, enclosure, escape);
2428+
2429+
ret = php_fputcsv(intern->u.file.stream, fields, delimiter, enclosure, escape, eol);
24282430
if (ret < 0) {
24292431
RETURN_FALSE;
24302432
}

ext/spl/spl_directory.stub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public function fread(int $length) {}
209209
public function fgetcsv(string $separator = ",", string $enclosure = "\"", string $escape = "\\") {}
210210

211211
/** @return int|false */
212-
public function fputcsv(array $fields, string $separator = ",", string $enclosure = "\"", string $escape = "\\") {}
212+
public function fputcsv(array $fields, string $separator = ",", string $enclosure = "\"", string $escape = "\\", string $eol = "\n") {}
213213

214214
/** @return bool|null */
215215
public function setCsvControl(string $separator = ",", string $enclosure = "\"", string $escape = "\\") {}

ext/spl/spl_directory_arginfo.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: b4aa4816381e8380d5dc0c5f9b9969992a72f9ed */
2+
* Stub hash: 67a2f007e82dd2168cc2cc8021275b3899df5b6a */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SplFileInfo___construct, 0, 0, 1)
55
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
@@ -181,6 +181,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SplFileObject_fputcsv, 0, 0, 1)
181181
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\",\"")
182182
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enclosure, IS_STRING, 0, "\"\\\"\"")
183183
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, escape, IS_STRING, 0, "\"\\\\\"")
184+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, eol, IS_STRING, 0, "\"\\n\"")
184185
ZEND_END_ARG_INFO()
185186

186187
#define arginfo_class_SplFileObject_setCsvControl arginfo_class_SplFileObject_fgetcsv
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
SplFileObject::fputcsv() with user provided eol
3+
--FILE--
4+
<?php
5+
$data = [
6+
['aaa', 'bbb', 'ccc', 'dddd'],
7+
['123', '456', '789'],
8+
['"aaa"', '"bbb"'],
9+
];
10+
11+
$eol_chars = ['||', '|', '\n', "\n"];
12+
foreach ($eol_chars as $eol_char) {
13+
$file = new SplTempFileObject;
14+
foreach ($data as $record) {
15+
$file->fputcsv($record, ',', '"', '', $eol_char);
16+
}
17+
18+
$file->rewind();
19+
foreach ($file as $line) {
20+
echo $line;
21+
}
22+
23+
echo "\n";
24+
}
25+
?>
26+
--EXPECT--
27+
aaa,bbb,ccc,dddd||123,456,789||"""aaa""","""bbb"""||
28+
aaa,bbb,ccc,dddd|123,456,789|"""aaa""","""bbb"""|
29+
aaa,bbb,ccc,dddd\n123,456,789\n"""aaa""","""bbb"""\n
30+
aaa,bbb,ccc,dddd
31+
123,456,789
32+
"""aaa""","""bbb"""

ext/spl/tests/bug68479.phpt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ var_dump($params);
99

1010
?>
1111
--EXPECT--
12-
array(4) {
12+
array(5) {
1313
[0]=>
1414
object(ReflectionParameter)#2 (1) {
1515
["name"]=>
@@ -30,4 +30,9 @@ array(4) {
3030
["name"]=>
3131
string(6) "escape"
3232
}
33-
}
33+
[4]=>
34+
object(ReflectionParameter)#6 (1) {
35+
["name"]=>
36+
string(3) "eol"
37+
}
38+
}

ext/standard/basic_functions.stub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ function unlink(string $filename, $context = null): bool {}
857857
function file_put_contents(string $filename, mixed $data, int $flags = 0, $context = null): int|false {}
858858

859859
/** @param resource $stream */
860-
function fputcsv($stream, array $fields, string $separator = ",", string $enclosure = "\"", string $escape = "\\"): int|false {}
860+
function fputcsv($stream, array $fields, string $separator = ",", string $enclosure = "\"", string $escape = "\\", string $eol = "\n"): int|false {}
861861

862862
/** @param resource $stream */
863863
function fgetcsv($stream, ?int $length = null, string $separator = ",", string $enclosure = "\"", string $escape = "\\"): array|false {}

ext/standard/basic_functions_arginfo.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 4edb7cad23ccb051dbe267b3979e98892607c98f */
2+
* Stub hash: f0372e69edc8dcbdaf374ff5eaefcdcef0402021 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0)
55
ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0)
@@ -1328,6 +1328,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_fputcsv, 0, 2, MAY_BE_LONG|MAY_B
13281328
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\",\"")
13291329
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enclosure, IS_STRING, 0, "\"\\\"\"")
13301330
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, escape, IS_STRING, 0, "\"\\\\\"")
1331+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, eol, IS_STRING, 0, "\"\\n\"")
13311332
ZEND_END_ARG_INFO()
13321333

13331334
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_fgetcsv, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE)

ext/standard/file.c

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,16 +1801,17 @@ PHP_FUNCTION(fputcsv)
18011801
php_stream *stream;
18021802
zval *fp = NULL, *fields = NULL;
18031803
ssize_t ret;
1804-
char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL;
1805-
size_t delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0;
1806-
1807-
ZEND_PARSE_PARAMETERS_START(2, 5)
1804+
char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL, *eol_str = NULL;
1805+
size_t delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0, eol_str_len = 0;
1806+
1807+
ZEND_PARSE_PARAMETERS_START(2, 6)
18081808
Z_PARAM_RESOURCE(fp)
18091809
Z_PARAM_ARRAY(fields)
18101810
Z_PARAM_OPTIONAL
18111811
Z_PARAM_STRING(delimiter_str, delimiter_str_len)
18121812
Z_PARAM_STRING(enclosure_str, enclosure_str_len)
18131813
Z_PARAM_STRING(escape_str, escape_str_len)
1814+
Z_PARAM_STRING(eol_str, eol_str_len)
18141815
ZEND_PARSE_PARAMETERS_END();
18151816

18161817
if (delimiter_str != NULL) {
@@ -1848,16 +1849,16 @@ PHP_FUNCTION(fputcsv)
18481849

18491850
PHP_STREAM_TO_ZVAL(stream, fp);
18501851

1851-
ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char);
1852+
ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char, eol_str);
18521853
if (ret < 0) {
18531854
RETURN_FALSE;
18541855
}
18551856
RETURN_LONG(ret);
18561857
}
18571858
/* }}} */
18581859

1859-
/* {{{ PHPAPI size_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char) */
1860-
PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char)
1860+
/* {{{ PHPAPI size_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, char *eol_str) */
1861+
PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, const char *eol_str)
18611862
{
18621863
int count, i = 0;
18631864
size_t ret;
@@ -1905,8 +1906,12 @@ PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, cha
19051906
}
19061907
zend_tmp_string_release(tmp_field_str);
19071908
} ZEND_HASH_FOREACH_END();
1908-
1909-
smart_str_appendc(&csvline, '\n');
1909+
1910+
if (eol_str == NULL) {
1911+
smart_str_appendc(&csvline, '\n');
1912+
} else {
1913+
smart_str_appendl(&csvline, eol_str, strlen(eol_str));
1914+
}
19101915
smart_str_0(&csvline);
19111916

19121917
ret = php_stream_write(stream, ZSTR_VAL(csvline.s), ZSTR_LEN(csvline.s));

ext/standard/file.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ PHPAPI void php_flock_common(php_stream *stream, zend_long operation, uint32_t o
4949

5050
#define PHP_CSV_NO_ESCAPE EOF
5151
PHPAPI void php_fgetcsv(php_stream *stream, char delimiter, char enclosure, int escape_char, size_t buf_len, char *buf, zval *return_value);
52-
PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char);
52+
PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, 10560 const char *eol_str);
5353

5454
#define META_DEF_BUFSIZE 8192
5555

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
fputcsv() with user provided eol
3+
--FILE--
4+
<?php
5+
$data = [
6+
['aaa', 'bbb', 'ccc', 'dddd'],
7+
['123', '456', '789'],
8+
['"aaa"', '"bbb"'],
9+
];
10+
11+
$eol_chars = ['||', '|', '\n', "\n"];
12+
foreach ($eol_chars as $eol_char) {
13+
$stream = fopen('php://memory', 'w+');
14+
foreach ($data as $record) {
15+
fputcsv($stream, $record, ',', '"', '\\', $eol_char);
16+
}
17+
rewind($stream);
18+
echo stream_get_contents($stream), "\n";
19+
fclose($stream);
20+
}
21+
?>
22+
23+
--EXPECT--
24+
aaa,bbb,ccc,dddd||123,456,789||"""aaa""","""bbb"""||
25+
aaa,bbb,ccc,dddd|123,456,789|"""aaa""","""bbb"""|
26+
aaa,bbb,ccc,dddd\n123,456,789\n"""aaa""","""bbb"""\n
27+
aaa,bbb,ccc,dddd
28+
123,456,789
29+
"""aaa""","""bbb"""

0 commit comments

Comments
 (0)
0