How to Stream Data in PHP

How to Stream Data in PHP

Takahiro Iwasa
Takahiro Iwasa
2 min read
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.

Takahiro Iwasa

Takahiro Iwasa

Software Developer at KAKEHASHI Inc.
Involved in the requirements definition, design, and development of cloud-native applications using AWS. Now, building a new prescription data collection platform at KAKEHASHI Inc. Japan AWS Top Engineers 2020-2023.