Подтвердить что ты не робот

Что такое ведровая бригада?

Мне бы очень хотелось реализовать php_user_filter::filter(). Но поэтому я должен знать, что такое ведровая бригада. Кажется, это ресурс, который я могу использовать с функциями stream_bucket_*. Но документация не очень полезна. Лучшее, что я мог найти, это те примеры в stream_filter_register().

Я особенно любопытно, что эти stream_bucket_new() и stream_bucket_make_writeable() может сделать.


Обновление. Похоже, что PHP подвергает внутреннюю структуру данных Apache.

4b9b3361

Ответ 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), вы должны иметь возможность делать всевозможные магии фильтрации пользовательских потоков с помощью объединяя все эти мощные возможности в еще более сильный код.