5

I am trying to understand the concept of sort order when using plugin.

As per my understanding the sort order works in lower to higher when used before method and higher to lower when used in after method. As per official doc This work as expected when I use scenario 1 but not when I use scenario 2.

Scenario 1

I had used plugin for Product Model Class

Product A Plugin (Sort Order 10)

namespace Priyank\PluginA\Plugin;

class Product
{
    public function beforeGetName($subject)
    {
        $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/PluginTest.log');
        $logger = new \Zend\Log\Logger();
        $logger->addWriter($writer);
        $logger->info('Before Execute From Plugin A');
    }

    public function aroundGetName($subject, $proceed)
    {
        $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/PluginTest.log');
        $logger = new \Zend\Log\Logger();
        $logger->addWriter($writer);
        $logger->info('Around Before Proceed Execute From Plugin A');

        $return = $proceed();

        $logger->info('Around After Proceed Execute From Plugin A');

        return $return;
    }

    public function afterGetName($subject, $result)
    {
        $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/PluginTest.log');
        $logger = new \Zend\Log\Logger();
        $logger->addWriter($writer);
        $logger->info('After Execute From Plugin A');
        return $result;
    }    
}

Product B Plugin (Sort Order 20)

namespace Priyank\PluginB\Plugin;

class Product
{
    public function beforeGetName($subject)
    {
        $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/PluginTest.log');
        $logger = new \Zend\Log\Logger();
        $logger->addWriter($writer);
        $logger->info('Before Execute From Plugin B');
    }

    public function aroundGetName($subject, $proceed)
    {
        $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/PluginTest.log');
        $logger = new \Zend\Log\Logger();
        $logger->addWriter($writer);
        $logger->info('Around Before Proceed Execute From Plugin B');

        $return = $proceed();

        $logger->info('Around After Proceed Execute From Plugin B');

        return $return;
    }

    public function afterGetName($subject, $result)
    {
        $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/PluginTest.log');
        $logger = new \Zend\Log\Logger();
        $logger->addWriter($writer);
        $logger->info('After Execute From Plugin B');
        return $result;
    }
}

The output is as expected

PluginA::BeforeGetName()
    PluginA::aroundGetName() // Magento calls the first half until callable
PluginB::BeforeGetName()
    PluginB::aroundGetName() // Magento calls the first half until callable
        Action::dispatch()
    PluginB::aroundGetName() // Magento calls the Second half until callable
PluginB::afterGetName()
    PluginA::aroundGetName() // Magento calls the Second half until callable
PluginA::afterGetName()

Scenario 2 Just removed the around method in both the plugins rest remains same.

Output

PluginA::BeforeGetName()
PluginB::BeforeGetName()
PluginA::afterGetName()
PluginB::afterGetName()

Here I was expecting Plugin B After called first as it has higher sort order. But when around was not used Plugin A after was called first. Why?

  • What if you add Product C Plugin (Sort Order 30). Is it 10, 20, 30 for before, around, and after? – Dominic Xigen May 28 at 21:14
  • What are you suggesting? – Priyank May 29 at 9:09
  • I was curious. What if you add a new Product C Plugin (Sort Order 30). What is the order? – Dominic Xigen May 29 at 12:41
0

Because its mistake of official documentation. I've checked dev and master branch of version 2.3.

So plugin without 'around' have prio for 'after' method due to sort order it has.

Please check

Magento\Framework\Interception\PluginList\PluginList:getNext()

returns next result in case of 2 plugins with around for PluginB only:

array (
    1 => //before 
        array (
            0 => 'PluginA(10)',                 
            1 => 'PluginB(20)',         
        ),
    4 => //after
        array (
            0 => 'PluginA(10)',
            1 => 'PluginB(20)',
        ),
    2 => 'PluginB(20)', //around    
)

returns next result in case of 3 plugins with around for PluginC only:

array (
    1 => //before 
        array (
            0 => 'PluginA(10)',                 
            1 => 'PluginB(20)',     
            2 => 'PluginC(30)', 
        ),
    4 => //after
        array (
            0 => 'PluginA(10)',
            1 => 'PluginB(20)',
            2 => 'PluginC(30)', 
        ),
    2 => 'PluginC(30)', //around    
)

returns next result in case of 3 plugins, each has his own around:

array (
    1 => //before 
        array (
            0 => 'PluginA(10)',                 
        ),
    4 => //after
        array (
            0 => 'PluginA(10)',
        ),
    2 => 'PluginA(10)', //around    
)

array (
    1 => //before 
        array (
            0 => 'PluginB(20)',                 
        ),
    4 => //after
        array (
            0 => 'PluginB(20)',
        ),
    2 => 'PluginB(20)', //around    
)

array (
    1 => //before 
        array (
            0 => 'PluginC(30)',                 
        ),
    4 => //after
        array (
            0 => 'PluginC(30)',
        ),
    2 => 'PluginC(30)', //around    
)

So in this case official documentation is wrong telling that PluginA without 'around' will be executed after PluginB.

It happens because rest plugins methods('after' and 'before') inside plugins without 'around' merged by Interceptor to the next sorted plugin(to his interception method list) that has 'around' to not lose next callable if exists, keeping their sorted order at execution time.

Magento\Framework\Interception\PluginList\PluginList:_inheritPlugins() Ln:189

One thing is not clear for me why they haven't fixed this yet?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.