I would really love to implement a php_user_filter::filter()
. But therefore I have to know what a bucket brigade is. This seems to be a resource which I can operate with the stream_bucket_*
functions. But the documentation is not really helpful. The best I could find are those examples in stream_filter_register()
.
I'm especially curios what these stream_bucket_new()
and stream_bucket_make_writeable()
can do.
Update: It seems that PHP is exposing an internal data structure of Apache.
Ah, welcome to the least documented parts of the PHP manual! [I opened a bug report about it; maybe this answer will be helpful for documenting it: https://bugs.php.net/bug.php?id=69966]
The bucket brigade
To start with your initial question, the bucket brigade is just a name to the resource named
userfilter.bucket brigade
.You are passed two different brigades in as first and second parameters to
php_user_filter::filter()
. The first brigade is the input buckets you read from, the second brigade is initially empty; you write to it.Regarding your update about the data structure… It's really just a doubly linked list with strings basically. But it may well be that the name was stolen from there ;-)
stream_bucket_prepend()
/stream_bucket_append()
The expected
$brigade
is the output brigade aka the second parameter onphp_user_filter::filter()
.The
$bucket
is astdClass
object like it is returned bystream_bucket_make_writable()
orstream_bucket_new()
.These two functions just prepend or append the passed bucket to the brigade.
stream_bucket_new()
To demystify this function, analyze first what it's function signature is:
First argument is the
$stream
you're writing this bucket to. Second is the$buffer
this new bucket will contain.[I'd like to note here that the
$stream
parameter actually is not very significant; it's just used to check whether we need to allocate memory persistently so that it survives through requests. I just suppose that you can make PHP nicely segfault by passing a persistent stream in here, when operating on a non-persistent filter...]There is now an
userfilter.bucket
resource created which is assigned to a property of a (stdClass
) object namedbucket
. That object has also two other properties:data
anddatalen
, which contain the buffer and the buffer size of this bucket.It will return you a
stdClass
which you can pass in tostream_bucket_prepend()
andstream_bucket_append()
.stream_bucket_make_writable()
It shifts the first bucket from the
$brigade
and returns it. If the$brigade
was emptied, it returnsnull
.Further notes
When
php_user_filter::filter()
is called, the$stream
property on the objectfilter()
is called on will be set to the stream we're currently working on. That's also the stream you need to pass tostream_bucket_new()
when calling it. (The$stream
property will be unset again after the call. You can't reuse it in e.g.php_user_filter::onClose()
).Also note that even when you're returned a
$datalen
property, you do not need to set that property in case you change$data
property before passing it tostream_bucket_prepend()
orstream_bucket_append()
.The implementation requires you (well, it expects that or will throw a warning) that you read all the data from the
$in
bucket before returning.There is another case of the documentation lying to us: in
php_user_filter::onCreate()
, the$stream
property is not set. It will only be set duringfilter()
method call.Generally, don't use filters with non-blocking streams. I tried that once and it went horribly wrong … And it's not likely that's ever going to be fixed...
Sum up (examples)
Let's start with the simplest case: writing back what we got in.
All what happens here is getting buckets from
$in
bucket brigade and putting it back into$out
bucket brigade.Okay, now try to manipulate our input.
Now we registered the
reverse://
protocol, which reverses your string (each write is being reversed on it's own here; write order is still preserved). So, we obviously now need to manipulate the bucket data and prepend it here.Now, what's the use case for
stream_bucket_new()
? Usually you can just append to$bucket->data
; yes, you even can concatenate all the data into the first bucket, but whenflush()
'ing it might be possible that nothing is in bucket brigade and you want to send a last bucket, then you need it.With that (and the existing documentation about
php_user_filter
class), one should be able to do all sorts of magic userland stream filtering by combining all these powerful possibilities into even stronger code.