あっぽログ
← 記事一覧に戻る

PHPのストリームを使いこなす:ファイル・ネットワーク・カスタムストリームの基本から実践まで

PHPのストリームとは

PHPのストリーム(Stream)は、ファイル・ネットワーク・メモリなどさまざまな入出力リソースを統一的に扱う仕組みです。file_get_contents()fopen() を使ったことがあれば、すでにストリームを間接的に利用しています。

ストリームを理解することで、次のようなことが実現できます。

  • ローカルファイルとリモートURLを同じ関数で読み書きする
  • HTTPリクエストのヘッダーやタイムアウトを細かく制御する
  • メモリ上の仮想ファイルを操作する
  • 独自のストリームラッパーを作成してカスタムプロトコルを定義する

ストリームラッパーとストリームフィルタ

PHPのストリームは**スキーマ(ラッパー)**によって扱うリソースの種類が決まります。

スキーマ説明
file://ローカルファイル(省略可)
http:// / https://HTTPリソース
ftp://FTP
php://PHP組み込みの入出力
memory://メモリ上の一時バッファ
data://インラインデータ
<?php
// どちらも同じファイルを読む
$content1 = file_get_contents('file:///var/www/html/sample.txt');
$content2 = file_get_contents('/var/www/html/sample.txt');

php:// ストリームを使う

php:// ラッパーはPHPの特別な入出力チャンネルにアクセスします。

<?php
// 標準入力を1行読む(CLIツールで便利)
$line = fgets(STDIN);

// 標準出力に書き込む
fwrite(STDOUT, "Hello, Stream!\n");

// メモリ上に一時ファイルを作成する
$handle = fopen('php://memory', 'r+');
fwrite($handle, 'テスト文字列');
rewind($handle); // 先頭に戻す
echo fread($handle, 1024); // テスト文字列
fclose($handle);

php://temp は一定サイズを超えると自動的にディスクに書き出してくれるので、大きなデータを扱う際に便利です。

<?php
$handle = fopen('php://temp/maxmemory:' . (1 * 1024 * 1024), 'r+'); // 1MBまでメモリ
fwrite($handle, str_repeat('A', 500000));
rewind($handle);
$data = stream_get_contents($handle);
fclose($handle);

echo strlen($data); // 500000

ストリームコンテキストでHTTPリクエストを制御する

file_get_contents() でHTTPリクエストを送る際、ストリームコンテキストを使うとヘッダーやメソッドを指定できます。

<?php
$options = [
    'http' => [
        'method'  => 'POST',
        'header'  => implode("\r\n", [
            'Content-Type: application/json',
            'Authorization: Bearer your-token-here',
        ]),
        'content' => json_encode(['name' => '太郎', 'age' => 25]),
        'timeout' => 10,
    ],
];

$context = stream_context_create($options);

$response = file_get_contents(
    'https://httpbin.org/post',
    false,
    $context
);

if ($response === false) {
    echo "リクエスト失敗\n";
} else {
    $data = json_decode($response, true);
    echo $data['json']['name']; // 太郎
}

レスポンスヘッダーを取得する

file_get_contents() 実行後、グローバル変数 $http_response_header にヘッダーが格納されます。

<?php
$context = stream_context_create(['http' => ['method' => 'GET']]);
$body = file_get_contents('https://example.com', false, $context);

foreach ($http_response_header as $header) {
    echo $header . "\n";
}
// HTTP/1.1 200 OK
// Content-Type: text/html; charset=UTF-8
// ...

ストリームフィルタで変換処理を挟む

ストリームフィルタを使うと、読み書きの際にデータ変換を自動的に行えます。PHPには string.toupperstring.rot13convert.base64-encode などの組み込みフィルタがあります。

<?php
// ファイル書き込み時にBase64エンコードする
$handle = fopen('encoded.txt', 'w');
stream_filter_append($handle, 'convert.base64-encode');
fwrite($handle, 'PHPのストリームは便利です');
fclose($handle);

// ファイル読み込み時にBase64デコードする
$handle = fopen('encoded.txt', 'r');
stream_filter_append($handle, 'convert.base64-decode');
echo stream_get_contents($handle); // PHPのストリームは便利です
fclose($handle);

カスタムストリームラッパーを作る

stream_wrapper_register() を使うと、独自のストリームラッパーを定義できます。ここでは簡単な例として、upper:// でアクセスすると文字列を大文字に変換するラッパーを作ります。

<?php
class UpperCaseStream
{
    private string $buffer = '';
    private int $position = 0;

    public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
    {
        // upper:// 以降の文字列を取得して大文字に変換
        $data = parse_url($path, PHP_URL_HOST) . parse_url($path, PHP_URL_PATH);
        $this->buffer   = strtoupper(str_replace(['/', '-'], ' ', $data));
        $this->position = 0;
        return true;
    }

    public function stream_read(int $count): string
    {
        $chunk = substr($this->buffer, $this->position, $count);
        $this->position += strlen($chunk);
        return $chunk;
    }

    public function stream_eof(): bool
    {
        return $this->position >= strlen($this->buffer);
    }

    public function stream_stat(): array
    {
        return [];
    }
}

stream_wrapper_register('upper', UpperCaseStream::class);

echo file_get_contents('upper://hello/world'); // HELLO WORLD

実際のプロダクションでは、データベースやキャッシュストレージをストリームとして扱うラッパーを作ることで、既存の fopen() / fread() 系の関数とシームレスに統合できます。


まとめ

PHPのストリームは奥深い機能ですが、要点を整理すると次のとおりです。

  • file:// / http:// / php:// などのラッパーで多様なリソースを統一的に扱える
  • php://memoryphp://temp でメモリ上の仮想ファイルを操作できる
  • stream_context_create() でHTTPリクエストのヘッダーやタイムアウトを制御できる
  • ストリームフィルタでデータ変換をパイプライン的に適用できる
  • stream_wrapper_register() でカスタムプロトコルを定義できる

ストリームを理解すると、PHPのI/O処理全体の見通しが格段によくなります。ぜひ日々のコードに取り入れてみてください。

← 記事一覧に戻る