Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

fputcsv improvements to allow RFC 4180 compliance #197

Closed
wants to merge 9 commits into from

6 participants

Joey Smith Lars Strojny Lee Leathers Adam Harvey Stanislav Malyshev Account for PHP Pull Requests
Joey Smith

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.

Joey Smith 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.
49f1ef4
Lars Strojny

We definitely need a test for that. Could you add one?

Joey Smith

I'm actually all thumbs with .phpt syntax, but I'll see if I can outsource it. :)

Lars Strojny lstrojny commented on the diff September 19, 2012
ext/standard/file.c
@@ -1839,6 +1840,17 @@ static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t le
1839 1840
 		enclosure = *enclosure_str;
1840 1841
 	}
1841 1842
 
  1843
+	if (escape_str != NULL) {
  1844
+		if (escape_str_len < 1) {
  1845
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "escape must be a character");
  1846
+			RETURN_FALSE;
  1847
+		} else if (escape_str_len > 1) {
  1848
+			php_error_docref(NULL TSRMLS_CC, E_NOTICE, "escape must be a single character");
1

Should be "Escape string must be a single character"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Lars Strojny lstrojny commented on the diff September 19, 2012
ext/standard/file.c
@@ -1839,6 +1840,17 @@ static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t le
1839 1840
 		enclosure = *enclosure_str;
1840 1841
 	}
1841 1842
 
  1843
