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.toupper や string.rot13、convert.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://memoryやphp://tempでメモリ上の仮想ファイルを操作できるstream_context_create()でHTTPリクエストのヘッダーやタイムアウトを制御できる- ストリームフィルタでデータ変換をパイプライン的に適用できる
stream_wrapper_register()でカスタムプロトコルを定義できる
ストリームを理解すると、PHPのI/O処理全体の見通しが格段によくなります。ぜひ日々のコードに取り入れてみてください。