# fputcsv()

- Thread: https://externals.io/message/9040
- Original archive: https://news-web.php.net/php.internals/9040
- Started by David Sklar on 2004-04-11T16:33:40+00:00
- Messages: 4
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #9040 — David Sklar — 2004-04-11T16:33:40+00:00

URL: https://externals.io/message/9040#9040

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

---

## #9042 — Andi Gutmans — 2004-04-11T17:07:12+00:00

URL: https://externals.io/message/9040#9042 · In reply to: #9040

How about using PHP_EOL?


At 12:33 PM 4/11/2004 -0400, 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: ext/standard/basic_functions.c
>===================================================================
>RCS file: /repository/php-src/ext/standard/basic_functions.c,v
>retrieving revision 1.662
>diff -u -B -b -r1.662 basic_functions.c
>--- ext/standard/basic_functions.c      3 Apr 2004 09:51:57 -0000       1.662
>+++ ext/standard/basic_functions.c      11 Apr 2004 16:31:33 -0000
>@@ -596,6 +596,7 @@
>         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)
>Index: ext/standard/file.h
>===================================================================
>RCS file: /repository/php-src/ext/standard/file.h,v
>retrieving revision 1.88
>diff -u -B -b -r1.88 file.h
>--- ext/standard/file.h 8 Jan 2004 17:32:51 -0000       1.88
>+++ ext/standard/file.h 11 Apr 2004 16:31:33 -0000
>@@ -39,6 +39,7 @@
>  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: ext/standard/file.c
>===================================================================
>RCS file: /repository/php-src/ext/standard/file.c,v
>retrieving revision 1.380
>diff -u -B -b -r1.380 file.c
>--- ext/standard/file.c 25 Feb 2004 20:16:26 -0000      1.380
>+++ ext/standard/file.c 11 Apr 2004 16:31:35 -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,112 @@
>         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;
>+    int delimiter_str_len = 0;
>+    char *enclosure_str = NULL;
>+    int enclosure_str_len = 0;
>+    char *buffer = NULL;
>+    int csvline_len = 0;
>+    HashPosition pos;
>+    int count = 0, 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;
>+        }
>+
>+        /* use first character from string */
>+        delimiter[0] = delimiter_str[0];
>+    }
>+
>+    if (enclosure_str != NULL) {
>+        if (enclosure_str_len < 1) {
>+            php_error_docref(NULL TSRMLS_CC, E_WARNING, "enclosure must 
>be a character");
>+            RETURN_FALSE;
>+        }
>+        /* use first character from string */
>+        enclosure[0] = enclosure_str[0];
>+    }
>+
>+    PHP_STREAM_TO_ZVAL(stream, &fp);
>+
>+    enc_double[0] = enc_double[1] = enclosure[0];
>+    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 ((*field)->type != IS_STRING) {
>+                       SEPARATE_ZVAL(field);
>+                       convert_to_string(*field);
>+               }
>+        /* enclose a field that contains a delimiter, an enclosure
>+           character, or a newline */
>+        if (php_memnstr(Z_STRVAL_PP(field), delimiter, 1, 
>Z_STRVAL_PP(field) + Z_STRLEN_PP(field)) ||
>+            php_memnstr(Z_STRVAL_PP(field), enclosure, 1, 
>Z_STRVAL_PP(field) + Z_STRLEN_PP(field)) ||
>+            php_memnstr(Z_STRVAL_PP(field), "\n", 1, Z_STRVAL_PP(field) + 
>Z_STRLEN_PP(field)) ||
>+            php_memnstr(Z_STRVAL_PP(field), "\r", 1, Z_STRVAL_PP(field) + 
>Z_STRLEN_PP(field))) {
>+
>+            smart_str_appendl(&csvline, enclosure, 1);
>+
>+            {
>+                zval enclosed_field;
>+                php_char_to_str_ex(Z_STRVAL_PP(field), 
>Z_STRLEN_PP(field), enclosure[0],
>+                                   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)) {
>+               buffer = estrndup(csvline.c, csvline.len);
>+               php_stripslashes(buffer, &csvline_len TSRMLS_CC);
>+       } else {
>+        csvline_len = csvline.len;
>+    }
>+
>+       ret = php_stream_write(stream, buffer ? buffer : csvline.c, 
>csvline_len);
>+
>+       if (buffer) {
>+               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)

---

## #9045 — Ilia Alshanetsky — 2004-04-11T17:56:16+00:00

URL: https://externals.io/message/9040#9045 · In reply to: #9040

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


> 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

---

## #9047 — Moriyoshi Koizumi — 2004-04-12T00:52:57+00:00

URL: https://externals.io/message/9040#9047 · In reply to: #9040

> 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.

As for writing stuff, we don't have to care about in which encoding 
strings
are encoded as long as it is 8-bit compatible. UTF-16 / UTF-32 / UCS-2 /
UCS-4 are not 8-bit compatible and I've never seen a case where one of
these is utilised for CSV files, so I think your code is actually
"multibyte safe" :)

Moriyoshi

>
> Any suggestions for fixing these things (or other issues) would be 
> appreciated.
>
> Thanks,
> David
