Magento 2 Plugin Interaction – Before/Around/After Methods Explained

interceptormagento-2.0magento2plugin

In Magento 2, when you create an "around" plugin

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

you can proceed to the next around plugin, culminating with call the actual original method, by calling/invoking the passed in $proceed method. This is a common design pattern, often seen in PHP Frameworks middleware implementations.

However — it does present some confusion w/r/t to implementation details. Specifically

If, in addition to an aroundPlugin, an object/class has a before or after plugin defined, when do they fire in relation to the chain of around plugins?

i.e. will all the before methods fire before any around plugin methods fire? Or will before plugins only fire before the final, actual real method fires?

The specific problem I'm trying to track down is, I can't seem to get a plugin attached to the dispatch method the a Magento 2 front controller when Magento's in full page caching mode. The full page cache operates by an around plugin that does not call $proceed($response). I've tried digging into some of the code around these plugins and have found the system difficult to reason about without knowing how its intended that plugins work.

i.e. — the description on the dev docs page appears, in this one specific instance, to be inaccurate. It's unclear if the documentation is wrong, or if this is a recently introduced bug, if it's an edge case, or if my plugin configuration is wrong.

Does anyone know, by direct observation, or by cultural knowledge, how this prioritization is supposed to work?

Best Answer

Plugins are sorted by sort order first, and then by method prefix.

Example: for method with 3 plugins (PluginA, PluginB, PluginC) with following methods and sortOrder:

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

Execution flow should be following:

  • PluginA::beforeDispatch()
  • PluginB::beforeDispatch()
  • PluginB::aroundDispatch()
    • PluginC::beforeDispatch()
    • PluginC::aroundDispatch()
      • Action::dispatch()
    • PluginC::afterDispatch()
  • PluginB::afterDispatch()
  • PluginA::afterDispatch()
Related Topic