本文目录导读:

PHP 自定义流(Custom Stream Wrapper)是一种强大的功能,允许你注册自己的流处理器来处理 file://、http:// 等协议之外的任意协议。
基本概念
自定义流的核心是实现 streamWrapper 类,然后通过 stream_wrapper_register() 注册。
完整示例:实现一个 JSON 文件流
<?php
class JsonStreamWrapper {
private $data = '';
private $position = 0;
private $filename = '';
private $mode = '';
// 打开流
public function stream_open($path, $mode, $options, &$opened_path) {
$this->filename = $path;
$this->mode = $mode;
$this->position = 0;
// 从文件读取 JSON 数据
$filepath = substr($path, 7); // 去掉 "json://"
$content = @file_get_contents($filepath);
$this->data = $content ?: '{}';
return true;
}
// 读取数据
public function stream_read($count) {
$data = substr($this->data, $this->position, $count);
$this->position += strlen($data);
return $data;
}
// 写入数据
public function stream_write($data) {
$this->data = substr($this->data, 0, $this->position) .
$data .
substr($this->data, $this->position + strlen($data));
$this->position += strlen($data);
return strlen($data);
}
// 获取当前位置
public function stream_tell() {
return $this->position;
}
// 定位
public function stream_seek($offset, $whence) {
switch ($whence) {
case SEEK_SET:
$this->position = $offset;
break;
case SEEK_CUR:
$this->position += $offset;
break;
case SEEK_END:
$this->position = strlen($this->data) + $offset;
break;
}
return true;
}
// 检查是否到末尾
public function stream_eof() {
return $this->position >= strlen($this->data);
}
// 关闭流
public function stream_close() {
// 如果是写入模式,保存数据
if (strpos($this->mode, 'w') !== false || strpos($this->mode, 'a') !== false) {
$filepath = substr($this->filename, 7);
file_put_contents($filepath, $this->data);
}
}
// 获取流统计信息
public function stream_stat() {
return ['size' => strlen($this->data)] + stat($this->filename);
}
// 清空流
public function stream_flush() {
$filepath = substr($this->filename, 7);
file_put_contents($filepath, $this->data);
return true;
}
}
// 注册流包装器
stream_wrapper_register('json', 'JsonStreamWrapper');
// 测试使用
$file = fopen('json://data/config.json', 'r+');
$content = stream_get_contents($file);
echo "读取内容: " . $content;
// 写入数据
fwrite($file, '{"name": "John", "age": 30}');
fclose($file);
更简单的内存流示例
<?php
class MemoryStream {
private $data = '';
private $pos = 0;
public function stream_open($path, $mode, $options, &$opened_path) {
$this->data = '';
$this->pos = 0;
return true;
}
public function stream_read($count) {
$ret = substr($this->data, $this->pos, $count);
$this->pos += strlen($ret);
return $ret;
}
public function stream_write($data) {
$this->data .= $data;
return strlen($data);
}
public function stream_tell() {
return $this->pos;
}
public function stream_seek($offset, $whence) {
switch ($whence) {
case SEEK_SET: $this->pos = $offset; break;
case SEEK_CUR: $this->pos += $offset; break;
case SEEK_END: $this->pos = strlen($this->data) + $offset; break;
}
return true;
}
public function stream_eof() {
return $this->pos >= strlen($this->data);
}
public function stream_close() {
// 可以把数据存到某个地方
$_SESSION['memory_stream_data'] = $this->data;
}
public function stream_stat() {
return [];
}
}
// 注册
stream_wrapper_register('memory', 'MemoryStream');
// 使用
$handle = fopen('memory://temp', 'w');
fwrite($handle, 'Hello, World!');
fclose($handle);
$handle = fopen('memory://temp', 'r');
echo stream_get_contents($handle); // 输出: Hello, World!
fclose($handle);
常用的流方法列表
| 方法 | 用途 |
|---|---|
stream_open() |
打开流时调用 |
stream_read() |
读取数据 |
stream_write() |
写入数据 |
stream_tell() |
获取当前位置 |
stream_seek() |
定位 |
stream_eof() |
检查末尾 |
stream_close() |
关闭流 |
stream_stat() |
获取文件统计信息 |
url_stat() |
获取路径信息 |
stream_flush() |
刷新缓冲区 |
unlink() |
删除 |
mkdir() |
创建目录 |
rmdir() |
删除目录 |
rename() |
重命名 |
注册和使用技巧
<?php
// 注册时可以指定前缀
stream_wrapper_unregister('json'); // 先注销已存在的
stream_wrapper_register('json', 'JsonStreamWrapper');
// 检查是否已注册
$wrappers = stream_get_wrappers();
var_dump(in_array('json', $wrappers));
// 也可以注册到全局
stream_wrapper_restore('json'); // 恢复默认包装器
// 使用示例
$context = stream_context_create([
'json' => [
'option1' => 'value',
'option2' => 'value'
]
]);
$data = file_get_contents('json://path/to/file.json', false, $context);
实际应用场景
数据库流
class DBStream {
private $connection;
private $result;
public function stream_open($path, $mode, $options, &$opened_path) {
// 解析路径: db://table/query
preg_match('/db:\/\/(\w+)\/(.+)/', $path, $matches);
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$this->connection = $db;
$this->result = $db->query($matches[2]);
return true;
}
public function stream_read($count) {
if ($row = $this->result->fetch(PDO::FETCH_ASSOC)) {
return json_encode($row) . "\n";
}
return '';
}
}
加密流
class EncryptedStream {
private $key;
private $data;
public function stream_open($path, $mode, $options, &$opened_path) {
$this->key = 'secret_key';
$this->data = '';
return true;
}
public function stream_write($data) {
$this->data .= openssl_encrypt($data, 'AES-128-CBC', $this->key);
return strlen($data);
}
}
注意事项
- 性能考虑:不要滥用自定义流,它比普通文件操作慢
- 错误处理:确保实现必要的方法,否则可能有未定义行为
- 权限检查:在自己的流包装器中检查权限
- 资源清理:在
stream_close()中释放资源 - PHP 版本:不同 PHP 版本可能有细微差异
自定义流非常适合用于数据处理管道、资源抽象、或者为特定格式提供统一接口等场景。