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