clue/stream-filter

PHP流过滤的简单现代方法

v1.7.0 2023-12-20 15:40 UTC

This package is auto-updated.

Last update: 2024-09-20 17:13:00 UTC


README

CI status installs on Packagist

PHP流过滤的简单现代方法

目录

为什么?

PHP的流过滤系统很棒!

它提供了非常强大的流过滤选项,并附带一组有用的内置过滤器。这些过滤器可以轻松高效地实时执行各种转换,例如

  • 从gzip压缩的输入文件中读取
  • 从ISO-8859-1(Latin1)转换到UTF-8
  • 写入bzip输出文件
  • 等等。

但让我们面对现实:它的API难以使用,其文档质量不高。这意味着其强大的功能往往被忽视。

本项目旨在使这些功能更容易被更广泛的受众访问。

  • 轻量级,SOLID设计 - 提供了一个薄薄的抽象层,它只是足够好,不会妨碍您。自定义过滤器只需做最少的努力。
  • 良好的测试覆盖率 - 随带自动测试套件,并在真实世界中定期测试

支持我们

我们在开发、维护和更新我们出色的开源项目上投入了大量时间。您可以通过成为GitHub赞助商来帮助我们保持这种高质量的工作。赞助商将获得许多回报,有关详情请参阅我们的赞助页面

让我们一起把这些项目提升到新的水平!🚀

用法

这个轻量级库仅由几个简单函数组成。所有函数都位于Clue\StreamFilter命名空间下。

以下示例使用全限定名称引用所有函数,例如

Clue\StreamFilter\append(…);

从PHP 5.6+开始,您还可以将每个所需函数导入到您的代码中,如下所示

use function Clue\StreamFilter\append;

append(…);

或者,您也可以使用类似下面的导入语句

use Clue\StreamFilter as Filter;

Filter\append(…);

append()

append(resource<stream> $stream, callable $callback, int $read_write = STREAM_FILTER_ALL): resource<stream filter>函数可以用来将过滤器回调追加到给定的流中。

每个流都可以附加一个过滤器列表。此函数将过滤器追加到该列表的末尾。

如果无法添加给定的过滤器,则抛出Exception

$stream可以是任何有效的流资源,例如

$stream = fopen('demo.txt', 'w+');

$callback应是一个有效的可调用函数,它接受一个数据块,并应返回更新后的块

$filter = Clue\StreamFilter\append($stream, function ($chunk) {
    // will be called each time you read or write a $chunk to/from the stream
    return $chunk;
});

因此,您也可以使用原生PHP函数或任何其他callable

Clue\StreamFilter\append($stream, 'strtoupper');

// will write "HELLO" to the underlying stream
fwrite($stream, 'hello');

如果$callback接受不带参数的调用,则此签名将在过滤器结束(刷新)时调用一次

Clue\StreamFilter\append($stream, function ($chunk = null) {
    if ($chunk === null) {
        // will be called once ending the filter
        return 'end';
    }
    // will be called each time you read or write a $chunk to/from the stream
    return $chunk;
});

fclose($stream);

注意:旧版PHP版本(PHP < 5.4)不支持在关闭流时从端信号处理器传递额外数据。

如果您的回调抛出了Exception,则过滤器进程将被中止。为了与PHP的流处理良好地协同工作,Exception将被转换为PHP警告。

Clue\StreamFilter\append($stream, function ($chunk) {
    throw new \RuntimeException('Unexpected chunk');
});

// raises an E_USER_WARNING with "Error invoking filter: Unexpected chunk"
fwrite($stream, 'hello');

可选的$read_write参数可以用来在写入流或仅从流中读取时调用$callback

Clue\StreamFilter\append($stream, function ($chunk) {
    // will be called each time you write to the stream
    return $chunk;
}, STREAM_FILTER_WRITE);

Clue\StreamFilter\append($stream, function ($chunk) {
    // will be called each time you read from the stream
    return $chunk;
}, STREAM_FILTER_READ);

此函数返回一个过滤器资源,可以传递给remove()

请注意,一旦过滤器被添加到流中,该流就不能再传递给stream_select()(及其相关函数)。

警告:在{file}的第{line}行,stream_select():无法在此系统上将过滤器流转换为{file}的第{line}行。

