Magento 2: плагин до / вокруг / после взаимодействия

32

В Magento 2, когда вы создаете плагин "вокруг"

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

Вы можете перейти к следующему плагину, который завершается вызовом фактического оригинального метода, вызывая / вызывая переданный $proceedметод. Это общий шаблон проектирования, часто встречающийся в реализациях промежуточного программного обеспечения PHP Frameworks.

Однако - это вносит некоторую путаницу в детали реализации. конкретно

Если в дополнение к aroundPluginобъекту / классу определен плагин beforeили after, когда они запускаются относительно цепочки вокруг плагинов?

т. е. будут ли срабатывать все методы before перед тем, как сработают все методы плагинов? Или плагины будут срабатывать только до того, как сработает последний реальный метод?

Конкретная проблема, которую я пытаюсь отследить, заключается в том, что я не могу подключить плагин к методу диспетчеризации фронт-контроллера Magento 2, когда Magento находится в режиме полного кэширования страниц . Кеш полной страницы работает вокруг плагина, который не вызывает $proceed($response). Я попытался покопаться в некотором коде вокруг этих плагинов и обнаружил, что систему трудно рассуждать, не зная, как предполагается, что плагины работают.

то есть - описание на странице документации разработчика , в данном конкретном случае, представляется неточным. Неясно, является ли документация неправильной, или это недавно введенная ошибка, является ли это крайним случаем, или если моя конфигурация плагина неверна.

Кто-нибудь знает по прямому наблюдению или культурным знаниям, как должна работать эта расстановка приоритетов?

Алан Сторм
источник
Алан, у тебя есть эмпирическое правило, когда использовать \closure $proceedпротив \callable $proceedв плагине? Официальный документ только упоминает \callableи никогда не затрагивает \closure.
thdoan

Ответы:

38

Плагины сортируются сначала по порядку сортировки, а затем по префиксу метода.

Пример: для метода с 3 плагинами (PluginA, PluginB, PluginC) со следующими методами и sortOrder:

  • PluginA (sortOrder = 10)
    • beforeDispatch ()
    • afterDispatch ()
  • PluginB (sortOrder = 20)
    • beforeDispatch ()
    • aroundDispatch ()
    • afterDispatch ()
  • PluginC (sortOrder = 30):
    • beforeDispatch ()
    • aroundDispatch ()
    • afterDispatch ()

Ход выполнения должен быть следующим:

  • Плугина :: beforeDispatch ()
  • PluginB :: beforeDispatch ()
  • PluginB :: aroundDispatch ()
    • PluginC :: beforeDispatch ()
    • PluginC :: aroundDispatch ()
      • Действие :: отправка ()
    • PluginC :: afterDispatch ()
  • PluginB :: afterDispatch ()
  • Плугина :: afterDispatch ()
Антон Криль
источник
16

Из кулинарной книги Magento 2:

Если есть несколько плагинов, которые расширяют одну и ту же исходную функцию, они выполняются в следующей последовательности:

  • перед плагин с самым низким sortOrder
  • вокруг плагин с самым низким sortOrder
  • другие перед плагинами (от самого низкого до самого высокого sortOrder)
  • другие вокруг плагинов (от самого низкого до самого высокого sortOrder)
  • последующий плагин с самым высоким sortOrder
  • другие после плагинов (от самого высокого до самого низкого sortOrder)
Рафаэль в цифровом пианизме
источник
1

Для меня это должно работать как:

  • если порядок сортировки не определен, его эквивалент равен нулю (а это означает, что реальный порядок не определен)
  • плагины должны быть отсортированы по порядку

Если вы просматриваете код, \Magento\Framework\Interception\Interceptor::___callPlugins()вы можете увидеть, что плагины вызываются в порядке, хранящемся в $pluginInfoпеременной. Эта информация передается из автоматически сгенерированного метода в перехватчики типа

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

Как видите, \Magento\Framework\Interception\PluginListInterfaceинтерфейс и \Magento\Framework\Interception\PluginList\PluginListреализация по умолчанию отвечают за сортировку плагинов. Смотрите _inheritPlugins: 152 метод

/**
 * 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;
    }
} 

Для меня эта функция имеет две логические ошибки:

  • return $itemB['sortOrder'];должно быть return - $itemB['sortOrder'];
  • return 1; должно быть return 0;

Надеюсь, это поможет вам.

Канди
источник
а $ pluginInfo полностью загружен плагинами? Или происходит какая-то ленивая загрузка, которая может повлиять на поведение? Что означает порядок сортировки для нескольких плагинов? то есть «перед плагином 1, вокруг плагина 1, после плагина 1, перед плагином 2, вокруг плагина 2, после плагина 2» или «перед плагином 1», «перед плагином 2, вокруг плагина 1, вокруг плагина 2» и т. д. Код выглядит как более поздний, но «getNext» заполняет информацию о плагине (может быть?) Ленивой загрузкой, и то, как Magento избегает рекурсии со всем вокруг, делает все это неясным, и трудно определить, в чем ошибка, в чем особенность.
Алан Шторм
Плагин класса сортировки Magento, а не метод плагина.
Кэнди
И список плагинов можно изменить, например, если мы загрузим новую арию.
Кэнди
Есть некоторые неявные знания, которые у вас есть, которые неочевидны, потому что «сортируйте класс плагина, а не метод плагина» не проясняет, каковы или должны быть правила взаимодействия с плагином.
Алан Шторм
может быть, эта ссылка будет полезна magehero.com/posts/472/magento-2-interception
KAndy