ストリーム出力

通常、リソースはレンダラーでレンダリングされて1つの文字列になり、最終的にechoで出力されます。しかし、この方法ではPHPのメモリ制限を超えるサイズのコンテンツは出力できません。StreamRendererを使用することでHTTP出力をストリーム化でき、メモリ消費を低く抑えることができます。このストリーム出力は、既存のレンダラーと共存することも可能です。

トランスファーとレンダラーの変更

ストリーム出力用のレンダラーとレスポンダーをインジェクトするために、ページにStreamTransferInjectトレイトをuseします。

以下のダウンロードページの例では、$bodyをストリームのリソース変数としているため、インジェクトされたレンダラーは無視され、リソースが直接ストリーム出力されます:

use BEAR\Streamer\StreamTransferInject;

class Download extends ResourceObject
{
    use StreamTransferInject;

    public $headers = [
        'Content-Type' => 'image/jpeg',
        'Content-Disposition' => 'attachment; filename="image.jpg"'
    ];

    public function onGet(): static
    {
        $fp = fopen(__DIR__ . '/BEAR.jpg', 'r');
        $this->body = $fp;

        return $this;
    }
}

レンダラーとの共存

ストリーム出力は従来のレンダラーと共存できます。通常、TwigレンダラーやJSONレンダラーは文字列を生成しますが、その一部にストリームをアサインすると、全体がストリームとして出力されます。

以下は、Twigテンプレートに文字列とresource変数をアサインして、インライン画像のページを生成する例です。

テンプレート:

<!DOCTYPE html>
<html lang="en">
<body>
<p>Hello, {{ name }}</p>
<img src="data:image/jpg;base64,{{ image }}">
</body>
</html>

nameには通常通り文字列をアサインし、imageには画像ファイルのファイルポインタリソースをbase64-encodeフィルターを通してアサインします:

class Image extends ResourceObject
{
    use StreamTransferInject;

    public function onGet(string $name = 'inline image'): static
    {
        $fp = fopen(__DIR__ . '/image.jpg', 'r');
        stream_filter_append($fp, 'convert.base64-encode'); // 画像をbase64形式に変換
        $this->body = [
            'name' => $name,
            'image' => $fp
        ];

        return $this;
    }
}

ストリーミングの帯域幅やタイミングをコントロールしたり、クラウドにアップロードしたりするなど、ストリーミングをさらに制御する場合は、StreamResponderを参考にして作成し、束縛します。

ストリーム出力のデモはMyVendor.Streamで確認できます。