Magento 2: Wtyczka przed / wokół / po interakcji

32

W Magento 2, kiedy tworzysz wtyczkę „około”

public function aroundRenderResult(
    \Magento\Framework\Controller\ResultInterface $subject,
    \Closure $proceed,
    ResponseHttp $response
) {
    //...
    $proceed($response);
    //...      
}    

możesz przejść do następnej wtyczki, kończąc się wywołaniem rzeczywistej oryginalnej metody, poprzez wywołanie / wywołanie przekazanej $proceedmetody. Jest to powszechny wzorzec projektowy, często spotykany w implementacjach oprogramowania pośredniego PHP Frameworks.

Jednak - wprowadza pewne zamieszanie w / r / t w szczegółach implementacji. konkretnie

Jeśli oprócz aroundPluginobiektu zdefiniowano obiekt lub klasę beforelub afterwtyczkę, kiedy są one uruchamiane w odniesieniu do łańcucha wokół wtyczek?

tj. czy wszystkie wcześniejsze metody zostaną uruchomione przed uruchomieniem jakichkolwiek metod wtyczek? Czy będzie przed wtyczek tylko ogień przed końcowym, faktycznych prawdziwe pożary metoda?

Konkretnym problemem, który próbuję wyśledzić, jest to, że nie mogę uzyskać wtyczki dołączonej do metody wysyłania przedniego kontrolera Magento 2, gdy Magento jest w trybie buforowania pełnej strony . Pamięć podręczna pełnej strony działa przy użyciu wtyczki wokół, która się nie wywołuje $proceed($response). Próbowałem zagłębić się w część kodu wokół tych wtyczek i znalazłem system trudny do uzasadnienia, nie wiedząc, w jaki sposób to działa.

tj. - opis na stronie dokumentacji programistów wydaje się, w tym konkretnym przypadku, niedokładny. Nie jest jasne, czy dokumentacja jest nieprawidłowa, czy jest to niedawno wprowadzony błąd, czy jest to przypadek krawędziowy, czy też moja konfiguracja wtyczki jest nieprawidłowa.

Czy ktoś wie, poprzez bezpośrednią obserwację lub wiedzę kulturową, jak powinna działać ta hierarchizacja?

Alan Storm
źródło
Alan, czy masz ogólną zasadę, kiedy używać \closure $proceedkontra \callable $proceedwe wtyczce? Oficjalny doc tylko wspomina \callablei nigdy nie dotyka \closure.
thdoan

Odpowiedzi:

38

Wtyczki są najpierw sortowane według kolejności sortowania, a następnie według prefiksu metody.

Przykład: dla metody z 3 wtyczkami (PluginA, PluginB, PluginC) z następującymi metodami i sortowaniem:

  • Wtyczka A (sortOrder = 10)
    • beforeDispatch ()
    • afterDispatch ()
  • Wtyczka B (sortOrder = 20)
    • beforeDispatch ()
    • aroundDispatch ()
    • afterDispatch ()
  • Wtyczka C (sortOrder = 30):
    • beforeDispatch ()
    • aroundDispatch ()
    • afterDispatch ()

Wykonanie powinno być następujące:

  • PluginA :: beforeDispatch ()
  • PluginB :: beforeDispatch ()
  • PluginB :: aroundDispatch ()
    • PluginC :: beforeDispatch ()
    • PluginC :: aroundDispatch ()
      • Action :: dispatch ()
    • PluginC :: afterDispatch ()
  • PluginB :: afterDispatch ()
  • PluginA :: afterDispatch ()
Anton Kril
źródło
16

Z książki kucharskiej Magento 2:

Jeśli istnieje wiele wtyczek, które rozszerzają tę samą oryginalną funkcję, są one wykonywane w następującej kolejności:

  • wtyczka przed z najniższą sortOrder
  • wokół wtyczki z najniższą sortOrder
  • inne przed wtyczkami (od najniższej do najwyższej sortOrder)
  • inne wokół wtyczek (od najniższej do najwyższej sortOrder)
  • wtyczka po z najwyższą sortOrder
  • inne po wtyczkach (od najwyższej do najniższej sortOrder)
Raphael at Digital Pianism
źródło
1

Dla mnie powinno to działać jako:

  • jeśli kolejność sortowania nie jest zdefiniowana, jej ekwiwalent wynosi zero (a to oznacza, że ​​rzeczywista kolejność jest niezdefiniowana)
  • wtyczki powinny być sortowane według kolejności

Jeśli \Magento\Framework\Interception\Interceptor::___callPlugins()przejrzysz kod , zobaczysz, że wtyczki są wywoływane w kolejności przechowywanej w $pluginInfozmiennej. Te informacje przekazane z automatycznie wygenerowanej metody w takich przechwytywaczach jak

public function {method}()
{
    $pluginInfo = $this->pluginList->getNext($this->subjectType, '{method}');
    if (!$pluginInfo) {
        return parent::{method}();
    } else {
        return $this->___callPlugins('{method}', func_get_args(), $pluginInfo);
    }
}

Jak widzisz \Magento\Framework\Interception\PluginListInterfaceinterfejs i \Magento\Framework\Interception\PluginList\PluginListdomyślna implementacja odpowiedzialne za sortowanie wtyczek. Zobacz _inheritPlugins: 152 metoda

/**
 * Sort items
 *
 * @param array $itemA
 * @param array $itemB
 * @return int
 */
protected function _sort($itemA, $itemB)
{
    if (isset($itemA['sortOrder'])) {
        if (isset($itemB['sortOrder'])) {
            return $itemA['sortOrder'] - $itemB['sortOrder'];
        }
        return $itemA['sortOrder'];
    } elseif (isset($itemB['sortOrder'])) {
        return $itemB['sortOrder'];
    } else {
        return 1;
    }
} 

Dla mnie ta funkcja ma dwa błędy logiczne:

  • return $itemB['sortOrder'];powinno być return - $itemB['sortOrder'];
  • return 1; powinno być return 0;

Mam nadzieję, że ci to pomoże.

KAndy
źródło
ale czy $ pluginInfo jest w pełni załadowany z wtyczkami? A może dzieje się jakieś opóźnione ładowanie, które może mieć wpływ na zachowanie? Co oznacza porządek sortowania dla wielu wtyczek? tzn. czy to „przed wtyczką 1, wokół wtyczki 1, po wtyczce 1, przed wtyczką 2, wokół wtyczki 2, po wtyczce 2” lub „przed wtyczką 1”, „przed wtyczką 2, wokół wtyczki 1, wokół wtyczki 2” itp. Kod wygląda jak później, ale „getNext” zapełnia informacje o wtyczce w (może?) Leniwy sposób ładowania, a sposób, w jaki Magento unika rekurencji, sprawia, że ​​wszystko jest niejasne i trudno dostrzec, co jest błędem, co jest cechą.
Alan Storm,
Magento sort class plugin method, a nie metoda pluginów.
KAndy
I listę wtyczek można zmienić, na przykład, jeśli zostanie załadowana nowa aria.
KAndy
Istnieje pewna ukryta wiedza, która nie jest oczywista, ponieważ „posortuj klasę wtyczek, a nie metodę wtyczek” nie wyjaśnia, jakie są lub powinny być reguły interakcji wtyczek.
Alan Storm,
może ten link będzie przydatny magehero.com/posts/472/magento-2-interception
KAndy