ストリーム出力

通常リソースはレンダラーでレンダリングされて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に画像ファイルのファイルポインタリソースのresource変数を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'); // image base64 format
        $this->body = [
            'name' => $name,
            'image' => $fp
        ];

        return $this;
    }
}

ストリーミングのバンドワイズやタイミングをコントロールしたり、クラウドにアップロードしたり等ストリーミングを更にコントロールする場合にはStreamResponderを参考にして作成して束縛します。

ストリーム出力のdemoがMyVendor.Streamにあります。