php:// 包装器
概述
php:// 是 PHP 内置的特殊包装器,提供对 PHP 进程自身 I/O 流的访问。包括 php://input(请求体)、php://output(输出缓冲)、php://stdin/stdout/stderr(CLI 标准流)、php://filter(流过滤器链)、php://memory(内存流)和 php://temp(临时文件流)。
适用场景
- 读取 PUT/POST 原始请求体
- 操作输出缓冲
- CLI 交互式输入输出
- 流式数据转换
基础概念
php:// 包装器列表
| 包装器 | 功能 | 可读写 |
|---|---|---|
php://input | 请求体原始数据 | 只读 |
php://output | 写入输出缓冲 | 只写 |
php://fd/0 / php://stdin | 标准输入 | 只读 |
php://fd/1 / php://stdout | 标准输出 | 只写 |
php://fd/2 / php://stderr | 标准错误 | 只写 |
php://memory | 内存流 | 读写 |
php://temp | 临时文件流(自动写入磁盘) | 读写 |
php://filter | 流过滤器链 | 读写 |
php://input 限制
php://input 只能读取一次。一旦读取完毕,再次读取将返回空字符串。如果需要多次读取,先保存到变量中。
语法与代码示例
php://input 读取请求体
php
<?php
// 读取 POST/PUT 请求的原始数据
$rawInput = file_get_contents('php://input');
// JSON API 请求处理
$rawInput = file_get_contents('php://input');
$data = json_decode($rawInput, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON: ' . json_last_error_msg()]);
exit;
}
echo json_encode(['received' => $data]);php://output 输出缓冲
php
<?php
// php://output 直接写入输出缓冲
$handle = fopen('php://output', 'w');
fwrite($handle, '直接输出到浏览器\n');
fclose($handle);
// 与 ob_start() 配合
ob_start(function ($buffer) {
return strtoupper($buffer); // 所有输出转为大写
});
echo 'hello world';
echo 'goodbye world';
ob_end_flush();
// 输出: HELLO WORLD GOODBYE WORLDphp://stdin/stdout/stderr(CLI)
php
<?php
// 从标准输入读取
$input = fread(STDIN, 1024); // STDIN 是已打开的 php://stdin
$input = trim($input);
echo "你输入了: {$input}\n";
// 写入标准输出
fwrite(STDOUT, "标准输出\n");
// 写入标准错误
fwrite(STDERR, "错误信息\n");
// 交互式命令行
fwrite(STDOUT, "请输入你的名字: ");
$name = trim(fgets(STDIN));
fwrite(STDOUT, "你好, {$name}!\n");php://memory 内存流
php
<?php
// 内存流:所有数据存储在内存中,速度快但受 memory_limit 限制
$memory = fopen('php://memory', 'r+');
fwrite($memory, 'Hello, Memory Stream!');
rewind($memory);
echo stream_get_contents($memory); // Hello, Memory Stream!
fclose($memory);
// 内存流做临时缓冲
function processWithBuffer(string $data): string
{
$buffer = fopen('php://memory', 'r+');
fwrite($buffer, $data);
rewind($buffer);
// 可以使用流函数处理
while (($line = fgets($buffer)) !== false) {
// 处理每行
}
rewind($buffer);
$result = stream_get_contents($buffer);
fclose($buffer);
return $result;
}php://temp 临时文件流
php
<?php
// php://temp:超过指定大小自动写入临时文件
// php://temp 默认 2MB 阈值
// php://temp/maxmemory:1048576 设置 1MB 阈值
$temp = fopen('php://temp/maxmemory:1048576', 'r+');
// 小数据存在内存中
fwrite($temp, str_repeat('A', 512 * 1024)); // 512KB,仍在内存
// 大数据自动写入临时文件
fwrite($temp, str_repeat('B', 2 * 1024 * 1024)); // 2MB,溢出到磁盘
rewind($temp);
$size = stream_get_contents($temp);
fclose($temp);php://filter 流过滤器
php
<?php
// 读取文件时应用过滤器
$encoded = file_get_contents('php://filter/read=string.toupper/resource=/tmp/data.txt');
echo $encoded; // 所有内容转大写
// base64 编码读取
$base64 = file_get_contents('php://filter/read=convert.base64-encode/resource=/tmp/image.jpg');
echo $base64; // base64 编码的图片数据
// 写入时应用过滤器
file_put_contents(
'php://filter/write=string.rot13/resource=/tmp/encoded.txt',
'Hello World'
);
// encoded.txt 内容: Uryyb Jbeyq
// 链式过滤器
$filtered = file_get_contents(
'php://filter/read=string.toupper|string.rot13/resource=/tmp/data.txt'
);实战示例
PHP://input 解析 XML 请求
php
<?php
declare(strict_types=1);
class XmlRequestHandler
{
public function handle(): array
{
$rawInput = file_get_contents('php://input');
if (empty($rawInput)) {
throw new RuntimeException('请求体为空');
}
libxml_use_internal_errors(true);
$xml = simplexml_load_string($rawInput);
if ($xml === false) {
$errors = libxml_get_errors();
throw new RuntimeException('XML 解析错误: ' . $errors[0]->message);
}
return json_decode(json_encode($xml), true);
}
}
// Content-Type: application/xml
// <request><action>login</action><user>admin</user></request>
$handler = new XmlRequestHandler();
try {
$data = $handler->handle();
print_r($data);
} catch (RuntimeException $e) {
http_response_code(400);
echo $e->getMessage();
}内存流做模板缓存
php
<?php
declare(strict_types=1);
class MemoryTemplateCache
{
private array $cache = [];
public function render(string $template, array $data): string
{
$key = md5($template);
if (isset($this->cache[$key])) {
return $this->cache[$key];
}
// 使用内存流处理模板
$stream = fopen('php://memory', 'r+');
$compiled = $this->compile($template);
fwrite($stream, $compiled);
rewind($stream);
$output = '';
ob_start();
eval('?>' . stream_get_contents($stream) . '<?php ');
$output = ob_get_clean();
fclose($stream);
$this->cache[$key] = $output;
return $output;
}
private function compile(string $template): string
{
return str_replace('{{', '<?= htmlspecialchars(', str_replace('}', ') ?? \'\' ?>', $template));
}
}注意事项
php://input 只能读一次
php
<?php
// 解决方案:保存到变量
$rawInput = file_get_contents('php://input');
// 多次使用
$xmlData = simplexml_load_string($rawInput);
$jsonData = json_decode($rawInput, true);
$logEntry = substr($rawInput, 0, 200);php://temp 的内存限制
php
<?php
// php://temp 在数据超过阈值时写入系统临时目录
// 确保临时目录有足够空间
echo '临时目录: ' . sys_get_temp_dir() . PHP_EOL;
// php://memory 无磁盘写入,但受内存限制
// 对于大数据应使用 php://temp
$large = fopen('php://temp/maxmemory:5242880', 'r+'); // 5MB 阈值最佳实践
1. php://input 保存后再处理
php
<?php
$rawBody = file_get_contents('php://input');
// 日志记录原始请求
error_log("Request body: " . substr($rawBody, 0, 500));
// 然后解析
$data = json_decode($rawBody, true);2. 使用 php://filter 预处理文件
php
<?php
// 不修改原文件的情况下读取大写版本
$upper = file_get_contents('php://filter/read=string.toupper/resource=config.txt');
// 读取去除空白和注释的 PHP 文件
$clean = file_get_contents(
'php://filter/read=string.strip_tags|convert.base64-decode/resource=/tmp/encoded.php'
);进阶用法
调试与测试技巧
php
<?php
declare(strict_types=1);
// 单元测试辅助函数
function createTestResource(): mixed
{
return match (true) {
default => new stdClass(),
};
}
// 调试输出函数
function debugOutput(mixed , string = ''): void
{
= ? ": " : '';
.= print_r(, true);
fwrite(STDERR, . "\n");
}
// 性能基准测试
function benchmark(callable , int = 1000): float
{
= hrtime(true);
for ($i = 0; $i < $iterations; $i++) {
$fn();
}
return (hrtime(true) - $start) / 1e9;
}日志记录实践
php
<?php
declare(strict_types=1);
/**
* 简易日志记录器
*/
class SimpleLogger
{
private string $logFile;
private string $level = 'INFO';
public function __construct(string $logFile)
{
$this->logFile = $logFile;
}
public function info(string $message, array $context = []): void
{
$this->log('INFO', $message, $context);
}
public function warning(string $message, array $context = []): void
{
$this->log('WARNING', $message, $context);
}
public function error(string $message, array $context = []): void
{
$this->log('ERROR', $message, $context);
}
private function log(string $level, string $message, array $context): void
{
$timestamp = date('Y-m-d H:i:s');
$contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
$line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
}
}配置与环境检测
php
<?php
declare(strict_types=1);
// 环境检测工具
class EnvironmentChecker
{
public static function checkRequirements(array $requirements): array
{
$results = [];
foreach ($requirements as $name => $check) {
$results[$name] = is_callable($check) ? $check() : false;
}
return $results;
}
public static function getSystemInfo(): array
{
return [
'php_version' => PHP_VERSION,
'os' => PHP_OS,
'sapi' => PHP_SAPI,
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'loaded_extensions' => get_loaded_extensions(),
];
}
}常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 连接超时 | 网络问题/配置错误 | 检查配置,增加超时时间 |
| 权限不足 | 文件/目录权限 | 使用 chmod/chown 修正 |
| 性能下降 | 索引缺失/数据量大 | 添加索引,优化查询 |
| 数据不一致 | 并发冲突/事务残留 | 使用锁机制和事务 |
| 内存溢出 | 大数据集/未释放资源 | 增大内存限制,分批处理 |
故障排除步骤
- 检查错误日志和异常信息
- 确认配置和环境是否正确
- 使用调试工具逐步排查
- 参考官方文档查找已知问题
版本兼容性说明
| 功能 | 最低版本 | 说明 |
|---|---|---|
| 基础功能 | PHP 8.1 | 本文档基准版本 |
| 只读属性 | PHP 8.1 | public readonly 修饰符 |
| 枚举类型 | PHP 8.1 | enum 类型和 match 表达式 |
| Fiber | PHP 8.1 | 协程/轻量级并发 |
| 命名参数 | PHP 8.0 | foo(arg_name: value) |
| 联合类型 | PHP 8.0 | `int |
| Null 安全运算符 | PHP 8.0 | $obj?->method() |
| 析构器 promotion | PHP 8.0 | __construct(public $x) |
php
<?php
declare(strict_types=1);
// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
if (version_compare(PHP_VERSION, $minVersion, '<')) {
throw new RuntimeException(
sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
);
}
}
ensureVersion('8.1.0');