Skip to content

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 WORLD

php://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 修正
性能下降索引缺失/数据量大添加索引,优化查询
数据不一致并发冲突/事务残留使用锁机制和事务
内存溢出大数据集/未释放资源增大内存限制,分批处理

故障排除步骤

  1. 检查错误日志和异常信息
  2. 确认配置和环境是否正确
  3. 使用调试工具逐步排查
  4. 参考官方文档查找已知问题

版本兼容性说明

功能最低版本说明
基础功能PHP 8.1本文档基准版本
只读属性PHP 8.1public readonly 修饰符
枚举类型PHP 8.1enum 类型和 match 表达式
FiberPHP 8.1协程/轻量级并发
命名参数PHP 8.0foo(arg_name: value)
联合类型PHP 8.0`int
Null 安全运算符PHP 8.0$obj?->method()
析构器 promotionPHP 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');

参考链接