Skip to content

Commit 2139d27

Browse files
tmlsmalyshev
authored andcommitted
Expose fputcsv's escape_char to userland
Allows users to assert that something other than the backslash should be considered an escape char; also follows the RFC 4180 recommendation that fields containing a " be enclosed.
1 parent bdccf0a commit 2139d27

File tree

5 files changed

+135
-12
lines changed

5 files changed

+135
-12
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PHP NEWS
33
?? ??? 2013, PHP 5.5.4
44

55
- Core:
6+
. Improved fputcsv() to allow specifying escape character.
67
. Fixed bug #65490 (Duplicate calls to get lineno & filename for
78
DTRACE_FUNCTION_*). (Chris Jones)
89
. Fixed bug #65483 (quoted-printable encode stream filter incorrectly encoding

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ PHP 5.5 UPGRADE NOTES
186186
DOMDocument::schemaValidate() accept flag parameter. Only flag
187187
available now is LIBXML_SCHEMA_CREATE. Default is 0.
188188

189+
- Since 5.5.4, fputcsv() has fifth parameter escape_char, allowing to
190+
specify escape char.
191+
189192
========================================
190193
5. New Functions
191194
========================================

ext/standard/file.c

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ PHP_FUNCTION(tempnam)
818818
if (p_len > 64) {
819819
p[63] = '\0';
820820
}
821-
821+
822822
RETVAL_FALSE;
823823

824824
if ((fd = php_open_temporary_fd_ex(dir, p, &opened_path, 1 TSRMLS_CC)) >= 0) {
@@ -1380,13 +1380,13 @@ PHP_FUNCTION(umask)
13801380
{
13811381
long arg1 = 0;
13821382
int oldumask;
1383-
1383+
13841384
oldumask = umask(077);
13851385

13861386
if (BG(umask) == -1) {
13871387
BG(umask) = oldumask;
13881388
}
1389-
1389+
13901390
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &arg1) == FAILURE) {
13911391
RETURN_FALSE;
13921392
}
@@ -1799,22 +1799,23 @@ static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t le
17991799

18001800
#define FPUTCSV_FLD_CHK(c) memchr(Z_STRVAL(field), c, Z_STRLEN(field))
18011801

1802-
/* {{{ proto int fputcsv(resource fp, array fields [, string delimiter [, string enclosure]])
1802+
/* {{{ proto int fputcsv(resource fp, array fields [, string delimiter [, string enclosure [, string escape_char]]])
18031803
Format line as CSV and write to file pointer */
18041804
PHP_FUNCTION(fputcsv)
18051805
{
1806-
char delimiter = ','; /* allow this to be set as parameter */
1807-
char enclosure = '"'; /* allow this to be set as parameter */
1808-
const char escape_char = '\\';
1806+
char delimiter = ','; /* allow this to be set as parameter */
1807+
char enclosure = '"'; /* allow this to be set as parameter */
1808+
char escape_char = '\\'; /* allow this to be set as parameter */
18091809
php_stream *stream;
18101810
zval *fp = NULL, *fields = NULL;
18111811
int ret;
1812-
char *delimiter_str = NULL, *enclosure_str = NULL;
1813-
int delimiter_str_len = 0, enclosure_str_len = 0;
1812+
char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL;
1813+
int delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0;
18141814

1815-
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|ss",
1815+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|sss",
18161816
&fp, &fields, &delimiter_str, &delimiter_str_len,
1817-
&enclosure_str, &enclosure_str_len) == FAILURE) {
1817+
&enclosure_str, &enclosure_str_len,
1818+
&escape_str, &escape_str_len) == FAILURE) {
18181819
return;
18191820
}
18201821

@@ -1842,6 +1843,17 @@ PHP_FUNCTION(fputcsv)
18421843
enclosure = *enclosure_str;
18431844
}
18441845

1846+
if (escape_str != NULL) {
1847+
if (escape_str_len < 1) {
1848+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "escape must be a character");
1849+
RETURN_FALSE;
1850+
} else if (escape_str_len > 1) {
1851+
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "escape must be a single character");
1852+
}
1853+
/* use first character from string */
1854+
escape_char = *escape_str;
1855+
}
1856+
18451857
PHP_STREAM_TO_ZVAL(stream, &fp);
18461858

18471859
ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char TSRMLS_CC);

ext/standard/tests/file/fputcsv_error.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Warning: fputcsv() expects at least 2 parameters, 0 given in %s on line %d
4848
NULL
4949
-- Testing fputcsv() with more than expected number of arguments --
5050