这是由于PHP流过滤器支持的限制,因为它不能再可靠地判断底层流资源是否真正就绪。作为替代方案,可以考虑在未过滤的流上调用stream_select(),然后将未过滤的数据通过fun()函数传递。

prepend()

prepend(resource<stream> $stream, callable $callback, int $read_write = STREAM_FILTER_ALL): resource<stream filter>函数可以用来将过滤器回调添加到指定的流中。

每个流都可以附加一个过滤器列表。此函数将过滤器添加到该列表的开头。

如果无法添加给定的过滤器,则抛出Exception

$filter = Clue\StreamFilter\prepend($stream, function ($chunk) {
    // will be called each time you read or write a $chunk to/from the stream
    return $chunk;
});

此函数返回一个过滤器资源,可以传递给remove()

除了在过滤器列表中的位置外,此函数的行为与append()函数完全相同。有关其行为的更多详细信息,请参阅append()函数。

fun()

fun(string $filter, mixed $parameters = null): callable函数可以用来创建一个使用给定内置$filter的过滤器函数。

PHP提供了一套有用的内置过滤器。使用fun()可以让访问这些过滤器变得像传递输入字符串进行过滤并获取过滤后的输出字符串一样简单。

$fun = Clue\StreamFilter\fun('string.rot13');

assert('grfg' === $fun('test'));
assert('test' === $fun($fun('test'));

请注意,根据安装的PHP扩展和使用的PHP版本,并非所有过滤器函数都可能可用。特别是,HHVM可能不会提供与Zend PHP相同的过滤器函数或参数。访问未知过滤器函数会导致抛出RuntimeException

Clue\StreamFilter\fun('unknown'); // throws RuntimeException

某些过滤器可能接受或需要额外的过滤器参数——大多数过滤器不需要过滤器参数。如果提供了,可选的$parameters参数将按原样传递给底层的过滤器处理器。特别是,请注意不传递此参数与明确传递一个null值(许多过滤器不接受)之间的区别。有关更多详细信息,请参阅各个过滤器的定义。例如,可以像这样调用string.strip_tags过滤器

$fun = Clue\StreamFilter\fun('string.strip_tags', '<a><b>');

$ret = $fun('<b>h<br>i</b>');
assert('<b>hi</b>' === $ret);

在内部,此函数分配一个临时内存流,因此建议在使用后清理过滤器函数。此外,一些过滤器函数(尤其是zlib压缩过滤器)可能使用内部缓冲区,并在关闭时可能发出最终数据块。可以通过不带任何参数的调用关闭过滤器函数。

$fun = Clue\StreamFilter\fun('zlib.deflate');

$ret = $fun('hello') . $fun('world') . $fun();
assert('helloworld' === gzinflate($ret));

关闭过滤器函数后不应再使用该过滤器函数。这样做会导致抛出RuntimeException

$fun = Clue\StreamFilter\fun('string.rot13');
$fun();

$fun('test'); // throws RuntimeException

注意:如果您正在使用zlib压缩过滤器,那么您应该注意不同PHP版本和HHVM之间的引擎不一致性。这些问题存在于底层PHP引擎中,我们在这个库中对此无能为力。《我们的测试套件》包含几个展示这些问题的测试用例。如果您觉得某些测试用例缺失或过时,我们乐意接受PR!:)

remove()

可以使用remove(resource<stream filter> $filter): bool函数来移除之前通过append()prepend()添加的过滤器。

$filter = Clue\StreamFilter\append($stream, function () {
    // …
});
Clue\StreamFilter\remove($filter);

安装

推荐通过Composer来安装这个库。您是Composer新手吗?

本项目遵循SemVer。这将安装最新支持的版本

$ composer require clue/stream-filter:^1.7

有关版本升级的详细信息,请参阅变更日志

本项目旨在在任意平台上运行,因此不需要任何PHP扩展,并支持在当前PHP 8+和HHVM上运行,以及从旧的PHP 5.3版本。我们强烈建议您使用此项目的最新支持版本。较旧的PHP版本可能会遇到上述文档中记录的许多不一致性。

测试

要运行测试套件,您首先需要克隆此仓库,然后通过Composer安装所有依赖项

$ composer install

要运行测试套件,请转到项目根目录并运行

$ vendor/bin/phpunit

许可协议

本项目采用宽松的MIT许可

你知道吗?我可以提供定制开发服务,并为版本发布和贡献发放发票。有关详情,请联系我(@clue)。