Skip to content

fputcsv improvements to allow RFC 4180 compliance #197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
34 changes: 23 additions & 11 deletions ext/standard/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ PHP_FUNCTION(tempnam)
if (p_len > 64) {
p[63] = '\0';
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theoreticaLee cleaned up stray tab

RETVAL_FALSE;

if ((fd = php_open_temporary_fd_ex(dir, p, &opened_path, 1 TSRMLS_CC)) >= 0) {
Expand Down Expand Up @@ -1380,13 +1380,13 @@ PHP_FUNCTION(umask)
{
long arg1 = 0;
int oldumask;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theoreticaLee cleaned up stray tab

oldumask = umask(077);

if (BG(umask) == -1) {
BG(umask) = oldumask;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theoreticaLee cleaned up stray tab

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &arg1) == FAILURE) {
RETURN_FALSE;
}
Expand Down Expand Up @@ -1799,22 +1799,23 @@ static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t le

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

/* {{{ proto int fputcsv(resource fp, array fields [, string delimiter [, string enclosure]])
/* {{{ proto int fputcsv(resource fp, array fields [, string delimiter [, string enclosure [, string escape_char]]])
Format line as CSV and write to file pointer */
PHP_FUNCTION(fputcsv)
{
char delimiter = ','; /* allow this to be set as parameter */
char enclosure = '"'; /* allow this to be set as parameter */
const char escape_char = '\\';
char delimiter = ','; /* allow this to be set as parameter */
char enclosure = '"'; /* allow this to be set as parameter */
char escape_char = '\\'; /* allow this to be set as parameter */
php_stream *stream;
zval *fp = NULL, *fields = NULL;
int ret;
char *delimiter_str = NULL, *enclosure_str = NULL;
int delimiter_str_len = 0, enclosure_str_len = 0;
char *delimiter_str = NULL, *enclosure_str = NULL, *escape_str = NULL;
int delimiter_str_len = 0, enclosure_str_len = 0, escape_str_len = 0;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|ss",
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|sss",
&fp, &fields, &delimiter_str, &delimiter_str_len,
&enclosure_str, &enclosure_str_len) == FAILURE) {
&enclosure_str, &enclosure_str_len,
&escape_str, &escape_str_len) == FAILURE) {
return;
}

Expand Down Expand Up @@ -1842,6 +1843,17 @@ PHP_FUNCTION(fputcsv)
enclosure = *enclosure_str;
}

if (escape_str != NULL) {
if (escape_str_len < 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "escape must be a character");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Message should be "Escape string must be a character"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and line 1848 are fine — the messages are consistent with fgetcsv() and the other warnings in the function.

RETURN_FALSE;
} else if (escape_str_len > 1) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "escape must be a single character");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be "Escape string must be a single character"

}
/* use first character from string */
escape_char = *escape_str;
}

PHP_STREAM_TO_ZVAL(stream, &fp);

ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char TSRMLS_CC);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/tests/file/fputcsv_error.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Warning: fputcsv() expects at least 2 parameters, 0 given in %s on line %d
NULL
-- Testing fputcsv() with more than expected number of arguments --

Warning: fputcsv() expects at most 4 parameters, 5 given in %s on line %d
Warning: fputcsv() expects parameter 5 to be string, resource given in %s on line %d
NULL
-- Testing fputcsv() with invalid arguments --
-- Iteration 1 --
Expand Down
107 changes: 107 additions & 0 deletions ext/standard/tests/file/fputcsv_variation15.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
--TEST--
various fputcsv() functionality tests
--CREDITS--
Lee Leathers <[email protected]>
--FILE--
<?php

$list = array (
0 => 'aaa,bbb',
1 => 'aaa,"bbb"',
2 => '"aaa","bbb"',
3 => 'aaa,bbb',
4 => '"aaa",bbb',
5 => '"aaa", "bbb"',
6 => ',',
7 => 'aaa,',
8 => ',"aaa"',
9 => '"",""',
10 => '"""""",',
11 => '""""",aaa',
12 => 'aaa,bbb ',
13 => 'aaa,"bbb "',
14 => 'aaa"aaa","bbb"bbb',
15 => 'aaa"aaa""",bbb',
16 => 'aaa,"/"bbb,ccc',
17 => 'aaa"/"a","bbb"',
18 => '"/"","aaa"',
19 => '"/""",aaa',
);

$file = dirname(__FILE__) . 'fgetcsv.csv';
@unlink($file);

$fp = fopen($file, "w");
foreach ($list as $v) {
fputcsv($fp, explode(',', $v), ',', '"', '/');
}
fclose($fp);

$res = file($file);
foreach($res as &$val)
{
$val = substr($val, 0, -1);
}
echo '$list = ';var_export($res);echo ";\n";

$fp = fopen($file, "r");
$res = array();
while($l=fgetcsv($fp, 0, ',', '"', '/'))
{
$res[] = join(',',$l);
}
fclose($fp);

echo '$list = ';var_export($res);echo ";\n";

@unlink($file);

?>
===DONE===
<?php exit(0); ?>
--EXPECT--
$list = array (
0 => 'aaa,bbb',
1 => 'aaa,"""bbb"""',
2 => '"""aaa""","""bbb"""',
3 => 'aaa,bbb',
4 => '"""aaa""",bbb',
5 => '"""aaa"""," ""bbb"""',
6 => ',',
7 => 'aaa,',
8 => ',"""aaa"""',
9 => '"""""",""""""',
10 => '"""""""""""""",',
11 => '"""""""""""",aaa',
12 => 'aaa,"bbb "',
13 => 'aaa,"""bbb """',
14 => '"aaa""aaa""","""bbb""bbb"',
15 => '"aaa""aaa""""""",bbb',
16 => 'aaa,"""/"bbb",ccc',
17 => '"aaa""/"a""","""bbb"""',
18 => '"""/"""","""aaa"""',
19 => '"""/"""""",aaa',
);
$list = array (
0 => 'aaa,bbb',
1 => 'aaa,"bbb"',
2 => '"aaa","bbb"',
3 => 'aaa,bbb',
4 => '"aaa",bbb',
5 => '"aaa", "bbb"',
6 => ',',
7 => 'aaa,',
8 => ',"aaa"',
9 => '"",""',
10 => '"""""",',
11 => '""""",aaa',
12 => 'aaa,bbb ',
13 => 'aaa,"bbb "',
14 => 'aaa"aaa","bbb"bbb',
15 => 'aaa"aaa""",bbb',
16 => 'aaa,"/"bbb,ccc',
17 => 'aaa"/"a","bbb"',
18 => '"/"","aaa"',
19 => '"/""",aaa',
);
===DONE===