啊,欢迎阅读 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 资源,该资源分配给名为 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 属性,也无需设置该属性,以防在将 $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 bucket brigade 获取桶并将其放回$out bucket brigade。
好的,现在尝试操纵我们的输入。
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()'ing 时,桶旅中可能没有任何内容并且您想发送最后一个桶,那么您需要它。
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 的现有文档),人们应该能够通过将所有这些强大的可能性组合成更强大的代码来执行各种神奇的用户空间流过滤。