Skip to content

Commit 5b29eba

Browse files
CameronHallGirgias
authored andcommitted
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 8f1ec5b commit 5b29eba

10 files changed

+62
-14
lines changed

ext/spl/spl_directory.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2360,11 +2360,13 @@ PHP_METHOD(SplFileObject, fputcsv)
23602360
size_t d_len = 0, e_len = 0, esc_len = 0;
23612361
zend_long ret;
23622362
zval *fields = NULL;
2363+
zend_string *eol = NULL;
23632364

2364-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|sss", &fields, &delim, &d_len, &enclo, &e_len, &esc, &esc_len) == SUCCESS) {
2365+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|sssS", &fields, &delim, &d_len, &enclo, &e_len, &esc, &esc_len, &eol) == SUCCESS) {
23652366

23662367
switch(ZEND_NUM_ARGS())
23672368
{
2369+
case 5:
23682370
case 4:
23692371
switch (esc_len) {
23702372
case 0:
@@ -2396,7 +2398,8 @@ PHP_METHOD(SplFileObject, fputcsv)
23962398
case 0:
23972399
break;
23982400
}
2399-
ret = php_fputcsv(intern->u.file.stream, fields, delimiter, enclosure, escape);
2401+
2402+
ret = php_fputcsv(intern->u.file.stream, fields, delimiter, enclosure, escape, eol);
24002403
if (ret < 0) {
24012404
RETURN_FALSE;
24022405
}

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: de510a0512057bfaecbac8228107600ed14e2ba5 */
2+
* Stub hash: 00139cce188b3950e5a7606c70c5848c6280851d */
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: 6 additions & 1 deletion
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+
[4]=>
34+
object(ReflectionParameter)#6 (1) {
35+
["name"]=>
36+
string(3) "eol"
37+
}
3338
}

ext/standard/basic_functions.stub.php

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

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

872872
/** @param resource $stream */
873873
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: 97edf8c87780c892984099e52ad1c6c745b919f8 */
2+
* Stub hash: 23c263defa042155631bec5fcb5282e4cd1e88a7 */
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)
@@ -1335,6 +1335,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_fputcsv, 0, 2, MAY_BE_LONG|MAY_B
13351335
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, separator, IS_STRING, 0, "\",\"")
13361336
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enclosure, IS_STRING, 0, "\"\\\"\"")
13371337
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, escape, IS_STRING, 0, "\"\\\\\"")
1338+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, eol, IS_STRING, 0, "\"\\n\"")
13381339
ZEND_END_ARG_INFO()
13391340

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

ext/standard/file.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,14 +1795,16 @@ PHP_FUNCTION(fputcsv)
17951795
ssize_t ret;
17961796
char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL;
17971797
size_t delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0;
1798+
zend_string *eol_str = NULL;
17981799

1799-
ZEND_PARSE_PARAMETERS_START(2, 5)
1800+
ZEND_PARSE_PARAMETERS_START(2, 6)
18001801
Z_PARAM_RESOURCE(fp)
18011802
Z_PARAM_ARRAY(fields)
18021803
Z_PARAM_OPTIONAL
18031804
Z_PARAM_STRING(delimiter_str, delimiter_str_len)
18041805
Z_PARAM_STRING(enclosure_str, enclosure_str_len)
18051806
Z_PARAM_STRING(escape_str, escape_str_len)
1807+
Z_PARAM_STR_OR_NULL(eol_str)
18061808
ZEND_PARSE_PARAMETERS_END();
18071809

18081810
if (delimiter_str != NULL) {
@@ -1840,16 +1842,16 @@ PHP_FUNCTION(fputcsv)
18401842

18411843
PHP_STREAM_TO_ZVAL(stream, fp);
18421844

1843-
ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char);
1845+
ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char, eol_str);
18441846
if (ret < 0) {
18451847
RETURN_FALSE;
18461848
}
18471849
RETURN_LONG(ret);
18481850
}
18491851
/* }}} */
18501852

1851-
/* {{{ PHPAPI size_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char) */
1852-
PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char)
1853+
/* {{{ PHPAPI size_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, zend_string *eol_str) */
1854+
PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, char enclosure, int escape_char, zend_string *eol_str)
18531855
{
18541856
int count, i = 0;
18551857
size_t ret;
@@ -1897,8 +1899,12 @@ PHPAPI ssize_t php_fputcsv(php_stream *stream, zval *fields, char delimiter, cha
18971899
}
18981900
zend_tmp_string_release(tmp_field_str);
18991901
} ZEND_HASH_FOREACH_END();
1900-
1901-
smart_str_appendc(&csvline, '\n');
1902+
1903+
if (eol_str) {
1904+
smart_str_append(&csvline, eol_str);
1905+
} else {
1906+
smart_str_appendc(&csvline, '\n');
1907+
}
19021908
smart_str_0(&csvline);
19031909

19041910
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, zend_string *eol_str);
5353

5454
#define META_DEF_BUFSIZE 8192
5555

739 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)