Skip to content

流过滤器

概述

流过滤器(Stream Filters)是 PHP 流系统中的数据转换层,可以在数据读写过程中自动进行编码转换、加密解密、字符串处理等操作。PHP 内置了多种过滤器(如 string.toupperconvert.iconvzlib.deflate 等),同时也支持自定义过滤器。

适用场景

  • 文件编码转换
  • 数据加密/解密
  • 字符串大小写转换
  • Base64 编解码
  • 数据压缩/解压

基础概念

内置过滤器

过滤器功能
string.rot13ROT13 编码
string.toupper转大写
string.tolower转小写
string.strip_tags去除 HTML/PHP 标签
string.base64PHP 8.0+ Base64 编解码
convert.iconv.*字符编码转换
convert.base64-encodeBase64 编码
convert.base64-decodeBase64 解码
convert.quoted-printable-encodeQP 编码
convert.quoted-printable-decodeQP 解码
zlib.deflate压缩
zlib.inflate解压
bzip2.compressBzip2 压缩
bzip2.decompressBzip2 解压

核心函数

函数功能
stream_filter_append()追加过滤器
stream_filter_prepend()前置过滤器
stream_filter_remove()移除过滤器
stream_get_filters()获取所有已注册过滤器

过滤器方向

stream_filter_append() 在读取时最后执行(写入时最先执行),stream_filter_prepend() 在读取时最先执行。

语法与代码示例

使用 php://filter 读取

php
<?php

// 读取时转大写
$content = file_get_contents(
    'php://filter/read=string.toupper/resource=/tmp/data.txt'
);
echo $content;

// Base64 编码读取
$encoded = file_get_contents(
    'php://filter/read=convert.base64-encode/resource=/tmp/image.jpg'
);

// 去除 HTML 标签后读取
$plain = file_get_contents(
    'php://filter/read=string.strip_tags/resource=/tmp/page.html'
);

// 链式过滤器(先去标签再转大写)
$clean = file_get_contents(
    'php://filter/read=string.strip_tags|string.toupper/resource=/tmp/data.html'
);

使用 php://filter 写入

php
<?php

// 写入时进行 Base64 编码
file_put_contents(
    'php://filter/write=convert.base64-encode/resource=/tmp/encoded.txt',
    'Hello, World!'
);
// encoded.txt: SGVsbG8sIFdvcmxkIQ==

// 写入时压缩
file_put_contents(
    'php://filter/write=zlib.deflate/resource=/tmp/compressed.bin',
    file_get_contents('/tmp/large_data.txt')
);

// 读取时解压
$original = file_get_contents(
    'php://filter/read=zlib.inflate/resource=/tmp/compressed.bin'
);

stream_filter_append 动态添加过滤器

php
<?php

// 打开文件后动态添加过滤器
$handle = fopen('/tmp/data.txt', 'r+');

// 添加大写过滤器(读取方向)
$filter = stream_filter_append($handle, 'string.toupper', STREAM_FILTER_READ);

// 添加 Base64 编码过滤器(写入方向)
$encFilter = stream_filter_append($handle, 'convert.base64-encode', STREAM_FILTER_WRITE);

// 写入数据(自动 Base64 编码)
fwrite($handle, 'Hello');
rewind($handle);

// 读取数据(自动转大写)
echo stream_get_contents($handle); // HELLO

// 移除过滤器
stream_filter_remove($filter);
fclose($handle);

字符编码转换

php
<?php

// convert.iconv 将文件从 GBK 转为 UTF-8
$content = file_get_contents(
    'php://filter/read=convert.iconv.GBK.UTF-8/resource=/tmp/gbk_file.txt'
);

// 写入时转换为 GBK
file_put_contents(
    'php://filter/write=convert.iconv.UTF-8.GBK/resource=/tmp/gbk_output.txt',
    '中文字符'
);

// 查看可用的 iconv 编码
$filters = stream_get_filters();
$iconvFilters = array_filter($filters, fn($f) => str_starts_with($f, 'convert.iconv.'));
echo "可用的编码转换过滤器: " . count($iconvFilters) . " 个\n";

ROT13 编码

php
<?php

// ROT13 是简单的字母替换编码(不是加密!)
$encoded = file_get_contents(
    'php://filter/read=string.rot13/resource=/tmp/secret.txt'
);

// 写入 ROT13 编码
file_put_contents(
    'php://filter/write=string.rot13/resource=/tmp/encoded.txt',
    'Hello World'
);
// encoded.txt: Uryyb Jbeyq

// ROT13 是对称的,编码两次恢复原文
$decoded = file_get_contents(
    'php://filter/read=string.rot13/resource=/tmp/encoded.txt'
);
echo $decoded; // Hello World

实战示例

自定义流过滤器

php
<?php

declare(strict_types=1);

// PHP 8.1+:继承 php_user_filter
class CsvToArrayFilter extends php_user_filter
{
    private string $buffer = '';
    private int $lineNumber = 0;