51-
Warning: fputcsv() expects at most 4 parameters, 5 given in %s on line %d
51+
Warning: fputcsv() expects parameter 5 to be string, resource given in %s on line %d
5252
NULL
5353
-- Testing fputcsv() with invalid arguments --
5454
-- Iteration 1 --
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
--TEST--
2+
various fputcsv() functionality tests
3+
--CREDITS--
4+
Lee Leathers <leeleathers@gmail.com>
5+
--FILE--
6+
<?php
7+
8+
$list = array (
9+
0 => 'aaa,bbb',
10+
1 => 'aaa,"bbb"',
11+
2 => '"aaa","bbb"',
12+
3 => 'aaa,bbb',
13+
4 => '"aaa",bbb',
14+
5 => '"aaa", "bbb"',
15+
6 => ',',
16+
7 => 'aaa,',
17+
8 => ',"aaa"',
18+
9 => '"",""',
19+
10 => '"""""",',
20+
11 => '""""",aaa',
21+
12 => 'aaa,bbb ',
22+
13 => 'aaa,"bbb "',
23+
14 => 'aaa"aaa","bbb"bbb',
24+
15 => 'aaa"aaa""",bbb',
25+
16 => 'aaa,"/"bbb,ccc',
26+
17 => 'aaa"/"a","bbb"',
27+
18 => '"/"","aaa"',
28+
19 => '"/""",aaa',
29+
);
30+
31+
$file = dirname(__FILE__) . 'fgetcsv.csv';
32+
@unlink($file);
33+
34+
$fp = fopen($file, "w");
35+
foreach ($list as $v) {
36+
fputcsv($fp, explode(',', $v), ',', '"', '/');
37+
}
38+
fclose($fp);
39+
40+
$res = file($file);
41+
foreach($res as &$val)
42+
{
43+
$val = substr($val, 0, -1);
44+
}
45+
echo '$list = ';var_export($res);echo ";\n";
46+
47+
$fp = fopen($file, "r");
48+
$res = array();
49+
while($l=fgetcsv($fp, 0, ',', '"', '/'))
50+
{
51+
$res[] = join(',',$l);
52+
}
53+
fclose($fp);
54+
55+
echo '$list = ';var_export($res);echo ";\n";
56+
57+
@unlink($file);
58+
59+
?>
60+
===DONE===
61+
<?php exit(0); ?>
62+
--EXPECT--
63+
$list = array (
64+
0 => 'aaa,bbb',
65+
1 => 'aaa,"""bbb"""',
66+
2 => '"""aaa""","""bbb"""',
67+
3 => 'aaa,bbb',
68+
4 => '"""aaa""",bbb',
69+
5 => '"""aaa"""," ""bbb"""',
70+
6 => ',',
71+
7 => 'aaa,',
72+
8 => ',"""aaa"""',
73+
9 => '"""""",""""""',
74+
10 => '"""""""""""""",',
75+
11 => '"""""""""""",aaa',
76+
12 => 'aaa,"bbb "',
77+
13 => 'aaa,"""bbb """',
78+
14 => '"aaa""aaa""","""bbb""bbb"',
79+
15 => '"aaa""aaa""""""",bbb',
80+
16 => 'aaa,"""/"bbb",ccc',
81+
17 => '"aaa""/"a""","""bbb"""',
82+
18 => '"""/"""","""aaa"""',
83+
19 => '"""/"""""",aaa',
84+
);
85+
$list = array (
86+
0 => 'aaa,bbb',
87+
1 => 'aaa,"bbb"',
88+
2 => '"aaa","bbb"',
89+
3 => 'aaa,bbb',
90+
4 => '"aaa",bbb',
91+
5 => '"aaa", "bbb"',
92+
6 => ',',
93+
7 => 'aaa,',
94+
8 => ',"aaa"',
95+
9 => '"",""',
96+
10 => '"""""",',
97+
11 => '""""",aaa',
98+
12 => 'aaa,bbb ',
99+
13 => 'aaa,"bbb "',
100+
14 => 'aaa"aaa","bbb"bbb',
101+
15 => 'aaa"aaa""",bbb',
102+
16 => 'aaa,"/"bbb,ccc',
103+
17 => 'aaa"/"a","bbb"',
104+
18 => '"/"","aaa"',
105+
19 => '"/""",aaa',
106+
);
107+
===DONE===

0 commit comments

Comments
 (0)