-
-
Save zuzuleinen/6db61b09465e9bc8a7ea to your computer and use it in GitHub Desktop.
<?php | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |
class CsvController extends Controller | |
{ | |
/** | |
* Get a CSV file from an array | |
* | |
* @return \Symfony\Component\HttpFoundation\Response | |
*/ | |
public function csvAction() | |
{ | |
$list = array( | |
//these are the columns | |
array('Firstname', 'Lastname',), | |
//these are the rows | |
array('Andrei', 'Boar'), | |
array('John', 'Doe') | |
); | |
$fp = fopen('php://output', 'w'); | |
foreach ($list as $fields) { | |
fputcsv($fp, $fields); | |
} | |
$response = new Response(); | |
$response->headers->set('Content-Type', 'text/csv'); | |
//it's gonna output in a testing.csv file | |
$response->headers->set('Content-Disposition', 'attachment; filename="testing.csv"'); | |
return $response; | |
} | |
} |
@Nyholm
Why even put the files in temp file? You can use fopen('php://memory', 'w');
But why even bother with file descriptors. The Response
constructor expects string. So you can do something like:
$csvData = 'email,first_name,last_name' . PHP_EOL;
foreach ($list as $fields) {
$csvData .= implode(',', [$fields['email'], $fields['first_name'], $fields['last_name'])) . PHP_EOL;
}
$response = new Response($csvData);
Good questions.
Using your suggestion or php://memory
will store the data in memory. If you have 100MB of CSV data then your PHP process will take at least 100MB of system memory.
Using php://temp
will automatically switch to use a temp file when the data is more than a few megabytes. There is a php.ini setting for this.
See more in the manual: https://www.php.net/manual/en/wrappers.php.php
But stream_get_contents($fp)
has to load the file in memory anyway?
Correct. But using php://temp
allow you to be smart, ie by using Symfony\Component\HttpFoundation\StreamedResponse
.
Agree
It's also worth to explain why one shouldn't write to php://output
in this case. That stream is the same one as the one used by echo
or print
statements. Obviously, you can also access it via ob_*
functions. The thing is - you've got no idea what's in it or who's going to write to it. I've had a case, where using it caused the Content-Type
of a response to randomly change between text/csv
and text/html
. In other words - weird things happened!
Switching to anything else, in my case to php://temp
(thanks Nyholm!), did the trick.
A bit late, but there is a class called CsvEncoder
. You can do something like this:
use Symfony\Component\Serializer\Encoder\CsvEncoder;
$csvEncoder = new CsvEncoder();
$data = [];
foreach ($people as $person) {
$data[] = [
'First Name' => $person->getFirstName(),
'Last Name' => $person->getLastName(),
];
}
$csv = $csvEncoder->encode($data, 'csv');
The array keys of each element in $data
are the headers. You can change the header order by doing something like this:
$headers = ['Last Name', 'First Name'];
$csv = $csvEncoder->encode($data, 'csv', [
'csv_headers' => $headers,
]);
Thank you for this.
You should avoid writing to php://output directly. Rather put the data in the
$response
object.