    public function filter($in, $out, &$consumed, bool $closing): int
    {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $this->buffer .= $bucket->data;
            $consumed += $bucket->datalen;
        }

        $lines = explode("\n", $this->buffer);
        $this->buffer = array_pop($lines); // 保存不完整的行

        foreach ($lines as $line) {
            $line = trim($line);
            if ($line === '') continue;

            $this->lineNumber++;
            $fields = str_getcsv($line);

            $outputLine = sprintf(
                "Line %d: %s (%d fields)\n",
                $this->lineNumber,
                $fields[0] ?? '',
                count($fields)
            );

            $bucket = stream_bucket_new($this->stream, $outputLine);
            stream_bucket_append($out, $bucket);
        }

        return PSFS_PASS_ON;
    }

    public function onCreate(): bool
    {
        $this->buffer = '';
        $this->lineNumber = 0;
        return true;
    }
}

// 注册自定义过滤器
stream_filter_register('csv.analyze', CsvToArrayFilter::class);

// 使用自定义过滤器
$result = file_get_contents('php://filter/read=csv.analyze/resource=/tmp/data.csv');
echo $result;
/*
Line 1: Alice (3 fields)
Line 2: Bob (3 fields)
*/

数据压缩流处理器

php
<?php

declare(strict_types=1);

class CompressionHelper
{
    /**
     * 使用流过滤器压缩文件
     */
    public static function compressFile(string $source, string $dest, string $algorithm = 'zlib'): void
    {
        $filterName = match ($algorithm) {
            'zlib' => 'zlib.deflate',
            'bzip2' => 'bzip2.compress',
            default => throw new InvalidArgumentException("不支持的算法: {$algorithm}"),
        };

        $sourceHandle = fopen($source, 'rb');
        $destHandle = fopen($dest, 'wb');

        if (!$sourceHandle || !$destHandle) {
            throw new RuntimeException('文件打开失败');
        }

        stream_filter_append($destHandle, $filterName);
        stream_copy_to_stream($sourceHandle, $destHandle);

        fclose($sourceHandle);
        fclose($destHandle);
    }

    /**
     * 使用流过滤器解压文件
     */
    public static function decompressFile(string $source, string $dest, string $algorithm = 'zlib'): void
    {
        $filterName = match ($algorithm) {
            'zlib' => 'zlib.inflate',
            'bzip2' => 'bzip2.decompress',
            default => throw new InvalidArgumentException("不支持的算法: {$algorithm}"),
        };

        $sourceHandle = fopen($source, 'rb');
        $destHandle = fopen($dest, 'wb');

        if (!$sourceHandle || !$destHandle) {
            throw new RuntimeException('文件打开失败');
        }

        stream_filter_append($sourceHandle, $filterName);
        stream_copy_to_stream($sourceHandle, $destHandle);

        fclose($sourceHandle);
        fclose($destHandle);
    }
}

// 使用
CompressionHelper::compressFile('/tmp/data.txt', '/tmp/data.zlib');
CompressionHelper::decompressFile('/tmp/data.zlib', '/tmp/restored.txt');

注意事项

过滤器顺序

php
<?php

// 链式过滤器的执行顺序
// php://filter/read=A|B|C/resource=file
// 读取时:先 C,再 B,最后 A
// 等价于 A(B(C(data)))

$doubleFiltered = file_get_contents(
    'php://filter/read=string.toupper|string.rot13/resource=/tmp/data.txt'
);
// 数据先经过 rot13,再经过 toupper

流过滤器 vs php://filter

php
<?php

// php://filter 方式(适合一次性操作)
$content = file_get_contents('php://filter/read=string.toupper/resource=/tmp/data.txt');

// stream_filter_append 方式(适合需要多次读写)
$handle = fopen('/tmp/data.txt', 'r+');
$filter = stream_filter_append($handle, 'string.toupper', STREAM_FILTER_READ);
$data = stream_get_contents($handle);
stream_filter_remove($filter);
fclose($handle);

最佳实践

1. 使用 convert.iconv 替代手动转换

php
<?php

// 不好:手动读取再转换
$content = file_get_contents('/tmp/gbk.txt');
$content = mb_convert_encoding($content, 'UTF-8', 'GBK');

// 好:使用流过滤器透明转换
$content = file_get_contents(
    'php://filter/read=convert.iconv.GBK.UTF-8/resource=/tmp/gbk.txt'
);

2. 大文件压缩使用流

php
<?php

// 流式压缩,不需要全部加载到内存
$in = fopen('/tmp/large_file.bin', 'rb');
$out = fopen('/tmp/large_file.bin.zlib', 'wb');
stream_filter_append($out, 'zlib.deflate');
stream_copy_to_stream($in, $out);
fclose($in);
fclose($out);

进阶用法

调试与测试技巧

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');

参考链接