- はじめに
- 関数の基本情報
- 構文
- 基本的な使い方
- 主なトランスポート一覧
- stream_get_transports() と stream_get_wrappers() / stream_get_filters() の違い
- 実践的なクラスベースの活用例
- 例1:トランスポートレジストリクラス(TransportRegistry)
- 例2:安全な接続ファクトリクラス(SecureSocketFactory)
- 例3:環境診断レポートクラス(TransportDiagnostics)
- 例4:接続戦略セレクタークラス(ConnectionStrategySelector)
- 例5:トランスポート自動フォールバッククライアント(FallbackSocketClient)
- 例6:Unix ドメインソケットサーバー・クライアントクラス(UnixSocketChannel)
- 例7:トランスポート対応マトリクス生成クラス(TransportCompatibilityMatrix)
- 関連する関数との比較
- 注意点とベストプラクティス
- まとめ
はじめに
PHPでソケット通信やネットワーク接続を扱う際、「このサーバー環境では SSL/TLS が使えるか?」「unix:// ドメインソケットは利用可能か?」といった確認が必要になる場面があります。
stream_get_transports() は、現在の PHP 環境で利用可能なソケットトランスポートの名前を配列で一括取得する関数です。fsockopen() や stream_socket_client() で接続する前にトランスポートの存在確認を行うことで、環境依存による接続エラーを未然に防げます。
関数の基本情報
| 項目 | 内容 |
|---|---|
| 関数名 | stream_get_transports() |
| 対応バージョン | PHP 5.0.0 以降 |
| 返り値 | string[](トランスポート名の配列) |
| カテゴリ | ストリーム関数 |
構文
stream_get_transports(): array
パラメータ
なし。
返り値
利用可能なトランスポート名の文字列配列。順序は不定です。
基本的な使い方
<?php
$transports = stream_get_transports();
print_r($transports);
// 出力例(環境により異なる):
// Array
// (
// [0] => tcp
// [1] => udp
// [2] => unix
// [3] => udg
// [4] => ssl
// [5] => tls
// [6] => tlsv1.0
// [7] => tlsv1.1
// [8] => tlsv1.2
// [9] => tlsv1.3
// )
// 特定トランスポートの存在確認
$hasSsl = in_array('ssl', stream_get_transports(), true);
var_dump($hasSsl); // bool(true) or bool(false)
主なトランスポート一覧
| トランスポート名 | 説明 | 要件 |
|---|---|---|
tcp | TCP ソケット接続 | 標準(常に利用可能) |
udp | UDP ソケット接続 | 標準(常に利用可能) |
unix | Unix ドメインソケット(SOCK_STREAM) | Unix 系 OS のみ |
udg | Unix ドメインソケット(SOCK_DGRAM) | Unix 系 OS のみ |
ssl | SSL(OpenSSL の汎用エイリアス) | OpenSSL 拡張が必要 |
tls | TLS(OpenSSL の汎用エイリアス) | OpenSSL 拡張が必要 |
tlsv1.0 | TLS 1.0 | OpenSSL 拡張が必要 |
tlsv1.1 | TLS 1.1 | OpenSSL 拡張が必要 |
tlsv1.2 | TLS 1.2 | OpenSSL 拡張が必要 |
tlsv1.3 | TLS 1.3 | OpenSSL 1.1.1 以降 + PHP 7.4 以降 |
stream_get_transports() と stream_get_wrappers() / stream_get_filters() の違い
| 関数 | 返す内容 | 用途例 |
|---|---|---|
stream_get_transports() | ソケット接続方式の一覧 | ssl://・tcp://・unix:// でのソケット接続 |
stream_get_wrappers() | ストリームラッパーの一覧 | fopen('http://...')・fopen('php://memory') |
stream_get_filters() | ストリームフィルターの一覧 | stream_filter_append() でのデータ変換 |
// トランスポート → fsockopen() / stream_socket_client() で使うプレフィックス
$sock = stream_socket_client('ssl://example.com:443');
// ラッパー → fopen() の URL スキームで使う
$fp = fopen('https://example.com/', 'r');
実践的なクラスベースの活用例
例1:トランスポートレジストリクラス(TransportRegistry)
利用可能なトランスポートをカテゴリ別に分類・管理し、存在確認や SSL/TLS サポートの有無を一括チェックするレジストリクラスです。
<?php
class TransportRegistry
{
private array $available;
/** SSL/TLS 系トランスポート名 */
private const SSL_TRANSPORTS = ['ssl', 'tls', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'];
/** Unix ドメインソケット系 */
private const UNIX_TRANSPORTS = ['unix', 'udg'];
public function __construct()
{
$this->available = stream_get_transports();
}
/** トランスポートが利用可能か確認する */
public function has(string $transport): bool
{
return in_array(strtolower($transport), $this->available, true);
}
/** SSL/TLS が少なくとも1つ利用可能か確認する */
public function hasSsl(): bool
{
return !empty(array_intersect(self::SSL_TRANSPORTS, $this->available));
}
/** 利用可能な SSL/TLS バージョンを返す */
public function getSslVersions(): array
{
return array_values(array_intersect(self::SSL_TRANSPORTS, $this->available));
}
/** 最新の推奨 TLS バージョンを返す(tlsv1.3 > tlsv1.2 > tls の優先順) */
public function getBestTls(): ?string
{
foreach (['tlsv1.3', 'tlsv1.2', 'tls', 'ssl'] as $candidate) {
if ($this->has($candidate)) return $candidate;
}
return null;
}
/** Unix ドメインソケットが利用可能か確認する */
public function hasUnixSocket(): bool
{
return $this->has('unix');
}
/** カテゴリ別に分類して返す */
public function grouped(): array
{
$groups = [
'tcp_udp' => [],
'ssl_tls' => [],
'unix' => [],
'other' => [],
];
foreach ($this->available as $t) {
if (in_array($t, ['tcp', 'udp'], true)) {
$groups['tcp_udp'][] = $t;
} elseif (in_array($t, self::SSL_TRANSPORTS, true)) {
$groups['ssl_tls'][] = $t;
} elseif (in_array($t, self::UNIX_TRANSPORTS, true)) {
$groups['unix'][] = $t;
} else {
$groups['other'][] = $t;
}
}
return array_filter($groups);
}
public function all(): array { return $this->available; }
public function count(): int { return count($this->available); }
}
// 使用例
$registry = new TransportRegistry();
echo "総トランスポート数: " . $registry->count() . PHP_EOL;
echo "SSL利用可能: " . ($registry->hasSsl() ? 'YES' : 'NO') . PHP_EOL;
echo "Unixソケット利用可能: " . ($registry->hasUnixSocket() ? 'YES' : 'NO') . PHP_EOL;
echo "推奨TLS: " . ($registry->getBestTls() ?? '利用不可') . PHP_EOL;
echo "SSL/TLSバージョン: " . implode(', ', $registry->getSslVersions()) . PHP_EOL;
echo PHP_EOL . "カテゴリ別:" . PHP_EOL;
foreach ($registry->grouped() as $category => $transports) {
echo " [{$category}] " . implode(', ', $transports) . PHP_EOL;
}
例2:安全な接続ファクトリクラス(SecureSocketFactory)
stream_get_transports() で TLS の利用可否を確認し、最適なトランスポートを自動選択してソケット接続を生成するファクトリクラスです。
<?php
class SocketConnectionException extends \RuntimeException {}
class SecureSocketFactory
{
private TransportRegistry $registry;
/** デフォルトのソケットコンテキストオプション */
private array $defaultContextOptions = [
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
],
];
public function __construct()
{
$this->registry = new TransportRegistry();
}
/**
* TLS 接続を生成する(利用可能な最新バージョンを自動選択)
*
* @throws SocketConnectionException TLS 非対応または接続失敗時
*/
public function createTls(
string $host,
int $port = 443,
int $timeout = 10,
array $contextOptions = []
): mixed {
$transport = $this->registry->getBestTls();
if ($transport === null) {
throw new SocketConnectionException(
'SSL/TLS トランスポートが利用できません。OpenSSL 拡張を確認してください。'
);
}
$options = array_merge_recursive($this->defaultContextOptions, $contextOptions);
$context = stream_context_create($options);
$address = "{$transport}://{$host}:{$port}";
$errno = 0;
$errstr = '';
$socket = @stream_socket_client(
$address,
$errno,
$errstr,
$timeout,
STREAM_CLIENT_CONNECT,
$context
);
if ($socket === false) {
throw new SocketConnectionException(
"TLS 接続に失敗しました [{$address}]: ({$errno}) {$errstr}"
);
}
stream_set_timeout($socket, $timeout);
return $socket;
}
/**
* TCP 接続を生成する
*
* @throws SocketConnectionException 接続失敗時
*/
public function createTcp(string $host, int $port, int $timeout = 10): mixed
{
if (!$this->registry->has('tcp')) {
throw new SocketConnectionException('TCP トランスポートが利用できません。');
}
$errno = 0;
$errstr = '';
$socket = @stream_socket_client(
"tcp://{$host}:{$port}",
$errno,
$errstr,
$timeout
);
if ($socket === false) {
throw new SocketConnectionException(
"TCP 接続に失敗しました [tcp://{$host}:{$port}]: ({$errno}) {$errstr}"
);
}
stream_set_timeout($socket, $timeout);
return $socket;
}
/**
* Unix ドメインソケット接続を生成する
*
* @throws SocketConnectionException Unix ソケット非対応または接続失敗時
*/
public function createUnix(string $socketPath, int $timeout = 5): mixed
{
if (!$this->registry->has('unix')) {
throw new SocketConnectionException(
'Unix ドメインソケットはこの環境では利用できません。'
);
}
if (!file_exists($socketPath)) {
throw new SocketConnectionException(
"ソケットファイルが見つかりません: {$socketPath}"
);
}
$errno = 0;
$errstr = '';
$socket = @stream_socket_client(
"unix://{$socketPath}",
$errno,
$errstr,
$timeout
);
if ($socket === false) {
throw new SocketConnectionException(
"Unix ソケット接続に失敗しました [{$socketPath}]: ({$errno}) {$errstr}"
);
}
return $socket;
}
}
// 使用例
$factory = new SecureSocketFactory();
try {
// TLS 接続(実環境では実際のホストを指定)
// $socket = $factory->createTls('smtp.gmail.com', 465);
// fwrite($socket, "EHLO localhost\r\n");
// echo fread($socket, 1024);
// fclose($socket);
echo "SecureSocketFactory: TLS接続の準備完了" . PHP_EOL;
} catch (SocketConnectionException $e) {
echo "接続エラー: " . $e->getMessage() . PHP_EOL;
}
例3:環境診断レポートクラス(TransportDiagnostics)
現在の PHP 環境におけるトランスポートの対応状況を診断し、ネットワーク機能の利用可否を一覧レポートするクラスです。
<?php
class TransportDiagnostics
{
/** アプリケーションが必要とするトランスポート定義 */
private array $requirements;
public function __construct(array $requirements = [])
{
$this->requirements = $requirements;
}
public function run(): array
{
$available = stream_get_transports();
return [
'available' => $available,
'total' => count($available),
'ssl_versions' => array_values(array_filter($available, fn($t) =>
in_array($t, ['ssl', 'tls', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'], true)
)),
'has_tcp' => in_array('tcp', $available, true),
'has_udp' => in_array('udp', $available, true),
'has_unix' => in_array('unix', $available, true),
'has_ssl' => !empty(array_intersect(['ssl', 'tls'], $available)),
'requirements' => $this->checkRequirements($available),
'openssl' => extension_loaded('openssl'),
'openssl_ver' => defined('OPENSSL_VERSION_TEXT') ? OPENSSL_VERSION_TEXT : 'N/A',
];
}
private function checkRequirements(array $available): array
{
$results = [];
foreach ($this->requirements as $transport) {
$results[$transport] = in_array($transport, $available, true) ? 'OK' : 'MISSING';
}
return $results;
}
public function printReport(): void
{
$r = $this->run();
echo "=== Transport Diagnostics ===" . PHP_EOL;
echo "利用可能トランスポート数: {$r['total']}" . PHP_EOL;
echo "全一覧: " . implode(', ', $r['available']) . PHP_EOL;
echo PHP_EOL . "--- 基本プロトコル ---" . PHP_EOL;
echo " TCP : " . ($r['has_tcp'] ? 'OK' : 'UNAVAILABLE') . PHP_EOL;
echo " UDP : " . ($r['has_udp'] ? 'OK' : 'UNAVAILABLE') . PHP_EOL;
echo " Unix : " . ($r['has_unix'] ? 'OK' : 'UNAVAILABLE(Windows環境など)') . PHP_EOL;
echo PHP_EOL . "--- SSL/TLS ---" . PHP_EOL;
echo " OpenSSL拡張: " . ($r['openssl'] ? 'OK' : 'NOT LOADED') . PHP_EOL;
echo " バージョン : " . $r['openssl_ver'] . PHP_EOL;
if (!empty($r['ssl_versions'])) {
foreach ($r['ssl_versions'] as $v) {
echo " [{$v}] OK" . PHP_EOL;
}
} else {
echo " SSL/TLS 非対応" . PHP_EOL;
}
if (!empty($r['requirements'])) {
echo PHP_EOL . "--- 必須トランスポート確認 ---" . PHP_EOL;
foreach ($r['requirements'] as $t => $status) {
echo " [{$status}] {$t}" . PHP_EOL;
}
}
}
}
// 使用例
$diag = new TransportDiagnostics(
requirements: ['tcp', 'tlsv1.2', 'tlsv1.3', 'unix']
);
$diag->printReport();
// 出力例:
// === Transport Diagnostics ===
// 利用可能トランスポート数: 10
// 全一覧: tcp, udp, unix, udg, ssl, tls, tlsv1.0, tlsv1.1, tlsv1.2, tlsv1.3
//
// --- 基本プロトコル ---
// TCP : OK
// UDP : OK
// Unix : OK
//
// --- SSL/TLS ---
// OpenSSL拡張: OK
// バージョン : OpenSSL 3.0.2 15 Mar 2022
// [ssl] OK
// [tls] OK
// [tlsv1.2] OK
// [tlsv1.3] OK
//
// --- 必須トランスポート確認 ---
// [OK] tcp
// [OK] tlsv1.2
// [OK] tlsv1.3
// [OK] unix
例4:接続戦略セレクタークラス(ConnectionStrategySelector)
stream_get_transports() で環境を確認し、「Unix ドメインソケット優先→TLS→TCP」のような優先順位に従って接続方法を自動選択するクラスです。
<?php
class ConnectionStrategy
{
public function __construct(
public readonly string $transport,
public readonly string $address,
public readonly string $description,
public readonly int $priority,
) {}
public function __toString(): string
{
return "[{$this->transport}] {$this->address} - {$this->description}";
}
}
class ConnectionStrategySelector
{
private array $available;
public function __construct()
{
$this->available = stream_get_transports();
}
/**
* Redis への接続戦略を優先順に返す
*/
public function forRedis(
string $host = '127.0.0.1',
int $port = 6379,
string $unixSocket = '/var/run/redis/redis.sock'
): array {
$strategies = [];
// Unix ドメインソケットが最速(同一ホスト時)
if (in_array('unix', $this->available, true) && file_exists($unixSocket)) {
$strategies[] = new ConnectionStrategy(
transport: 'unix',
address: "unix://{$unixSocket}",
description: 'Unix ドメインソケット(最速)',
priority: 1,
);
}
// TLS 暗号化 TCP
foreach (['tlsv1.3', 'tlsv1.2', 'tls'] as $tls) {
if (in_array($tls, $this->available, true)) {
$strategies[] = new ConnectionStrategy(
transport: $tls,
address: "{$tls}://{$host}:{$port}",
description: 'TLS暗号化TCP接続',
priority: 2,
);
break;
}
}
// 平文 TCP(最後の手段)
if (in_array('tcp', $this->available, true)) {
$strategies[] = new ConnectionStrategy(
transport: 'tcp',
address: "tcp://{$host}:{$port}",
description: '平文TCP(非推奨・開発環境のみ)',
priority: 3,
);
}
usort($strategies, fn($a, $b) => $a->priority <=> $b->priority);
return $strategies;
}
/**
* メール送信(SMTP)の接続戦略を優先順に返す
*/
public function forSmtp(string $host, int $port = 587): array
{
$strategies = [];
if (in_array('tlsv1.3', $this->available, true)) {
$strategies[] = new ConnectionStrategy('tlsv1.3', "tlsv1.3://{$host}:{$port}", 'SMTPS TLS1.3', 1);
}
if (in_array('tlsv1.2', $this->available, true)) {
$strategies[] = new ConnectionStrategy('tlsv1.2', "tlsv1.2://{$host}:465", 'SMTPS TLS1.2', 2);
}
if (in_array('tcp', $this->available, true)) {
$strategies[] = new ConnectionStrategy('tcp', "tcp://{$host}:587", 'STARTTLS(平文で開始)', 3);
}
return $strategies;
}
}
// 使用例
$selector = new ConnectionStrategySelector();
echo "=== Redis 接続戦略 ===" . PHP_EOL;
foreach ($selector->forRedis() as $strategy) {
echo " {$strategy}" . PHP_EOL;
}
echo PHP_EOL . "=== SMTP 接続戦略 ===" . PHP_EOL;
foreach ($selector->forSmtp('smtp.example.com') as $strategy) {
echo " {$strategy}" . PHP_EOL;
}
// 出力例(Unix ソケットファイルが存在する場合):
// === Redis 接続戦略 ===
// [unix] unix:///var/run/redis/redis.sock - Unix ドメインソケット(最速)
// [tlsv1.3] tlsv1.3://127.0.0.1:6379 - TLS暗号化TCP接続
// [tcp] tcp://127.0.0.1:6379 - 平文TCP(非推奨・開発環境のみ)
//
// === SMTP 接続戦略 ===
// [tlsv1.3] tlsv1.3://smtp.example.com:587 - SMTPS TLS1.3
// [tlsv1.2] tlsv1.2://smtp.example.com:465 - SMTPS TLS1.2
// [tcp] tcp://smtp.example.com:587 - STARTTLS(平文で開始)
例5:トランスポート自動フォールバッククライアント(FallbackSocketClient)
接続試行時にトランスポートを順番に試し、成功するまで自動フォールバックするクライアントクラスです。
<?php
class FallbackSocketClient
{
private array $available;
private ?mixed $socket = null;
private string $usedTransport = '';
private array $attemptLog = [];
public function __construct(
private int $timeout = 10
) {
$this->available = stream_get_transports();
}
/**
* ホスト・ポートに対してトランスポート候補を順番に試して接続する
*
* @param string[] $transportPriority 試行するトランスポートの優先順リスト
* @return bool 接続成功なら true
*/
public function connect(string $host, int $port, array $transportPriority): bool
{
$this->socket = null;
$this->usedTransport = '';
$this->attemptLog = [];
foreach ($transportPriority as $transport) {
// 環境で利用不可なら即スキップ
if (!in_array($transport, $this->available, true)) {
$this->attemptLog[] = ['transport' => $transport, 'result' => 'SKIPPED(利用不可)'];
continue;
}
$context = $this->buildContext($transport);
$address = "{$transport}://{$host}:{$port}";
$errno = 0;
$errstr = '';
$sock = @stream_socket_client(
$address,
$errno,
$errstr,
$this->timeout,
STREAM_CLIENT_CONNECT,
$context
);
if ($sock !== false) {
$this->socket = $sock;
$this->usedTransport = $transport;
$this->attemptLog[] = ['transport' => $transport, 'result' => 'SUCCESS'];
stream_set_timeout($sock, $this->timeout);
return true;
}
$this->attemptLog[] = [
'transport' => $transport,
'result' => "FAILED: ({$errno}) {$errstr}",
];
}
return false;
}
private function buildContext(string $transport): mixed
{
$isSsl = in_array($transport, ['ssl', 'tls', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'], true);
if (!$isSsl) return stream_context_create();
return stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
],
]);
}
public function write(string $data): int|false
{
return $this->socket ? fwrite($this->socket, $data) : false;
}
public function read(int $length = 4096): string|false
{
return $this->socket ? fread($this->socket, $length) : false;
}
public function close(): void
{
if ($this->socket) {
fclose($this->socket);
$this->socket = null;
}
}
public function getUsedTransport(): string { return $this->usedTransport; }
public function isConnected(): bool { return $this->socket !== null; }
public function printLog(): void
{
echo "=== 接続試行ログ ===" . PHP_EOL;
foreach ($this->attemptLog as $log) {
echo " [{$log['transport']}] {$log['result']}" . PHP_EOL;
}
}
}
// 使用例
$client = new FallbackSocketClient(timeout: 5);
$connected = $client->connect('example.com', 443, [
'tlsv1.3', // 最優先
'tlsv1.2', // フォールバック1
'tls', // フォールバック2
'ssl', // フォールバック3
'tcp', // 最終フォールバック(非推奨)
]);
$client->printLog();
if ($connected) {
echo "接続成功: " . $client->getUsedTransport() . " トランスポートを使用" . PHP_EOL;
$client->close();
} else {
echo "全トランスポートで接続失敗" . PHP_EOL;
}
例6:Unix ドメインソケットサーバー・クライアントクラス(UnixSocketChannel)
unix トランスポートの利用可否を確認した上で、プロセス間通信用の Unix ドメインソケットサーバーとクライアントを構築するクラスです。
<?php
class UnixSocketNotAvailableException extends \RuntimeException {}
class UnixSocketChannel
{
private string $socketPath;
/**
* @throws UnixSocketNotAvailableException Unix ソケット非対応環境の場合
*/
public function __construct(string $socketPath)
{
if (!in_array('unix', stream_get_transports(), true)) {
throw new UnixSocketNotAvailableException(
'Unix ドメインソケット(unix://)はこの環境では利用できません。' . PHP_EOL .
'利用可能なトランスポート: ' . implode(', ', stream_get_transports())
);
}
$this->socketPath = $socketPath;
}
/**
* サーバーソケットを作成して返す
*/
public function createServer(int $backlog = 5): mixed
{
// 既存のソケットファイルを削除
if (file_exists($this->socketPath)) {
unlink($this->socketPath);
}
$errno = 0;
$errstr = '';
$server = stream_socket_server(
"unix://{$this->socketPath}",
$errno,
$errstr,
STREAM_SERVER_BIND | STREAM_SERVER_LISTEN
);
if ($server === false) {
throw new \RuntimeException("サーバー作成失敗: ({$errno}) {$errstr}");
}
chmod($this->socketPath, 0600);
return $server;
}
/**
* クライアント接続を確立して返す
*/
public function createClient(int $timeout = 5): mixed
{
$errno = 0;
$errstr = '';
$client = stream_socket_client(
"unix://{$this->socketPath}",
$errno,
$errstr,
$timeout
);
if ($client === false) {
throw new \RuntimeException(
"クライアント接続失敗 [{$this->socketPath}]: ({$errno}) {$errstr}"
);
}
stream_set_timeout($client, $timeout);
return $client;
}
public function getSocketPath(): string { return $this->socketPath; }
}
// 使用例(Unix ソケットの動作確認)
try {
$channel = new UnixSocketChannel('/tmp/php_ipc.sock');
// サーバー側(本来は別プロセス)
$server = $channel->createServer();
echo "サーバー起動: {$channel->getSocketPath()}" . PHP_EOL;
// クライアント接続
$client = $channel->createClient();
fwrite($client, "HELLO FROM CLIENT\n");
// サーバーが接続を受け付けて読む
$conn = stream_socket_accept($server, 1);
if ($conn) {
$msg = fread($conn, 256);
echo "サーバー受信: " . trim($msg) . PHP_EOL;
fwrite($conn, "ACK\n");
fclose($conn);
}
// クライアントが応答を読む
echo "クライアント受信: " . trim(fread($client, 256)) . PHP_EOL;
fclose($client);
fclose($server);
unlink('/tmp/php_ipc.sock');
} catch (UnixSocketNotAvailableException $e) {
echo "Unix ソケット非対応: " . $e->getMessage() . PHP_EOL;
}
// 出力:
// サーバー起動: /tmp/php_ipc.sock
// サーバー受信: HELLO FROM CLIENT
// クライアント受信: ACK
例7:トランスポート対応マトリクス生成クラス(TransportCompatibilityMatrix)
複数のサーバー環境(本番・ステージング・開発)で stream_get_transports() の結果を比較し、環境間の差異を可視化するマトリクスを生成するクラスです。
<?php
class TransportCompatibilityMatrix
{
/** @var array<string, string[]> 環境名 => トランスポート配列 */
private array $environments = [];
/**
* 環境を追加する
*
* @param string[] $transports stream_get_transports() の結果
*/
public function addEnvironment(string $name, array $transports): void
{
$this->environments[$name] = $transports;
}
/**
* 現在の実行環境を自動追加する
*/
public function addCurrent(string $name = 'current'): void
{
$this->addEnvironment($name, stream_get_transports());
}
/**
* 全環境で共通して使えるトランスポートを返す
*/
public function getCommon(): array
{
if (empty($this->environments)) return [];
return array_values(array_intersect(...array_values($this->environments)));
}
/**
* いずれかの環境にしか存在しないトランスポートを返す
*
* @return array<string, string[]> トランスポート名 => 利用可能な環境名の配列
*/
public function getUnique(): array
{
$all = array_unique(array_merge(...array_values($this->environments)));
$unique = [];
foreach ($all as $transport) {
$available = [];
foreach ($this->environments as $envName => $transports) {
if (in_array($transport, $transports, true)) {
$available[] = $envName;
}
}
if (count($available) < count($this->environments)) {
$unique[$transport] = $available;
}
}
return $unique;
}
/**
* 互換性マトリクスをテーブル形式で出力する
*/
public function printMatrix(): void
{
$all = array_unique(array_merge(...array_values($this->environments)));
$envNames = array_keys($this->environments);
sort($all);
// ヘッダー
$header = sprintf("%-15s", 'Transport');
foreach ($envNames as $env) {
$header .= sprintf(" %-12s", $env);
}
echo $header . PHP_EOL;
echo str_repeat('-', strlen($header)) . PHP_EOL;
// 各トランスポート行
foreach ($all as $transport) {
$row = sprintf("%-15s", $transport);
foreach ($envNames as $env) {
$mark = in_array($transport, $this->environments[$env], true) ? '✓' : '✗';
$row .= sprintf(" %-12s", $mark);
}
echo $row . PHP_EOL;
}
echo PHP_EOL . "共通トランスポート: " . implode(', ', $this->getCommon()) . PHP_EOL;
$unique = $this->getUnique();
if (!empty($unique)) {
echo "環境差異:" . PHP_EOL;
foreach ($unique as $transport => $envs) {
echo " {$transport}: " . implode(', ', $envs) . " のみ" . PHP_EOL;
}
}
}
}
// 使用例
$matrix = new TransportCompatibilityMatrix();
// 各環境のトランスポート情報を登録(実際は各サーバーで stream_get_transports() の結果を収集)
$matrix->addEnvironment('production', ['tcp', 'udp', 'ssl', 'tls', 'tlsv1.2', 'tlsv1.3']);
$matrix->addEnvironment('staging', ['tcp', 'udp', 'ssl', 'tls', 'tlsv1.2', 'tlsv1.3', 'unix']);
$matrix->addEnvironment('local', ['tcp', 'udp', 'ssl', 'tls', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3', 'unix', 'udg']);
$matrix->printMatrix();
// 出力例:
// Transport production staging local
// --------------------------------------------------
// ssl ✓ ✓ ✓
// tcp ✓ ✓ ✓
// tls ✓ ✓ ✓
// tlsv1.0 ✗ ✗ ✓
// tlsv1.1 ✗ ✗ ✓
// tlsv1.2 ✓ ✓ ✓
// tlsv1.3 ✓ ✓ ✓
// udg ✗ ✗ ✓
// udp ✓ ✓ ✓
// unix ✗ ✓ ✓
//
// 共通トランスポート: ssl, tcp, tls, tlsv1.2, tlsv1.3, udp
// 環境差異:
// tlsv1.0: local のみ
// tlsv1.1: local のみ
// udg: local のみ
// unix: staging, local のみ
関連する関数との比較
| 関数 | 役割 |
|---|---|
stream_get_transports() | 利用可能なソケットトランスポート名の一覧を返す |
stream_get_wrappers() | 利用可能なストリームラッパー名の一覧を返す |
stream_get_filters() | 利用可能なストリームフィルター名の一覧を返す |
stream_socket_client() | トランスポートを使ってクライアントソケットを接続する |
stream_socket_server() | トランスポートを使ってサーバーソケットを作成する |
stream_get_meta_data() | 開いたストリームのメタ情報(モード・タイムアウト等)を返す |
注意点とベストプラクティス
1. tlsv1.0 / tlsv1.1 は非推奨
TLS 1.0・1.1 は現在セキュリティ上の理由で非推奨とされており、多くのサーバーで無効化されています。新規実装では tlsv1.2 以上を使いましょう。
// 推奨:tlsv1.2 以上
$preferred = array_intersect(['tlsv1.3', 'tlsv1.2'], stream_get_transports());
2. ssl と tls はエイリアス
ssl はシステムの OpenSSL 設定に従った TLS バージョンのエイリアス、tls は TLS 汎用エイリアスです。バージョンを明示したい場合は tlsv1.2 / tlsv1.3 を使いましょう。
3. Windows 環境では unix が存在しない
Unix ドメインソケットは Windows ではデフォルトで利用不可のため、クロスプラットフォームなコードでは事前確認が必須です。
if (in_array('unix', stream_get_transports(), true)) {
// Unix ドメインソケットを使う処理
} else {
// TCP フォールバック
}
4. 結果はキャッシュして使い回す
stream_get_transports() の結果は実行中に変わることはありません。ループ内で繰り返し呼ぶ代わりに変数に保存して使い回しましょう。
// NG
foreach ($hosts as $host) {
if (in_array('tls', stream_get_transports())) { ... }
}
// OK
$transports = stream_get_transports();
foreach ($hosts as $host) {
if (in_array('tls', $transports)) { ... }
}
まとめ
| ポイント | 内容 |
|---|---|
| 基本動作 | 現在の PHP 環境で使えるソケットトランスポート名を配列で返す |
| 引数 | なし |
| 返り値 | string[](順序不定) |
| 主な用途 | SSL/TLS 対応確認・Unix ソケット可否・接続方式の自動選択 |
| TLS 推奨 | tlsv1.2 / tlsv1.3 を優先。tlsv1.0 / tlsv1.1 は非推奨 |
| Windows 注意 | unix / udg は Windows 環境では通常利用不可 |
| キャッシュ | 結果は実行中に変わらないので変数に保存して再利用する |
| 活用シーン | 環境診断・接続ファクトリ・自動フォールバック・互換性マトリクスなど |
stream_get_transports() はシンプルな API ながら、環境依存のネットワーク接続を安全・堅牢に構築するための重要な起点となります。stream_get_wrappers() / stream_get_filters() と合わせてストリーム系の三兄弟として活用することで、PHP のストリーム機能を最大限に引き出せます。
