本文目录导读:

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 版本可能有细微差异
自定义流非常适合用于数据处理管道、资源抽象、或者为特定格式提供统一接口等场景。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播