stream_context_createとは?
stream_context_create() は、PHPのストリーム操作に対してオプション(設定)やパラメータを付与するコンテキストリソースを生成する関数です。
PHPのストリームとは、ファイル・HTTP・FTP・SSL/TLSなど、あらゆる入出力をひとつの統一されたAPIで扱うための仕組みです。file_get_contents() や fopen() など、多くの組み込み関数はストリームを内部で利用しており、stream_context_create() によって作成したコンテキストをこれらの関数に渡すことで、通信方法・タイムアウト・ヘッダー・SSLオプションなどを細かく制御できます。
基本構文
stream_context_create(array $options = [], array $params = []): resource
| 引数 | 型 | 説明 |
|---|---|---|
$options | array | ラッパー(http, ftp, sslなど)ごとのオプション連想配列 |
$params | array | コンテキスト自体のパラメータ(通知コールバックなど) |
| 戻り値 | resource | 生成されたストリームコンテキストリソース |
$options の基本構造
$options = [
'ラッパー名' => [
'オプション名' => 値,
...
],
];
基本的な使い方
<?php
// 最もシンプルな例:HTTPでPOSTリクエストを送る
$options = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query(['name' => 'Taro', 'age' => 30]),
],
];
$context = stream_context_create($options);
$result = file_get_contents('https://httpbin.org/post', false, $context);
echo $result;
stream_context_create() が返すのはリソース型の値で、file_get_contents() や fopen() の第3引数に渡して使います。
実践クラスサンプル
サンプル1:HTTPクライアントクラス(GETリクエスト)
外部APIにGETリクエストを送り、JSONレスポンスを取得します。
<?php
class SimpleHttpClient
{
private int $timeout;
private string $userAgent;
public function __construct(int $timeout = 10, string $userAgent = 'PHP-Client/1.0')
{
$this->timeout = $timeout;
$this->userAgent = $userAgent;
}
public function get(string $url, array $headers = []): array
{
$headerString = "User-Agent: {$this->userAgent}\r\n";
foreach ($headers as $key => $value) {
$headerString .= "{$key}: {$value}\r\n";
}
$options = [
'http' => [
'method' => 'GET',
'header' => $headerString,
'timeout' => $this->timeout,
'ignore_errors' => true, // エラー時もレスポンスボディを取得
],
];
$context = stream_context_create($options);
$body = file_get_contents($url, false, $context);
$metaData = stream_get_meta_data($GLOBALS['_last_stream'] ?? false);
// $http_response_header はfile_get_contents後に自動セットされるグローバル変数
$statusLine = $http_response_header[0] ?? '';
preg_match('/HTTP\/\d\.\d (\d{3})/', $statusLine, $matches);
$statusCode = (int)($matches[1] ?? 0);
return [
'status' => $statusCode,
'body' => $body,
'headers'=> $http_response_header ?? [],
];
}
}
// 使用例
$client = new SimpleHttpClient(timeout: 15);
$response = $client->get('https://jsonplaceholder.typicode.com/todos/1');
echo "ステータス: {$response['status']}\n";
$data = json_decode($response['body'], true);
echo "タイトル: {$data['title']}\n";
サンプル2:REST APIへのPOSTリクエスト(JSONボディ)
<?php
class JsonApiClient
{
private string $baseUrl;
private string $apiKey;
public function __construct(string $baseUrl, string $apiKey)
{
$this->baseUrl = rtrim($baseUrl, '/');
$this->apiKey = $apiKey;
}
public function post(string $endpoint, array $data): ?array
{
$url = $this->baseUrl . $endpoint;
$body = json_encode($data, JSON_UNESCAPED_UNICODE);
$options = [
'http' => [
'method' => 'POST',
'header' => implode("\r\n", [
'Content-Type: application/json',
'Accept: application/json',
"Authorization: Bearer {$this->apiKey}",
'Content-Length: ' . strlen($body),
]),
'content' => $body,
'timeout' => 30,
'ignore_errors' => true,
],
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
if ($response === false) {
return null;
}
return json_decode($response, true);
}
}
// 使用例
$client = new JsonApiClient('https://jsonplaceholder.typicode.com', 'dummy-token');
$result = $client->post('/posts', [
'title' => 'PHPストリームの解説',
'body' => 'stream_context_createの使い方',
'userId' => 1,
]);
echo "作成されたID: {$result['id']}\n";
echo "タイトル: {$result['title']}\n";
サンプル3:SSL/TLS証明書の検証制御クラス
本番環境・開発環境でSSL検証を切り替えます。
<?php
class SecureHttpFetcher
{
private bool $verifyPeer;
private ?string $caFile;
public function __construct(bool $verifyPeer = true, ?string $caFile = null)
{
$this->verifyPeer = $verifyPeer;
$this->caFile = $caFile;
}
public function fetch(string $url): string|false
{
$sslOptions = [
'verify_peer' => $this->verifyPeer,
'verify_peer_name' => $this->verifyPeer,
'allow_self_signed' => !$this->verifyPeer,
];
if ($this->caFile !== null) {
$sslOptions['cafile'] = $this->caFile;
}
$options = [
'http' => [
'method' => 'GET',
'timeout' => 10,
],
'ssl' => $sslOptions,
];
$context = stream_context_create($options);
return file_get_contents($url, false, $context);
}
}
// 本番環境:SSL検証あり
$fetcher = new SecureHttpFetcher(verifyPeer: true);
$html = $fetcher->fetch('https://www.php.net/');
// 開発環境(自己署名証明書など):SSL検証なし ※本番では使用しないこと
// $fetcher = new SecureHttpFetcher(verifyPeer: false);
echo mb_strlen($html) . " バイト取得\n";
⚠️ セキュリティ注意:
verify_peer: falseは自己署名証明書のローカル開発環境専用です。本番環境では必ずtrueに設定してください。中間者攻撃(MITM)のリスクがあります。
サンプル4:FTPファイルアップロードクラス
<?php
class FtpStreamUploader
{
private string $host;
private string $user;
private string $password;
public function __construct(string $host, string $user, string $password)
{
$this->host = $host;
$this->user = $user;
$this->password = $password;
}
public function upload(string $localPath, string $remotePath): bool
{
if (!file_exists($localPath)) {
throw new RuntimeException("ローカルファイルが見つかりません: {$localPath}");
}
$options = [
'ftp' => [
'overwrite' => true, // 既存ファイルを上書き
],
];
$context = stream_context_create($options);
$url = "ftp://{$this->user}:{$this->password}@{$this->host}/{$remotePath}";
$localContent = file_get_contents($localPath);
$result = file_put_contents($url, $localContent, 0, $context);
return $result !== false;
}
public function download(string $remotePath): string|false
{
$options = [
'ftp' => [
'overwrite' => false,
],
];
$context = stream_context_create($options);
$url = "ftp://{$this->user}:{$this->password}@{$this->host}/{$remotePath}";
return file_get_contents($url, false, $context);
}
}
// 使用例
// $uploader = new FtpStreamUploader('ftp.example.com', 'ftpuser', 'secret');
// $uploader->upload('/tmp/report.csv', 'uploads/report.csv');
サンプル5:リダイレクト制御・プロキシ設定クラス
<?php
class ProxyAwareFetcher
{
private ?string $proxyUrl;
private int $maxRedirects;
public function __construct(?string $proxyUrl = null, int $maxRedirects = 5)
{
$this->proxyUrl = $proxyUrl;
$this->maxRedirects = $maxRedirects;
}
public function fetch(string $url): string|false
{
$httpOptions = [
'method' => 'GET',
'timeout' => 20,
'follow_location' => 1, // リダイレクトを追跡
'max_redirects' => $this->maxRedirects,
'ignore_errors' => true,
];
if ($this->proxyUrl !== null) {
$httpOptions['proxy'] = $this->proxyUrl;
$httpOptions['request_fulluri'] = true; // プロキシ用にフルURIを使用
}
$options = ['http' => $httpOptions];
$context = stream_context_create($options);
return file_get_contents($url, false, $context);
}
public function getLastResponseHeaders(): array
{
return $http_response_header ?? [];
}
}
// 使用例:プロキシなし、最大3回リダイレクトを許可
$fetcher = new ProxyAwareFetcher(maxRedirects: 3);
$content = $fetcher->fetch('https://httpbin.org/redirect/2');
// プロキシ経由の場合
// $fetcher = new ProxyAwareFetcher(proxyUrl: 'tcp://proxy.example.com:8080');
サンプル6:通知コールバック付きの進捗モニタリングクラス
stream_context_create() の第2引数 $params を使い、ストリームイベントをフックします。
<?php
class StreamProgressMonitor
{
private array $log = [];
public function download(string $url): string|false
{
$params = [
'notification' => [$this, 'onNotification'],
];
$options = [
'http' => [
'method' => 'GET',
'timeout' => 30,
'ignore_errors' => true,
],
];
$context = stream_context_create($options, $params);
$result = file_get_contents($url, false, $context);
return $result;
}
public function onNotification(
int $notificationCode,
int $severity,
?string $message,
int $messageCode,
int $bytesTransferred,
int $bytesMax
): void {
switch ($notificationCode) {
case STREAM_NOTIFY_CONNECT:
$this->log[] = "接続を確立しました";
break;
case STREAM_NOTIFY_AUTH_REQUIRED:
$this->log[] = "認証が必要です";
break;
case STREAM_NOTIFY_MIME_TYPE_IS:
$this->log[] = "MIMEタイプ: {$message}";
break;
case STREAM_NOTIFY_FILE_SIZE_IS:
$this->log[] = "ファイルサイズ: {$bytesMax} バイト";
break;
case STREAM_NOTIFY_PROGRESS:
if ($bytesMax > 0) {
$percent = round($bytesTransferred / $bytesMax * 100, 1);
$this->log[] = "進捗: {$bytesTransferred}/{$bytesMax} バイト ({$percent}%)";
}
break;
case STREAM_NOTIFY_COMPLETED:
$this->log[] = "ダウンロード完了";
break;
case STREAM_NOTIFY_FAILURE:
$this->log[] = "エラー: {$message} (コード: {$messageCode})";
break;
}
}
public function getLog(): array
{
return $this->log;
}
}
// 使用例
$monitor = new StreamProgressMonitor();
$content = $monitor->download('https://www.php.net/');
foreach ($monitor->getLog() as $entry) {
echo $entry . "\n";
}
echo "\n取得サイズ: " . strlen($content) . " バイト\n";
サンプル7:Webhookリスナーのテスト送信クラス
POSTで署名ヘッダー付きのWebhookペイロードを送信します。
<?php
class WebhookSender
{
private string $secret;
public function __construct(string $secret)
{
$this->secret = $secret;
}
public function send(string $url, array $payload): array
{
$body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
$signature = 'sha256=' . hash_hmac('sha256', $body, $this->secret);
$timestamp = time();
$options = [
'http' => [
'method' => 'POST',
'header' => implode("\r\n", [
'Content-Type: application/json',
"X-Hub-Signature-256: {$signature}",
"X-Timestamp: {$timestamp}",
'Content-Length: ' . strlen($body),
]),
'content' => $body,
'timeout' => 15,
'ignore_errors' => true,
],
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
$statusLine = $http_response_header[0] ?? '';
preg_match('/HTTP\/\d\.\d (\d{3})/', $statusLine, $matches);
return [
'status' => (int)($matches[1] ?? 0),
'response' => $response,
'signature' => $signature,
];
}
}
// 使用例
$sender = new WebhookSender(secret: 'my-webhook-secret');
$result = $sender->send('https://httpbin.org/post', [
'event' => 'order.created',
'orderId' => 12345,
'amount' => 9800,
]);
echo "ステータス: {$result['status']}\n";
echo "署名: {$result['signature']}\n";
関連関数との比較
| 関数 | 用途 |
|---|---|
stream_context_create() | コンテキストリソースを新規作成 |
stream_context_set_option() | 既存コンテキストにオプションを追加・変更 |
stream_context_get_options() | コンテキストのオプションを取得 |
stream_context_get_params() | コンテキストのパラメータを取得 |
stream_context_get_default() | デフォルトコンテキストを取得 |
stream_context_set_default() | デフォルトコンテキストを設定(全ストリーム関数に影響) |
よく使うオプション一覧
httpラッパー
| オプション | 型 | 説明 |
|---|---|---|
method | string | GET, POST, PUT, DELETE など |
header | string | HTTPリクエストヘッダー(\r\n 区切り) |
content | string | リクエストボディ |
timeout | float | タイムアウト秒数 |
follow_location | int | リダイレクトを追跡するか(1=はい) |
max_redirects | int | 最大リダイレクト回数(デフォルト20) |
ignore_errors | bool | 4xx・5xxでもボディを返すか |
proxy | string | プロキシURL(例:tcp://proxy:8080) |
user_agent | string | User-Agentヘッダー |
sslラッパー
| オプション | 型 | 説明 |
|---|---|---|
verify_peer | bool | サーバー証明書を検証するか |
verify_peer_name | bool | ホスト名を検証するか |
allow_self_signed | bool | 自己署名証明書を許可するか |
cafile | string | CA証明書ファイルのパス |
local_cert | string | クライアント証明書のパス |
まとめ
| 項目 | 内容 |
|---|---|
| 関数名 | stream_context_create() |
| 分類 | ストリーム制御関数 |
| PHP バージョン | PHP 4.3.0以上 |
| 戻り値 | ストリームコンテキストリソース |
| 主な用途 | HTTP/FTP/SSLの細かい通信制御 |
stream_context_create() を使いこなすことで、file_get_contents() や fopen() だけでHTTPヘッダー操作・認証・SSL設定・プロキシ・タイムアウト制御まで幅広く対応できます。cURLを使わなくても高度な通信処理が実現でき、シンプルなスクリプトからクラスベースの設計まで柔軟に活用できる関数です。
外部APIとの連携やWebhook送信など、現代のWeb開発に欠かせない場面で活躍しますので、ぜひ積極的に活用してみてください。
