How to Stream Data in PHP
PHP can stream response using php://output
, which can be useful for handling large data.
php://output is a write-only stream that allows you to write to the output buffer mechanism in the same way as print and echo.
PHP 7.1 Example
Here is a short example which allows users to download CSV files.
The key points are fopen('php://output', 'w');
which opens a stream.
App\Lib\Stream\DownloadStream.php
<?php
namespace App\Lib\Stream;
/**
* Class DownloadStream
* @package App\Lib\Stream
*/
class DownloadStream
{
public static function getStream(string $fileName)
{
// Send header
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename={$fileName}");
// Get output stream
$stream = fopen('php://output', 'w');
// Filter to conver character code to ShiftJIS
stream_filter_prepend($stream, 'convert.iconv.utf-8/cp932');
// Filter to convert newline to CRLF
stream_filter_register('CrlfFilter', 'App\Lib\Stream\Filter\CrlfFilter');
stream_filter_append($stream, 'CrlfFilter');
return $stream;
}
}
App\Lib\Stream\Filter\CrlfFilter.php
This is a stream filter to convert line ending into CRLF. Although this is not directly relevant to streaming data, it would be useful for Windows users.
<?php
namespace App\Lib\Stream\Filter;
/**
* Class CrlfFilter
* @package App\Lib\Stream\Filter
*/
class CrlfFilter extends \php_user_filter
{
/**
* Callback method when this filter is applied
* @param resource $in
* @param resource $out
* @param int $consumed
* @param bool $closing
* @return int
*/
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
// Clear newline codes in advance
$bucket->data = preg_replace("/\r$/", "", $bucket->data);
$bucket->data = preg_replace("/\n$/", "", $bucket->data);
// Append CRLF
$bucket->data = $bucket->data . "\r\n";
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
Usage
Here is an example usage.
// Filename
$fileName = 'test.csv';
// Get download stream
$stream = DownloadStream::getStream($fileName);
// Output header
fputcsv($stream, [
'last_name',
'first_name',
'company',
]);
// Output record
fputcsv($stream, [
'Jane',
'Doe',
'ABC Company',
]);
// Close stream
fclose($stream);
Conclusion
If you want to implement a feature such as downloading a CSV file containing one million records, you can use php://output
.
I hope you will find this post useful.