Ответ 1
А, добро пожаловать в наименее задокументированные части руководства по 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 отлично segfault, передав постоянный поток здесь, при работе с непостоянным фильтром...]
Теперь создается ресурс 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()
.
Как правило, не используйте фильтры с неблокирующими потоками. Я попробовал это однажды, и это пошло ужасно неправильно... И это вряд ли когда-нибудь будет исправлено...
Сумма (примеры)
Давайте начнем с простейшего случая: напишите, что у нас получилось.
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), вы должны иметь возможность делать всевозможные магии фильтрации пользовательских потоков с помощью объединяя все эти мощные возможности в еще более сильный код.