我真的很想实现一个 php_user_filter::filter()
.但因此我必须知道什么是斗式旅。这似乎是我可以使用 stream_bucket_*
操作的资源职能。但是文档并没有真正的帮助。我能找到的最好的例子是 stream_filter_register()
中的那些例子。 .
我特别好奇这些是什么 stream_bucket_new()
和 stream_bucket_make_writeable()
可以做。
更新:PHP 似乎暴露了 internal data structure of Apache .
最佳答案
啊,欢迎阅读 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://
协议(protocol),它反转您的字符串(每次写入都在此处自行反转;写入顺序仍然保留)。因此,我们显然现在需要操作存储桶数据并将其添加到此处。现在,
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 的现有文档),人们应该能够通过将所有这些强大的可能性组合成更强大的代码来进行各种神奇的用户态流过滤。
关于php - 什么是斗式旅?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27103269/