我真的很想实现一个php_user_filter::filter()
。但因此,我必须知道什么是水桶旅。这似乎是我可以使用stream_bucket_*
功能操作的资源。但是文档并没有真正的帮助。我能找到的最好的是stream_filter_register()
中的那些例子。
我特别好奇这些stream_bucket_new()
和stream_bucket_make_writeable()
能做什么。
更新:PHP似乎公开了Apache的内部数据结构。
啊,欢迎来到PHP手册中文档最少的部分![我打开了一个关于它的错误报告;也许这个答案将有助于记录它:https://bugs.php.net/bug.php?id=69966]
水桶旅
从您最初的问题开始,存储桶旅只是名为 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
是输出旅,也就是php_user_filter::filter()
上的第二个参数。
$bucket
是一个stdClass
对象,就像它由stream_bucket_make_writable()
或stream_bucket_new()
返回一样。
这两个函数只是将传递的存储桶前置或追加到旅。
stream_bucket_new()
要揭开此函数的神秘面纱,请先分析它的函数签名是什么:
stream_bucket_new(resource $stream, string $buffer): stdClass
第一个参数是您要将此存储桶写入到的$stream
。其次是这个新存储桶将包含$buffer
。
[我想在这里指出,$stream
参数实际上不是很重要;它只是用来检查我们是否需要持久分配内存,以便它通过请求存活下来。我只是假设当在非持久过滤器上操作时,您可以通过在此处传递持久流来使 PHP 很好地段错误......]
现在创建了一个userfilter.bucket
资源,该资源被分配给名为 bucket
的 (stdClass
) 对象的属性。该对象还具有另外两个属性:data
和 datalen
,它们包含此存储桶的缓冲区和缓冲区大小。
它将返回一个stdClass
,您可以将其传递给stream_bucket_prepend()
并stream_bucket_append()
。
stream_bucket_make_writable()
stream_bucket_make_writeable(resource $brigade): stdClass|null
它将第一个存储桶从$brigade
中移出并返回。如果$brigade
已清空,则返回null
。
进一步说明
调用 php_user_filter::filter()
时,调用filter()
的对象上的 $stream
属性将设置为我们当前正在处理的流。这也是调用它时需要传递给stream_bucket_new()
的流。(调用后,$stream
属性将再次取消设置。您不能在例如 php_user_filter::onClose()
)。
另请注意,即使返回了$datalen
属性,也无需设置该属性,以防在将属性传递给stream_bucket_prepend()
或stream_bucket_append()
之前更改$data
属性。
该实现要求您(嗯,它期望或将引发警告)您在返回之前从$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_filter
类的现有文档),人们应该能够通过将所有这些强大的可能性组合成更强大的代码来进行各种神奇的用户空间流过滤。
我会提供一些背景信息。
首先,术语桶和旅。原来有一种东西叫水桶旅...有点像团队努力救火...在那里,你有一连串的人站着不动,但把水桶递给他们旁边的人,产生源源不断的水桶。
此外,如上所述,PHP 采用桶和旅来自阿帕奇 [桶和旅](http://www.apachetutor.org/dev/brigades],也许......对方法和推理进行了很好的解释。
但从本质上讲,这个想法是,如果您需要在发送之前对某些内容进行修改,那么在中流进行修改有很多好处,尤其是当您使用存储桶和旅对流进行建模时。