28

我真的很想实现一个php_user_filter::filter(). 但是因此我必须知道什么是水桶旅。这似乎是我可以使用这些stream_bucket_*功能操作的资源。但是文档并没有真正的帮助。我能找到的最好的例子是stream_filter_register().

我特别好奇这些stream_bucket_new()stream_bucket_make_writeable()能做什么。


更新:似乎 PHP 暴露了Apache 的内部数据结构

4

2 回答 2

41

啊,欢迎阅读 PHP 手册中记录最少的部分![我打开了一个关于它的错误报告;也许这个答案将有助于记录它:https ://bugs.php.net/bug.php?id=69966 ]

水桶大队

从您最初的问题开始,bucket brigade 只是名为 的资源的名称userfilter.bucket brigade

您将作为第一个和第二个参数传递给两个不同的旅php_user_filter::filter()。第一个旅是您从中读取的输入桶,第二个旅最初是空的;你给它写信。

关于您对数据结构的更新……它实际上只是一个带有字符串的双向链表。但很可能这个名字是从那里被盗的;-)

stream_bucket_prepend()/stream_bucket_append()

stream_bucket_prepend(resource $brigade, stdClass $bucket): null
stream_bucket_append(resource $brigade, stdClass $bucket): null

预期$brigade的是输出旅,也就是 .on 上的第二个参数php_user_filter::filter()

$bucket是一个对象,stdClass就像它由stream_bucket_make_writable()or返回一样stream_bucket_new()

这两个函数只是将传递的桶添加或附加到旅。

stream_bucket_new()

为了揭开这个函数的神秘面纱,首先分析它的函数签名是什么:

stream_bucket_new(resource $stream, string $buffer): stdClass

第一个参数是$stream你正在写这个桶。其次是$buffer这个新桶将包含的内容。

[我想在这里指出,该$stream参数实际上并不是很重要;它只是用来检查我们是否需要持久分配内存以便它在请求中存活下来。我只是假设当在非持久过滤器上操作时,您可以通过在此处传递持久流来很好地使 PHP 出现段错误...]

现在userfilter.bucket创建了一个资源,该资源被分配给stdClass名为 的 () 对象的属性bucket。该对象还有两个其他属性:datadatalen,其中包含此存储桶的缓冲区和缓冲区大小。

它会返回一个stdClass你可以传入的astream_bucket_prepend()stream_bucket_append()

stream_bucket_make_writable()

stream_bucket_make_writeable(resource $brigade): stdClass|null

它将第一个存储桶从 转移$brigade并返回。如果$brigade被清空,则返回null

补充说明

php_user_filter::filter()被调用时,$stream对象上的属性filter()将被设置为我们当前正在处理的流。stream_bucket_new()这也是调用它时需要传递的流。(调用后该$stream属性将再次取消设置。您不能在 eg 中重复使用它php_user_filter::onClose())。

另请注意,即使您返回了一个$datalen属性,也无需设置该属性,以防在将$data属性传递给stream_bucket_prepend()or之前更改该属性stream_bucket_append()

该实现要求您(好吧,它期望或将引发警告)$in在返回之前从存储桶中读取所有数据。

文档对我们撒谎的另一种情况:在 中php_user_filter::onCreate()$stream属性设置。它只会在filter()方法调用期间设置。

通常,不要将过滤器与非阻塞流一起使用。我试过一次,结果大错特错......而且不太可能修复......

总结(例子)

让我们从最简单的情况开始:写回我们输入的内容。

class simple_filter extends php_user_filter {
    function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

stream_filter_register("simple", "simple_filter")

这里发生的所有事情都是从$in桶大队获取桶并将其放回$out桶大队。

好的,现在尝试操纵我们的输入。

class reverse_filter extends php_user_filter {
    function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $consumed += $bucket->datalen;
            $bucket->data = strrev($bucket->data);
            stream_bucket_prepend($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

stream_filter_register("reverse", "reverse_filter")

现在我们注册了reverse://协议,它反转了你的字符串(每个写入都在它自己的这里被反转;写入顺序仍然保留)。因此,我们现在显然需要操作存储桶数据并将其添加到此处。

现在,用例是stream_bucket_new()什么?通常你可以追加到$bucket->data; 是的,您甚至可以将所有数据连接到第一个桶中,但是当flush()桶旅中可能没有任何数据并且您想发送最后一个桶时,您需要它。

class append_filter extends php_user_filter {
    public $stream;

    function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        // always append a terminating \n
        if ($closing) {
            $bucket = stream_bucket_new($this->stream, "\n");
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

stream_filter_register("append", "append_filter")

有了它(以及关于php_user_filterclass的现有文档),人们应该能够通过将所有这些强大的可能性组合成更强大的代码来进行各种神奇的用户空间流过滤。

于 2015-06-30T07:56:15.717 回答
0

我想我会提供一些背景信息。

首先,术语桶和旅。原来有一种叫做水桶大队的东西……有点像打火的标签团队……你有一连串的人站着不动,但把水桶递给旁边的人,产生一个恒定的装满水的桶流。

另外,如上所述,PHP 对桶和旅的采用来自 Apaches [桶和旅](http://www.apachetutor.org/dev/brigades],也许......对方法和推理给出了很好的解释.

但本质上,这个想法是,如果您需要在发送之前对某些内容进行修改,那么在中间流中进行修改有很多好处,尤其是当您使用桶和旅对流进行建模时。

于 2020-07-07T03:55:38.140 回答