Hi David,

I've made a slight revision to your proposed patch with adds the following 
changes:

1) Raise notice when delimeter or enclosure are >1 character long.
2) Fixed a crash with custom delimeters & enclosures.
3) Made fields with spaces and tabs be enclosed.
4) Simplify the code & made it abide by PHP's CS.

As far as the line terminators, I think your initial usage of '\n' is the best 
approach. Most applications capable of reading csv files will support \n 
without further input from the user regardless of the system's EOL. Although, 
\r\n is equally well supported, the only one which is not terribly well 
supported is \r especially on some Win32 applications. Since the idea would 
be to generate a universally usable csv file, \n seems (to me) like the best 
choice.

Ilia

On April 11, 2004 12:33 pm, David Sklar wrote:
> Attached is a patch that implements fputcsv() as a complement to fgetcsv().
>
> There are two things that still need improvement:
>
> - It adds "\n" as a newline onto the end of each line. I think it would
> be better to add a platform-specific line ending.
>
> - It is not mbstring-aware.
>
> Any suggestions for fixing these things (or other issues) would be
> appreciated.
>
> Thanks,
> David
Index: file.c
===================================================================
RCS file: /repository/php-src/ext/standard/file.c,v
retrieving revision 1.380
diff -u -3 -p -r1.380 file.c
--- file.c      25 Feb 2004 20:16:26 -0000      1.380
+++ file.c      11 Apr 2004 17:46:28 -0000
@@ -35,6 +35,7 @@
 #include "php_open_temporary_file.h"
 #include "ext/standard/basic_functions.h"
 #include "php_ini.h"
+#include "php_smart_str.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -1704,6 +1705,109 @@ quit_loop:
        return ptr;
 }
 
+/* {{{ proto int fputcsv(resource fp, array fields [, string delimiter [, string 
enclosure]])
+   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 */
+       php_stream *stream;
+       int ret;
+       zval *fp = NULL, *fields = NULL, **field = NULL;
+       char *delimiter_str = NULL, *enclosure_str = NULL;
+       int delimiter_str_len, enclosure_str_len;
+       HashPosition pos;
+       int count, i = 0;
+       char enc_double[3];
+       smart_str csvline = {0};
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ass",
+                       &fp, &fields, &delimiter_str, &delimiter_str_len,
+                       &enclosure_str, &enclosure_str_len) == FAILURE) {
+               return;
+       }       
+
+       if (delimiter_str != NULL) {
+               /* Make sure that there is at least one character in string */
+               if (delimiter_str_len < 1) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "delimiter must be 
a character");
+                       RETURN_FALSE;
+               } else if (delimiter_str_len > 1) {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, "delimiter must be 
a single character");
+               }
+
+               /* use first character from string */
+               delimiter = *delimiter_str;
+       }
+
+       if (enclosure_str != NULL) {
+               if (enclosure_str_len < 1) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "enclosure must be 
a character");
+                       RETURN_FALSE;
+               } else if (enclosure_str_len > 1) {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, "enclosure must be 
a single character");
+               }
+               /* use first character from string */
+               enclosure = *enclosure_str;
+       }
+    
+       PHP_STREAM_TO_ZVAL(stream, &fp);
+
+       enc_double[0] = enc_double[1] = enclosure;
+       enc_double[2] = '\0';
+       count = zend_hash_num_elements(Z_ARRVAL_P(fields));
+       zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(fields), &pos);
+       while (zend_hash_get_current_data_ex(Z_ARRVAL_P(fields), (void **) &field, 
&pos) == SUCCESS) {
+               if (Z_TYPE_PP(field) != IS_STRING) {
+                       SEPARATE_ZVAL(field);
+                       convert_to_string(*field);
+               } 
+
+#define FPUTCSV_FLD_CHK(c) php_memnstr(Z_STRVAL_PP(field), c, 1, Z_STRVAL_PP(field) + 
Z_STRLEN_PP(field))
+
+               /* enclose a field that contains a delimiter, an enclosure character, 
or a newline */
+               if (FPUTCSV_FLD_CHK(&delimiter) || FPUTCSV_FLD_CHK(&enclosure) || 
FPUTCSV_FLD_CHK("\n") ||
+                       FPUTCSV_FLD_CHK("\r") || FPUTCSV_FLD_CHK(" ") || 
FPUTCSV_FLD_CHK("\t")
+               ) {
+                       zval enclosed_field;
+                       smart_str_appendl(&csvline, &enclosure, 1);
+
+                       php_char_to_str_ex(Z_STRVAL_PP(field), Z_STRLEN_PP(field),
+                                               enclosure, enc_double, 2, 
&enclosed_field, 0, NULL);
+                       smart_str_appendl(&csvline, Z_STRVAL(enclosed_field), 
Z_STRLEN(enclosed_field));
+                       zval_dtor(&enclosed_field);
+
+                       smart_str_appendl(&csvline, &enclosure, 1);
+               } else {
+                       smart_str_appendl(&csvline, Z_STRVAL_PP(field), 
Z_STRLEN_PP(field));
+               }
+
+               if (++i != count) {
+                       smart_str_appendl(&csvline, &delimiter, 1);
+               }
+               zend_hash_move_forward_ex(Z_ARRVAL_P(fields), &pos);
+       }
+
+       /* XXX: this should be platform-specific */
+       smart_str_appendc(&csvline, '\n');
+
+       smart_str_0(&csvline);
+
+       if (!PG(magic_quotes_runtime)) {
+               ret = php_stream_write(stream, csvline.c, csvline.len);
+       } else {
+               char *buffer = estrndup(csvline.c, csvline.len);
+               int len;
+               php_stripslashes(buffer, &len TSRMLS_CC);
+               ret = php_stream_write(stream, buffer, len);
+               efree(buffer);
+       }
+
+       smart_str_free(&csvline);
+
+       RETURN_LONG(ret);
+}
+
 /* {{{ proto array fgetcsv(resource fp [,int length [, string delimiter [, string 
enclosure]]])
    Get line from file pointer and parse for CSV fields */
 PHP_FUNCTION(fgetcsv)
