PHP 自定义流过滤器
流过滤器允许对流中的文本数据进行更改。这允许在写入或读取流时更改文本,而不是在流运行后更改文本。PHP 中内置了一个框架,允许将自定义过滤器添加到内置过滤器组中。
让我们先看看 PHP 内置了哪些过滤器。
stream_get_filters()
要查看系统上存在哪些过滤器,您可以运行该stream_get_filters()函数。这将返回可用过滤器的列表。
$ php -r "print_r(stream_get_filters());"Array
(
[0] => zlib.*
[1] => bzip2.*
[2] => convert.iconv.*
[3] => string.rot13
[4] => string.toupper
[5] => string.tolower
[6] => string.strip_tags
[7] => convert.*
[8] => consumed
[9] => dechunk
)
这里有很多过滤器可供选择,但我们如何创建自己的过滤器?弄清楚如何生成自定义过滤器有点困难,因为这可能是 PHP 中记录最少的方面。这里使用的大部分内部函数根本没有记录,这使得很难弄清楚发生了什么。
在我们开始创建自己的过滤器之前,有一些事情需要解决。
水桶旅
“bucket brigade”(如果你不知道的话)是一个英语成语,它描述了一系列人来回传递水桶以便为火供水。
当我们调用自定义过滤器时,会向它传递一个桶队。在内部,这是一个 PHP 资源userfilter.bucket brigade,它的作用类似于userfilter.bucket资源的双向链表。该 userfilter.bucket资源包含我们的数据。
stream_bucket_make_writeable()
使用这个函数,我们可以传入一个userfilter.bucket 旅 资源并从中获取下一个userfilter.bucket对象。
$bucket = stream_bucket_make_writeable($in);
该对象是一个 stdClass 对象,具有以下结构。
bucket - 这是userfilter.bucket资源。
数据 - 如果一次处理太多,这将是所有数据或数据的一部分。
datalen - data 属性中数据的长度。
为了证明这一点,让我们看一下实际对象的结构。
print_r(stream_bucket_make_writeable($in));
我们将打印出这样的东西(取决于我们过滤的数据)。
object(stdClass)#2 (3) {["bucket"]=>resource(10) of type (userfilter.bucket)
["data"]=>string(119) "Antimony is a silvery, lustrous gray metalloid with a Mohs scale hardness of 3, which is too soft to make hard objects."
["datalen"]=>int(119)
}
如果资源中没有剩余数据,则此函数将返回 null。这意味着我们可以循环遍历bucket brigade并继续抓取bucket项目,直到找到null为止。
stream_bucket_prepend() 和 stream_bucket_append()
有了我们从我们那里得到的对象,stream_bucket_make_writeable()我们现在需要把它放在某个地方。这就是函数stream_bucket_prepend()和stream_bucket_append()进来的地方。
自定义过滤器有一个 $in 参数和一个 $out 参数(它们都是userfilter.bucket 旅 资源)。数据stream_bucket_make_writable()以对象的形式从 $in 参数中提取出来。然后在此对象中更改数据,然后使用这些函数之一将其传递回 $out 参数。
以下是使用流的整个过程的示例。
$bucket = stream_bucket_make_writeable($in);$bucket->data = strtoupper($bucket->data);
stream_bucket_append($out, $bucket);
stream_bucket_new()
该函数接受一个资源和一个字符串,并将返回一个存储桶,我们可以使用它向输出存储桶旅添加内容。该函数具有以下足迹。
stream_bucket_new(resource $stream, string $buffer): stdClass
我们可以采用任何流,使用它来创建一个包含一些字符串内容的存储桶,然后将其附加到我们的过滤器输出流中。例如,我们可以生成一个流内存流,然后像这样创建一个新的存储桶。
$stream = fopen('php://memory', 'r');$bucket = stream_bucket_new($stream, 'monkey');
stream_bucket_append($out, $bucket);
我们创建的任何自定义过滤器都会自动获得一个名为流的属性。这本质上是一个指向正在处理的流的链接,并为我们提供了一个方便的资源,我们可以使用它来生成一个新的存储桶。例如,要将字符串添加到输出流中,我们可以执行以下操作。
$bucket = stream_bucket_new($this->stream, PHP_EOL);stream_bucket_append($out, $bucket);
请注意,过滤器对象的流属性是在第一次filter()调用该方法时创建的。如前所述,它将被设置为我们目前正在处理的流。
创建自定义过滤器
自定义过滤器被创建为一个类,其中在应用过滤器时调用某些函数。这些函数在 php_user_filter 类中有详细说明,但直接使用该类并没有用。为了方便子类的创建,我创建了一个接口,强制过滤器类实现某些功能。
interface CustomFilter{
/**
* Called when applying the filter.
*
* @param resource $in
* in is a resource pointing to a bucket brigade which contains one or more bucket
* objects containing data to be filtered.
* @param resource $out
* out is a resource pointing to a second bucket brigade into which your modified
* buckets should be placed.
* @param int $consumed
* consumed, which must always be declared by reference, should be incremented by
* the length of the data which your filter reads in and alters. In most cases
* this means you will increment consumed by $bucket->datalen for each $bucket.
* @param bool $closing
* If the stream is in the process of closing (and therefore this is the last pass
* through the filterchain), the closing parameter will be set to TRUE.
*
* @return int
* The filter() method must return one of three values upon completion.
* - PSFS_PASS_ON: Filter processed successfully with data available in the out
* bucket brigade.
* - PSFS_FEED_ME: Filter processed successfully, however no data was available to
* return. More data is required from the stream or prior filter.
* - PSFS_ERR_FATAL (default): The filter experienced an unrecoverable error and
* cannot continue.
*/
public function filter($in, $out, &$consumed = NULL, bool $closing = false): int;
/**
* Called when creating the filter.
*
* @return bool
* Your implementation of this method should return FALSE on failure, or TRUE on success.
*/
public function onCreate(): bool;
/**
* Called when closing the filter.
*/
public function onClose(): void;
}
使用这个接口,我组合了一个对字符串执行过滤器的类。作为一个例子,我创建了一个类,它将接受一个字符串并将每个字符转换为 X。
请注意,此类的所有属性都是由 PHP 自动生成的。
class CensorFilter implements CustomFilter{
/**
* Name of the filter registered by stream_filter_append().
*
* @var string
*/
public $filtername;
/**
* Additional parameters passed through the stream_filter_append() function.
*
* @var mixed
*/
public $params;
/**
* A resource of type 'userfilter.filter'.
*
* @var resource
*/
public $filter;
/**
* A resource of type 'stream'.
*
* @var resource
*/
public $stream;
public function filter($in, $out, &$consumed = NULL, bool $closing = false): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$bucket->data = preg_replace('/[a-zA-Z][0-9]/', 'X', $bucket->data);
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
public function onCreate(): bool
{
return true;
}
public function onClose(): void
{
}
}
filter()此类中的方法是我们的审查过滤器中进行大部分工作的地方。这个函数的两个重要部分是$in,它是我们的传入bucket brigade和$out,它是传出bucket brigade。在上面的类中,我们正在执行以下操作:
获取 $in 桶旅并调用stream_bucket_make_writable()它,直到我们得到一个空值。
从创建的存储桶中获取数据并使用 将其交换为审查版本preg_replace()。
通过将桶的长度写入 $consumed 变量,让 PHP 知道我们消耗了多少。这是通过引用传递的,因此此分配将在上游冒泡。请注意,由于我们没有更改文本的长度,因此可以不理会这个值。
使用该stream_bucket_append()函数将存储桶写入 $out 存储桶旅。
返回 PSFS_PASS_ON,假设我们已经成功通过了流。
一个重要的方面是我们必须循环遍历bucket brigade中的所有数据,否则我们将生成以下警告。
PHP Warning: fgets(): Unprocessed filter buckets remaining on input brigade in custom_filter.php on line 115
使用自定义过滤器
要将此类用作过滤器,我们调用了两个函数。该 stream_filter_register()函数将注册过滤器类,该 stream_filter_append()函数会将过滤器附加到流中。
这是一个所有正在运行的示例。
// 注册 CensorFilter 类。stream_filter_register("censor", "CensorFilter");
// 从文本文件创建流。
$stream = fopen('test.txt', 'r');
// 将审查过滤器附加到我们的流中。
stream_filter_append($stream, "censor");
// 将流读出到命令行。
while (false !== ($line = fgets($stream))) {
echo $line;
}
// 关闭流。
fclose($stream);
带有包含以下内容的文本文件。
Antimony is a silvery, lustrous gray metalloid with a Mohs scale hardness of 3.
这个的输出是。
XXXXXXXX XX X XXXXXXX, XXXXXXXX XXXX XXXXXXXXX XXXX X XXXX XXXXX XXXXXXXX XX X.
我们还可以使用php://filter语法将过滤器应用于流。
stream_filter_register("censor", "CensorFilter");$input = fopen('php://filter/read=censor/resource=test.txt', 'r');
while (false !== ($line = fgets($input))) {
echo $line;
}
最后,我要感谢这篇文章,它帮助我解决了 PHP 的这一部分中的一些未知问题。
以上是 PHP 自定义流过滤器 的全部内容, 来源链接: utcz.com/z/338735.html