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.
März 27th, 2015 at 12:28
This is not „in memory“.
März 27th, 2015 at 12:58
Hi Jean!
What do you mean?
April 14th, 2015 at 16:22
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.
April 14th, 2015 at 16:40
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.
April 20th, 2015 at 16:05
@thorsten you’re son of a bitch
April 20th, 2015 at 20:39
Thanks!
Mai 8th, 2015 at 20:33
This is exactly what I was looking for! Nifty that it’s basically the same as creating a regular file!
Cheers!
Mai 22nd, 2015 at 17:51
Great article!