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

回到顶部