Pytanie Co to jest brygada kubełkowa?


Naprawdę chciałbym zaimplementować a php_user_filter::filter(). Ale dlatego muszę wiedzieć, co brygada wiadro jest. To wydaje się być zasób, który mogę operować z stream_bucket_* Funkcje. Ale dokumentacja nie jest naprawdę pomocna. Najlepsze, co mogłem znaleźć, to te przykłady stream_filter_register().

Jestem szczególnie ciekaw, co te stream_bucket_new() i stream_bucket_make_writeable() może zrobić.


Aktualizacja: Wygląda na to, że PHP ujawnia wewnętrzna struktura danych Apache.


21
2017-11-24 11:00


pochodzenie


Zgaduję, że to tylko kawałki oktetów. Lub obsługuje porcje oktetów - Tim Seguine
Czy to ma jakieś aktualizacje? Uważam, że brak dokuczania dokumentów ... Nie byłem w stanie znaleźć dobrego wyjaśnienia tego, co się naprawdę dzieje. Większość artykułów / poradników merly drapie powierzchnię i są to najczęściej lista kroków bez patrzenia za kurtyną. Przykłady: etutorials.org/Server+Administration/upgrading+php+5/... & codediesel.com/php/creating-custom-stream-filters - Hirnhamster
przydatny? Nadchodzi brygada kubełkowa ... - Ryan Vincent
W końcu udało mi się stworzyć żywy przykład takiego php_user_filter: TokenBucketFilter. - Markus Malkusch


Odpowiedzi:


Ach, witaj w najmniej udokumentowanych częściach instrukcji PHP! [Otworzyłem o tym raport o błędzie; może ta odpowiedź będzie pomocna przy jej dokumentowaniu: https://bugs.php.net/bug.php?id=69966]

Brygada kubełkowa

Aby rozpocząć od pierwszego pytania, brygada kubełkowa jest po prostu nazwą zasobu o nazwie userfilter.bucket brigade.

Zostały ci przekazane dwie różne brygady jako pierwsze i drugie parametry php_user_filter::filter(). Pierwsza brygada to wiadra wejściowe, z których czytasz, druga brygada jest początkowo pusta; piszesz do niego.

Jeśli chodzi o twoją aktualizację struktury danych ... To naprawdę tylko podwójnie związana lista z łańcuchami. Być może jednak nazwa została skradziona ;-)

stream_bucket_prepend() / stream_bucket_append()

stream_bucket_prepend(resource $brigade, stdClass $bucket): null
stream_bucket_append(resource $brigade, stdClass $bucket): null

Oczekiwany $brigade to brygada wyjściowa jest drugim parametrem na php_user_filter::filter().

The $bucket jest stdClass obiekt taki jak jest zwracany przez stream_bucket_make_writable() lub stream_bucket_new().

Te dwie funkcje tylko dodają lub przekazują przekazane wiadro brygadzie.

stream_bucket_new()

Aby zdemodulować tę funkcję, najpierw przeanalizuj, jaki jest jej podpis funkcji:

stream_bucket_new(resource $stream, string $buffer): stdClass

Pierwszym argumentem jest $stream piszesz to wiadro na. Drugi to $buffer to nowe wiadro będzie zawierało.

[Chciałbym tutaj zaznaczyć, że $stream parametr rzeczywiście nie jest bardzo znaczący; służy tylko do sprawdzenia, czy musimy nieustannie przydzielać pamięć, aby mogła przetrwać przez żądania. Po prostu przypuszczam, że możesz sprawić, że PHP ładnie się odskakuje, przekazując tu stały strumień, gdy działa na nietrwałym filtrze ...]

Teraz jest userfilter.bucket utworzony zasób, który jest przypisany do właściwości (stdClass) obiekt o nazwie bucket. Ten obiekt ma również dwie inne właściwości: data i datalen, które zawierają bufor i rozmiar bufora tego zasobnika.

Zwróci ci a stdClass do którego możesz przejść stream_bucket_prepend() i stream_bucket_append().

stream_bucket_make_writable()

stream_bucket_make_writeable(resource $brigade): stdClass|null

Przesuwa pierwsze wiadro z $brigade i zwraca go. Jeśli $brigade został opróżniony, wraca null.

Dalsze uwagi

Gdy php_user_filter::filter() nazywa się, $stream właściwość na obiekcie filter() zostanie wywołany, zostanie ustawiony na strumień, nad którym obecnie pracujemy. To także strumień, do którego musisz przejść stream_bucket_new() kiedy to nazywam. (The $stream Właściwość będzie nieoprawny ponownie po wywołaniu. Nie możesz jej użyć ponownie na przykład w php_user_filter::onClose()).

Zwróć też uwagę, że nawet gdy wrócisz $datalen nie musisz ustawiać tej właściwości na wypadek zmiany $data własność przed przekazaniem jej stream_bucket_prepend() lub stream_bucket_append().

