我真的很想实现一个php_user_filter::filter()
。但因此我必须知道斗队旅是什么。这似乎是我可以使用stream_bucket_*
函数操作的资源。但文档并没有真正的帮助。我能找到的最好的是stream_filter_register()
中的那些例子。
我特别好奇这些stream_bucket_new()
和stream_bucket_make_writeable()
可以做些什么。
更新:似乎PHP正在公开internal data structure of Apache。
答案 0 :(得分:34)
啊,欢迎使用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
是输出旅,也就是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
资源,该资源被分配给名为stdClass
的(bucket
)对象的属性。
该对象还有另外两个属性: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()
时,调用对象$stream
上的filter()
属性将设置为我们当前正在处理的流。这也是您在调用它时需要传递给stream_bucket_new()
的流。 (调用后,$stream
属性将再次取消设置。您无法在php_user_filter::onClose()
}中重复使用。
另请注意,即使您返回$datalen
属性,也不需要设置该属性,以防更改$data
属性,然后再将其传递给stream_bucket_prepend()
或stream_bucket_append()
。
实现要求您(好吧,它希望或将发出警告)您在返回之前读取$in
存储桶中的所有数据。
还有另一个案例告诉我们:在php_user_filter::onCreate()
中,$stream
属性未设置。它只会在filter()
方法调用期间设置。
通常,不要使用具有非阻塞流的过滤器。我尝试了一次,但是它出现了可怕的错误... And it's not likely that's ever going to be fixed...
让我们从最简单的案例开始:回写我们所得到的东西。
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
class的现有文档),人们应该能够通过将所有这些强大的可能性结合到更强大的代码中来进行各种魔术用户流过滤。
答案 1 :(得分:0)
我想我会提供一些背景信息。
首先,术语“桶”和“旅”。事实证明,有一个叫做水桶大队的东西...一种类似于扑救大火的团队努力...在那里,有一堆人站着不动,但将水桶传递给旁边的人,产生恒定的满桶的水流。
此外,如上所述,PHP对存储桶和旅的采用来自Apache [Buckets and Brigades](http://www.apachetutor.org/dev/brigades],也许……在方法论和推理方面给出了很好的解释。
但从本质上讲,这种想法是,如果您需要在发送某些内容之前对其进行修改,则在流中进行操作有很多好处,尤其是在使用存储桶和旅进行建模时。