PHP项目流与上下文选项

wen PHP项目 1

本文目录导读:

PHP项目流与上下文选项

  1. 什么是PHP流(Streams)?
  2. 上下文选项(Context Options)
  3. 上下文参数(Context Parameters)
  4. 自定义流包装器
  5. 实战:完整HTTP客户端(带错误处理)
  6. 流 vs Curl:如何选择?
  7. 总结知识图谱

在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://memoryphp://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开发者的必备技能。

抱歉,评论功能暂时关闭!