Index: file.h
===================================================================
RCS file: /repository/php-src/ext/standard/file.h,v
retrieving revision 1.88
diff -u -3 -p -r1.88 file.h
--- file.h      8 Jan 2004 17:32:51 -0000       1.88
+++ file.h      11 Apr 2004 17:46:28 -0000
@@ -39,6 +39,7 @@ PHPAPI PHP_FUNCTION(fgets);
 PHP_FUNCTION(fscanf);
 PHPAPI PHP_FUNCTION(fgetss);
 PHP_FUNCTION(fgetcsv);
+PHP_FUNCTION(fputcsv);
 PHPAPI PHP_FUNCTION(fwrite);
 PHPAPI PHP_FUNCTION(fflush);
 PHPAPI PHP_FUNCTION(rewind);
Index: basic_functions.c
===================================================================
RCS file: /repository/php-src/ext/standard/basic_functions.c,v
retrieving revision 1.662
diff -u -3 -p -r1.662 basic_functions.c
--- basic_functions.c   3 Apr 2004 09:51:57 -0000       1.662
+++ basic_functions.c   11 Apr 2004 17:46:29 -0000
@@ -596,6 +596,7 @@ function_entry basic_functions[] = {
        PHP_FE(stream_copy_to_stream,                                                  
                                 NULL)
        PHP_FE(stream_get_contents,                                                    
                                         NULL)
        PHP_FE(fgetcsv,                                                                
                                                 NULL)
+       PHP_FE(fputcsv,                                                                
                                                 NULL)
        PHP_FE(flock,                                                                  
                  third_arg_force_ref)
        PHP_FE(get_meta_tags,                                                          
                                         NULL)
        PHP_FE(stream_set_write_buffer,                                                
                                 NULL)

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to