本文目录导读:

- 什么是PHP流(Streams)?
- 上下文选项(Context Options)
- 上下文参数(Context Parameters)
- 自定义流包装器
- 实战:完整HTTP客户端(带错误处理)
- 流 vs Curl:如何选择?
- 总结知识图谱
在PHP中,流(Streams) 和上下文选项(Context Options) 是非常强大但常被低估的功能,它们为PHP提供了统一的文件、网络、压缩等资源访问抽象层。
以下是关于PHP流与上下文选项的详细解释和实战指南。
什么是PHP流(Streams)?
简单说,流是一个资源对象,它表现出可流式的行为(可读、可写、可查找),PHP将所有类似文件的操作(读文件、写文件、HTTP请求、FTP连接、压缩文件、内存数据等)都抽象为“流”。
流式操作的特点
- 低内存占用:不需要一次性把整个文件加载到内存,可以边读边处理(适合大文件)。
- 统一接口:
fopen,fread,fwrite,fclose可应用于几乎任何数据源。
常见的流封装协议(Wrapper)
PHP内置了许多“流包装器”,通过指定协议来访问不同类型的资源:
| 协议 | 用途 | 示例 |
|---|---|---|
file:// |
本地文件系统(默认) | fopen('file:///tmp/test.txt', 'r') |
http:// / https:// |
HTTP/HTTPS 请求 | fopen('http://example.com', 'r') |
ftp:// / ftps:// |
FTP 连接 | fopen('ftp://user:pass@host/file', 'w') |
php:// |
访问各种I/O流 | php://stdin, php://output, php://memory |
compress.zlib:// |
读取/写入gzip压缩文件 | fopen('compress.zlib://file.gz', 'r') |
data:// |
内联数据(类似data URI) | fopen('data://text/plain;base64,...', 'r') |
phar:// |
访问Phar归档包内的文件 | fopen('phar://app.phar/config.php', 'r') |
极其实用的 php:// 流:
php://stdin/php://stdout/php://stderr:标准输入/输出/错误php://input:读取HTTP POST请求的原始body(常用于API接收JSON)php://output:写入HTTP响应body(相当于echo)php://memory/php://temp:虚拟临时内存/文件存储
// 将POST原始请求体(如JSON)转为数组
$json = file_get_contents('php://input');
$data = json_decode($json, true);
// 向内存写入临时数据(无需真实文件)
$temp = fopen('php://memory', 'r+');
fwrite($temp, 'Hello Stream!');
rewind($temp);
echo fread($temp, 1024); // 输出: Hello Stream!
上下文选项(Context Options)
当使用流函数(fopen, file_get_contents, stream_context_create 等)时,可以通过流上下文(Stream Context) 来修改协议的行为。
流上下文是一个关联数组,包含:
- 选项:针对特定协议的配置(如HTTP头、超时、代理)
- 参数:通知回调、读/写缓冲区大小等
如何使用上下文
// 1. 创建上下文数组
$options = [
'http' => [
'method' => 'GET',
'header' => "User-Agent: MyApp/1.0\r\nAccept: application/json",
'timeout' => 10
]
];
// 2. 创建上下文资源
$context = stream_context_create($options);
// 3. 将上下文传递给流函数
$result = file_get_contents('https://api.example.com/data', false, $context);
常见协议选项详解
HTTP/HTTPS 常用选项
| 选项 | 说明 | 示例值 |
|---|---|---|
method |
请求方法 | 'GET', 'POST', 'DELETE' |
header |
请求头(多行用\r\n分隔) |
"Content-Type: application/json\r\nAuthorization: Bearer xxx" |
content |
POST请求的body | json_encode(['key' => 'value']) |
timeout |
超时秒数(float) | 0 |
proxy |
HTTP代理 | 'tcp://proxy.example.com:8080' |
user_agent |
User-Agent字符串 | 'Mozilla/5.0' |
ignore_errors |
忽略HTTP错误码(如404) | true |
POST请求实战:
$data = ['name' => 'Alice', 'email' => 'alice@example.com'];
$options = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => json_encode($data),
'timeout' => 15
]
];
$context = stream_context_create($options);
$response = file_get_contents('https://httpbin.org/post', false, $context);
FTP 常用选项
| 选项 | 说明 |
|---|---|
overwrite |
覆盖已有文件 |
resume_pos |
从指定位置继续上传/下载 |
proxy |
FTP代理 |
SSL/TLS 选项(用于https://或ftps://)
| 选项 | 说明 | 示例值 |
|---|---|---|
verify_peer |
验证服务器证书 | true(默认) |
verify_peer_name |
验证主机名匹配证书 | true |
cafile |
CA证书文件路径(用于自定义根证书) | '/path/to/ca.pem' |
local_cert |
客户端证书路径 | '/path/to/client.pem' |
passphrase |
私钥密码 | 'secret' |
安全HTTP请求:
$options = [
'ssl' => [
'verify_peer' => true,
'cafile' => '/etc/ssl/certs/ca-certificates.crt',
'verify_depth' => 5
]
];
$context = stream_context_create($options);
$data = file_get_contents('https://secure-api.example.com', false, $context);
上下文参数(Context Parameters)
除了协议选项,上下文还可以设置参数,用于控制流的行为和通知。
| 参数 | 说明 | 用途 |
|---|---|---|
notification |
流事件回调函数 | 监控进度、重定向等 |
blocking |
阻塞(true)或非阻塞(false)模式 |
异步I/O |
进度通知回调(非常实用!)
function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
switch ($notification_code) {
case STREAM_NOTIFY_FILE_SIZE_IS:
echo "文件大小: $bytes_max 字节\n";
break;
case STREAM_NOTIFY_PROGRESS:
if ($bytes_max > 0) {
$percent = round($bytes_transferred / $bytes_max * 100);
echo "下载进度: $percent%\r";
}
break;
case STREAM_NOTIFY_COMPLETED:
echo "\n下载完成!\n";
break;
case STREAM_NOTIFY_FAILURE:
echo "错误: $message\n";
break;
}
}
$params = ['notification' => 'stream_notification_callback'];
$context = stream_context_create([], $params);
$data = file_get_contents('https://cdn.example.com/large-file.zip', false, $context);
自定义流包装器
如果内建协议不够用,可以注册自己的流包装器。
class MyStreamWrapper {
private $position = 0;
private $data = '';
public function stream_open($path, $mode, $options, &$opened_path) {
$this->data = "Hello from custom stream!";
return true;
}
public function stream_read($count) {
$ret = substr($this->data, $this->position, $count);
$this->position += strlen($ret);
return $ret;
}
public function stream_eof() {
return $this->position >= strlen($this->data);
}
public function stream_stat() {
return ['size' => strlen($this->data)];
}
}
// 注册协议
stream_wrapper_register('my', 'MyStreamWrapper');
// 使用
$fp = fopen('my://something', 'r');
echo fread($fp, 1024); // 输出: Hello from custom stream!
实战:完整HTTP客户端(带错误处理)
function http_get($url, $headers = []) {
$header_str = '';
foreach ($headers as $key => $value) {
$header_str .= "$key: $value\r\n";
}
$options = [
'http' => [
'method' => 'GET',
'header' => $header_str,
'timeout' => 5,
'ignore_errors' => true // 允许捕获非2xx响应
],
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'cafile' => '/etc/ssl/certs/ca-certificates.crt'
]
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
if ($response === false) {
$error = error_get_last();
throw new \RuntimeException("HTTP请求失败: " . ($error['message'] ?? '未知错误'));
}
// 获取响应头
$response_headers = $http_response_header ?? [];
$status_code = 0;
if (preg_match('#HTTP/\d+\.\d+ (\d+)#', $response_headers[0] ?? '', $m)) {
$status_code = (int) $m[1];
}
return [
'status' => $status_code,
'headers' => $response_headers,
'body' => $response
];
}
// 使用
$result = http_get('https://api.github.com', ['User-Agent' => 'PHP-Streams']);
echo $result['status']; // 200
流 vs Curl:如何选择?
| 场景 | 推荐方案 |
|---|---|
| 简单GET/POST请求 | file_get_contents + 上下文(最简洁) |
| 复杂HTTP(cookie、重定向控制、多请求) | cURL(功能更全) |
| 大文件下载/上传 | 流(可用fread分批处理) |
| FTP传输 | 流(内建支持,无需扩展) |
| 异步/并发请求 | cURL多处理 或 Guzzle |
| 内存中的数据处理 | php://memory 或 php://temp |
对于简单的HTTP请求和几乎所有文件操作,流+上下文是PHP最原生的方案,无需额外扩展,复杂HTTP任务请使用cURL。
总结知识图谱
流 (Streams)
├── 封装协议
│ ├── file://, http://, ftp://
│ ├── php:// (stdin, input, memory, temp)
│ └── compress.zlib://, data://
├── 流函数
│ ├── fopen, fread, fwrite, fclose
│ ├── file_get_contents, file_put_contents
│ └── stream_get_contents, fgets, fgetcsv
└── 上下文 (Context)
├── 协议选项
│ ├── http (method, header, content, timeout)
│ ├── ssl (verify_peer, cafile)
│ └── ftp (overwrite, proxy)
└── 参数
└── notification (进度回调)
掌握流和上下文,你就能用最少的代码完成从本地文件操作到远程API调用、大文件上传监控等一系列任务,是PHP开发者的必备技能。