Guide to Streaming Data in PHP

Guide to Streaming Data in PHP

Takahiro Iwasa
Takahiro Iwasa
2 min read
PHP

Introduction

Streaming data in PHP is a powerful technique, especially for handling large datasets. Using php://output, you can write directly to the output buffer, bypassing memory constraints.

Why Stream Data

Streaming data ensures efficient memory usage and is particularly useful when dealing with large files like CSVs, JSON, or XML.

Example: Streaming CSV Data in PHP

Below is an example of creating a downloadable CSV file. The core functionality uses fopen('php://output', 'w'); to open a stream.

App\Lib\Stream\DownloadStream.php

<?php

namespace App\Lib\Stream;

/**
 * Class DownloadStream
 * Provides utilities for streaming downloadable content.
 *
 * @package App\Lib\Stream
 */
class DownloadStream
{
    public static function getStream(string $fileName)
    {
        // Send headers for file download
        header("Content-Type: application/octet-stream");
        header("Content-Disposition: attachment; filename={$fileName}");

        // Open an output stream
        $stream = fopen('php://output', 'w');

        // Apply filters
        stream_filter_prepend($stream, 'convert.iconv.utf-8/cp932'); // Convert to ShiftJIS
        stream_filter_register('CrlfFilter', 'App\Lib\Stream\Filter\CrlfFilter');
        stream_filter_append($stream, 'CrlfFilter'); // Convert line endings to CRLF

        return $stream;
    }
}

Creating a Custom Stream Filter for Line Endings

For Windows users, converting line endings to CRLF is often essential. Here’s how you can create a custom filter.

App\Lib\Stream\Filter\CrlfFilter.php

<?php

namespace App\Lib\Stream\Filter;

/**
 * Class CrlfFilter
 * Converts line endings to CRLF format.
 *
 * @package App\Lib\Stream\Filter
 */
class CrlfFilter extends \php_user_filter
{
    public function filter($in, $out, &$consumed, $closing)
    {
        while ($bucket = stream_bucket_make_writeable($in)) {
            // Normalize line endings
            $bucket->data = preg_replace("/\r$/", "", $bucket->data);
            $bucket->data = preg_replace("/\n$/", "", $bucket->data);

            // Append CRLF
            $bucket->data .= "\r\n";

            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

Usage Example

The following code demonstrates how to create and download a CSV file using the DownloadStream utility.

// Define the file name
$fileName = 'example.csv';

// Get the download stream
$stream = DownloadStream::getStream($fileName);

// Write the CSV header
fputcsv($stream, [
    'last_name',
    'first_name',
    'company',
]);

// Write a CSV record
fputcsv($stream, [
    'Jane',
    'Doe',
    'ABC Company',
]);

// Close the stream
fclose($stream);

Conclusion

Streaming data with php://output is an efficient way to handle large files without overwhelming memory. This approach is particularly suited for scenarios like exporting large datasets to CSV. By adding custom filters, you can further tailor the stream to specific requirements, such as encoding or line-ending conversions.

Happy Coding! 🚀

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.