+	if (escape_str != NULL) {
  1844
+		if (escape_str_len < 1) {
  1845
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "escape must be a character");
2

Message should be "Escape string must be a character"

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Lars Strojny

Any news?

Joey Smith

No - if you know of anyone who would be willing to write a .phpt for it, feel free to pass it along to them.

Lars Strojny

@tml ping

Lee Leathers

@lstrojny I have a test coming your way. branching and submitting a pull request

Lee Leathers

@tml @lstrojny

TML, hey, It's error_code from irc. I just submitted a pull request tml#1 on your repo like we talked about on monday.

Either hit me up on irc or here if you want me to work further on it.

thanks guys!

Lars Strojny

@tml @theoreticaLee could you both coordinate and consolidate a single PR including the test?

Adam Harvey

@lstrojny It's OK, I'm about to make this even more annoying for everyone — it needs a rebase after the fix for https://bugs.php.net/bug.php?id=43225, and SplFileObject::fputcsv() also needs to have an escape argument added to keep it in line with fputcsv().

It turned out that @theoreticaLee and I were working on this simultaneously unbeknownst to each other — I'll consolidate the commits from @tml and @theoreticaLee together with my own changes for SplFileObject and the aforementioned rebase and commit it.

Adam Harvey

Actually, forget what I said about rebasing — the #43225 fix isn't right, so I've reverted it for now, since I can't spend any more time on it at present.

@tml @theoreticaLee As @lstrojny said, if you guys can consolidate this, that'd be grand.

My earlier note about SplFileObject::fputcsv() stands, though.

Joey Smith
tml commented January 15, 2013

@LawnGnome Thank you for finding that - it was why I couldn't manage to get a solid .phpt out of this, but I couldn't find the bottom of the stack of changes. As always, your heroic efforts are greatly appreciated.

@lstrojny, the test case from @theoreticaLee has been merged into this pull request; as far as I'm concerned, @LawnGnome and @theoreticaLee need the credit for this patch; I just happened to code monkey it, they did the heavy lifting.

Joey Smith tml commented on the diff January 15, 2013
ext/standard/file.c
@@ -817,7 +817,7 @@ static void file_globals_dtor(php_file_globals *file_globals_p TSRMLS_DC)
817 817
 	if (p_len > 64) {
818 818
 		p[63] = '\0';
819 819
 	}
820  
-	
  820
+
1
Joey Smith
tml added a note January 15, 2013

@theoreticaLee cleaned up stray tab

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Joey Smith tml commented on the diff January 15, 2013
ext/standard/file.c
@@ -1377,13 +1377,13 @@ PHPAPI int php_mkdir(char *dir, long mode TSRMLS_DC)
1377 1377
 {
1378 1378
 	long arg1 = 0;
1379 1379
 	int oldumask;
1380  
-	
  1380
+
1
Joey Smith
tml added a note January 15, 2013

@theoreticaLee cleaned up stray tab

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Joey Smith tml commented on the diff January 15, 2013
ext/standard/file.c
((6 lines not shown))
1381 1381
 	oldumask = umask(077);
1382 1382
 
1383 1383
 	if (BG(umask) == -1) {
1384 1384
 		BG(umask) = oldumask;
1385 1385
 	}
1386  
-	
  1386
+
1
Joey Smith
tml added a note January 15, 2013

@theoreticaLee cleaned up stray tab

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Stanislav Malyshev

This patch seems to break fputcsv_variation6.phpt, fputcsv_error.phpt and fputcsv_variation9.phpt. Changes to fputcsv_error.phpt are trivial but for two others I'm not sure what's wrong. @tml, please look into it.

Stanislav Malyshev

@tml any news?

Joey Smith
tml commented August 19, 2013

I personally feel these tests are horribly incorrect (not to mention bloated with c/p code that doesn't make much sense at all). They're testing what PHP has historically done, rather than what makes sense (most notably, if I explicitly request delimiters and enclosures, why is PHP dropping them!?).

The line that is causing these tests to fail was FPUTCSV_FLD_CHK('"'), added because RFC 4180 2.6 says: " Fields containing line breaks (CRLF), double quotes, and commas should be enclosed in double-quotes..." (emphasis mine). However, I have no interest in a BC fight, so I've retracted the fix; now we can just brush the problem under the rug for the next 10 years.

Joey Smith
tml commented August 19, 2013

I cannot reproduce the failed builds at this point; the tests failing are:

PostgreSQL notice function [ext/pgsql/tests/09notice.phpt]
Bug #32223 (8.0+) (weird behaviour of pg_last_notice using define) [ext/pgsql/tests/80_bug32223b.phpt]

Neither of them appears to bear the slightest relationship to my fputcsv changes.

Stanislav Malyshev

Looks like doesn't fail for me anymore, so I guess it's ok.

Account for PHP Pull Requests
Collaborator

Comment on behalf of stas at php.net:

merged

Account for PHP Pull Requests php-pulls closed this August 24, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
34  ext/standard/file.c
@@ -818,7 +818,7 @@ PHP_FUNCTION(tempnam)
818 818
 	if (p_len > 64) {
819 819
 		p[63] = '\0';
820 820
 	}
821  
-	
  821
+
822 822
 	RETVAL_FALSE;
823 823
 
824 824
 	if ((fd = php_open_temporary_fd_ex(dir, p, &opened_path, 1 TSRMLS_CC)) >= 0) {
@@ -1380,13 +1380,13 @@ PHP_FUNCTION(umask)
1380 1380
 {
1381 1381
 	long arg1 = 0;
1382 1382
 	int oldumask;
1383  
-	
  1383
+
1384 1384
 	oldumask = umask(077);
1385 1385
 
1386 1386
 	if (BG(umask) == -1) {
1387 1387
 		BG(umask) = oldumask;
1388 1388
 	}
1389  
-	
  1389
+
1390 1390
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &arg1) == FAILURE) {
1391 1391
 		RETURN_FALSE;
1392 1392
 	}
@@ -1799,22 +1799,23 @@ static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t le
1799 1799
 
1800 1800
 #define FPUTCSV_FLD_CHK(c) memchr(Z_STRVAL(field), c, Z_STRLEN(field))
1801 1801
 
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]]])
1803 1803
    Format line as CSV and write to file pointer */
1804 1804
 PHP_FUNCTION(fputcsv)
1805 1805
 {
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 */
1809 1809
 	php_stream *stream;
1810 1810
 	zval *fp = NULL, *fields = NULL;
1811 1811
 	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;
1814 1814
 
1815  
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|ss",
  1815
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|sss",
1816 1816
 			&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) {
1818 1819
 		return;
1819 1820
 	}
1820 1821
 
@@ -1842,6 +1843,17 @@ PHP_FUNCTION(fputcsv)
1842 1843
 		enclosure = *enclosure_str;
1843 1844
 	}
1844 1845
 
  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
+
1845 1857
 	PHP_STREAM_TO_ZVAL(stream, &fp);
1846 1858
 
1847 1859
 	ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char TSRMLS_CC);
2  ext/standard/tests/file/fputcsv_error.phpt
@@ -48,7 +48,7 @@ Warning: fputcsv() expects at least 2 parameters, 0 given in %s on line %d
48 48
 NULL
49 49
 -- Testing fputcsv() with more than expected number of arguments --
50 50
 
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
52 52
 NULL
53 53
 -- Testing fputcsv() with invalid arguments --
54 54
 -- Iteration 1 --
107  ext/standard/tests/file/fputcsv_variation15.phpt
... ...
@@ -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===
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.