Я бы очень хотел реализовать php_user_filter::filter()
. Но поэтому я должен знать, что ведерная бригада является. Кажется, это ресурс, которым я могу управлять с stream_bucket_*
функции. Но документация не очень полезна. Лучшее, что я мог найти, это те примеры в stream_filter_register()
.
Я особенно любопытен, что это stream_bucket_new()
а также stream_bucket_make_writeable()
сможет сделать.
Обновление: похоже, что PHP выставляет внутренняя структура данных Apache.
Ах, добро пожаловать в наименее документированные части руководства по PHP! [Я открыл сообщение об ошибке об этом; возможно этот ответ будет полезен для документирования: https://bugs.php.net/bug.php?id=69966]
Чтобы начать с вашего первоначального вопроса, бригада ведра — это просто имя ресурса, названного userfilter.bucket brigade
,
Вы передали две разные бригады в качестве первого и второго параметров php_user_filter::filter()
, Первая бригада — это входные сегменты, из которых вы читаете, вторая бригада изначально пуста; ты пишешь в него.
Что касается вашего обновления о структуре данных … Это на самом деле просто двусвязный список со строками. Но вполне может быть, что имя было украдено оттуда 😉
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(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_writeable(resource $brigade): stdClass|null
Это сдвигает первое ведро из $brigade
и возвращает его. Если $brigade
был опустошен, он возвращается null
,
когда php_user_filter::filter()
называется, $stream
недвижимость на объекте filter()
Будет вызван поток, над которым мы сейчас работаем. Это также поток, который нужно передать stream_bucket_new()
при звонке (The $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
учебный класс), нужно уметь выполнять все виды волшебной фильтрации пользовательских потоков, объединяя все эти мощные возможности в еще более сильный код.
Других решений пока нет …