Create CSV file in memory PHP

Februar 9th, 2014

As CSV is a very simple, self-describing data format, it seems easy to create a CSV in memory using simple string functions like this:

 printf("%s,%s,%s\n", '1', 'hek2mgl', '36');

But be warned that this is true only if the data is really simple and it is save that is cannot contain the delimiter itself.

When the data to be dumped as CSV comes from an untrusted source, you are highly encouraged to use the php function fputcsv(). It offers to specify a freely chosen delimiter and takes care of proper escaping in the output:

$fd = fopen('output.csv', 'w');
if($fd === FALSE) {
    die('Failed to open output file');
}

$headers = array('id', 'name', 'age', 'species');
$records = array(
    array('1', 'gise', '4', 'cat'),
    array('2', 'hek2mgl', '36', 'human')
);

fputcsv($fd, $headers);
foreach($records as $record) {
    fputcsv($fd, $record);
}

fclose($fd);

So far so good. fputcsv() takes an open file descriptor and writes proper CSV to a file. But what if we want the CSV the content of a variable and don’t care about the file itself? There are several situations where this is helpful, currently in our company it was required to send it as an email attachment (base64 encoded)

I’ve often seen solutions which are creating a temporary file using tempnam(). However, this solution still needs all contents written to a file first and then, in a second step, reading again using file_get_contents(). Also it needs to be sure that the file will be removed after operations, … I mean ensured! 😉

Here I will show an alternative to this practice using the php://temp fopen wrapper. The temp wrapper offers to write to a temporary file but leaves the tempfile creation and deletion up to PHP. Nice, isn’t it? Also it will create and use the temporary file only if the amount of data to be written reaches a, configurable, threshold. This offers a huge performance gain in most cases while it still offers to control memory limits. Great, isn’t it? :)

This is almost the same as above but we’re now having the generated CSV in a variable:

// we use a threshold of 1 MB (1024 * 1024), it's just an example
$fd = fopen('php://temp/maxmemory:1048576', 'w');
if($fd === FALSE) {
    die('Failed to open temporary file');
}

$headers = array('id', 'name', 'age', 'species');
$records = array(
    array('1', 'gise', '4', 'cat'),
    array('2', 'hek2mgl', '36', 'human')
);

fputcsv($fd, $headers);
foreach($records as $record) {
    fputcsv($fd, $record);
}

rewind($fd);
$csv = stream_get_contents($fd);
fclose($fd);

Now PHP handles the temp file related work on it’s own.


If this wouldn’t be cool enough, I will show another nice thing that can be done using fopen wrappers. I’ve told that at work there was a requirement that the CSV should be attached to an email but needs to base64 encoded before.

Having my code above the first solution that comes in mind would be something like this:

$csv = base64_encode(stream_get_contents($fd));

While this would work, PHP needs to loop through the data twice. This can be avoided using the php://filter fopen wrapper. It offers a base64 filter. You need to specify it when opening the temp file:

// we use a threshold of 1 MB (1024 * 1024), it's just an example
$fd = fopen('php://filter/read=convert.base64-encode/resource=php://temp/maxmemory:1048576', 'w');
if($fd === FALSE) {
    die('Failed to open temporary file');
}

$headers = array('id', 'name', 'age', 'species');
$records = array(
    array('1', 'gise', '4', 'cat'),
    array('2', 'hek2mgl', '36', 'human')
);

fputcsv($fd, $headers);
foreach($records as $record) {
    fputcsv($fd, $record);
}

rewind($fd);
$base64_encoded_csv = stream_get_contents($fd);
fclose($fd);

This is the final solution, thanks PHP.

8 Responses to “Create CSV file in memory PHP”

  1. Jean Cule Says:

    This is not „in memory“.

  2. thorsten Says:

    Hi Jean!

    What do you mean?

  3. Stefan Says:

    I think Jean says it’s not in memory as php://temp actually writes to a temp file not to memory. You should use php://memory instead.

  4. thorsten Says:

    Check the code properly. I defined a threshold. Unless the threshold will be reached the tempfile won’t get created and everything will happen in memory. I like this.

  5. Mubarak Says:

    @thorsten you’re son of a bitch

  6. thorsten Says:

    Thanks!

  7. Adrian von Gegerfelt Says:

    This is exactly what I was looking for! Nifty that it’s basically the same as creating a regular file!

    Cheers!

  8. Ross Says:

    Great article!

Leave a Reply