Wdrożenie wymaga od ciebie (no cóż, spodziewa się, że ostrzeże zostanie wysłane), że czytasz wszystkie dane z $inwiadro przed powrotem.

Jest inny przypadek dokumentacji leżącej nam: w php_user_filter::onCreate(), $stream właściwość to nie zestaw. Zostanie on ustawiony tylko podczas filter() wywołanie metody.

Zasadniczo nie należy używać filtrów z nie blokującymi strumieniami. Spróbowałem tego raz i poszło strasznie źle ... I prawdopodobnie nie zostanie to naprawione ...

Podsumuj (przykłady)

Zacznijmy od najprostszego przypadku: odtworzymy to, co mamy.

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")

Wszystko, co się tutaj dzieje, to pobieranie wiader $in brygada wiadrowa i włożenie jej z powrotem $out brygada wiadro.

Okay, teraz spróbuj manipulować naszymi danymi wejściowymi.

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")

Teraz zarejestrowaliśmy reverse:// protokół, który odwraca twój ciąg znaków (każdy zapis jest odwracany tutaj), kolejność zapisu jest nadal zachowywana). Tak więc, oczywiście musimy teraz manipulować danymi wiadra i wstawić je tutaj.

A teraz, w jakim celu stream_bucket_new()? Zwykle możesz po prostu dołączyć do $bucket->data; tak, można nawet połączyć wszystkie dane w pierwszym segmencie, ale kiedy flush()Może to być możliwe, że nic nie jest w brygadzie wiadro i chcesz wysłać ostatni wiadro, to potrzebujesz.

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")

Z tym (i istniejącą dokumentacją na temat php_user_filter klasa), powinno być możliwe robienie różnego rodzaju magicznego filtrowania strumienia użytkownika, łącząc wszystkie te potężne możliwości w jeszcze silniejszy kod.


30
2018-06-30 07:56



Naprawdę doceniam wysiłek, jaki włożyłeś w tę odpowiedź, ale dla mnie nadal nie wyjaśnia dokładnie, co to brygada kubełkowa jest i dlaczego istnieje potrzeba takiego podejścia w radzeniu sobie z (filtrowaniem) strumieni. Relacja OP o brygadach Apache brzmi przekonująco, ale zastanawiałem się, czy mógłbyś to potwierdzić że rzeczywiście jest to powód, dla którego stosowane jest podejście "brokadowe". - Decent Dabbler
Jak powiedziano, brygada kubełkowa jest zasobem. Ten zasób znajduje się pod maską uporządkowanej listy wiader / ciągów. (które można przeciągnąć z / dodać do tych funkcji.) Nie mogę potwierdzić, czy tak jest czemu został użyty, musiałbyś zapytać autora implementacji filtra strumieniowego, Wez Furlong. Po prostu mogę zgadnąć, więc nie mam więcej pomysłu niż ty. Zastanawiam się też, dlaczego chcesz wiedzieć dokładnie że? Nie wiem, jak to pomaga w implementowaniu filtrów ... - bwoebi
Cóż, przechodząc przez link o brygadach Apache, wydaje się, że nie można / nie powinno się zapisywać więcej danych do wiadra niż jego początkowej długości i należy utworzyć nowe wiadro, jeśli potrzeba więcej danych do wstawienia do wyjścia strumień niż w strumieniu wejściowym. Gdyby żeto jest cała uwaga tego "brygu", to jest raczej cenna informacja, nie? - Decent Dabbler
Nie patrzyłem tak dokładnie na brygady z wiadrem Apache; przynajmniej wdrożenie (lxr.php.net/xref/PHP_5_6/ext/standard/user_filters.c#470) bezpośrednio kopiuje cały zmanipulowany ciąg, który mamy z powrotem do wiadra. Myślę, że pośrednio zasugerowałem, że jest to możliwe w mojej odpowiedzi. ["Możesz nawet połączyć wszystkie dane w pierwszym wiadrze"]. Być może jest to kwestia brygadowania kubka, gdy mówimy o rozszerzeniach PHP, które mogą wydajniej pracować, jeśli nie muszą wykonywać dodatkowych przydziałów; ale php_user_filter bezpośrednio ujawnia wewnętrzny interfejs API w bezpieczny droga. - bwoebi
@DecentDabbler Chciałbym dodać, że wewnętrzny interfejs API wygląda bardzo podobnie do tego, co opisano w tej witrynie apache. Nawet niektóre nazwy funkcji itp. Są bardzo podobne. Ale te funkcje są po prostu wystawione na rozszerzenia, a nie na PHP użytkownika. The php_user_filter bardzo dużo jest (bezpieczny) zerwanie wewnętrznej implementacji oprócz wewnętrznej implementacji. Tak więc, myślę, że ostatecznie można bezpiecznie powiedzieć, że pochodzi on od brygad Apache. - bwoebi