Skip to content
intermediate Phase 63 · Extension Points

Plugins Deep Dive — Before, After, and Around Methods

Advanced plugin patterns in Magento 2: before/after/around plugins, plugin interception mechanics, plugin limitations, and generated code analysis

1h
1 problems
Topic Progress 0%

Plugin Types — Before, After, Around

Plugin Declaration

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Model\Product">
        <plugin name="vendor_product_plugin"
                type="Vendor\Module\Plugin\ProductPlugin"
                sortOrder="10"/>
    </type>
</config>

Before Plugin

Runs before the original method. Can modify arguments:

namespace Vendor\Module\Plugin;

class ProductPlugin
{
    public function beforeSetName(
        \Magento\Catalog\Model\Product $subject,
        string $name
    ): string {
        return strtoupper($name);
    }
}

After Plugin

Runs after the original method. Can modify the return value:

public function afterGetName(
    \Magento\Catalog\Model\Product $subject,
    string $result
): string {
    return $result . ' (Modified)';
}

Around Plugin

Wraps the original method. Has full control:

public function aroundGetName(
    \Magento\Catalog\Model\Product $subject,
    callable $proceed
): string {
    $startTime = microtime(true);
    $result = $proceed();
    $elapsed = microtime(true) - $startTime;
    $this->logger->info('getName took: ' . $elapsed);
    return $result;
}

If $proceed() is not called, the original method is skipped.

Plugin Interception Mechanics

Generated Interceptor Class

When you create a plugin, Magento generates an interceptor class:

// Generated: var/classes/Vendor/Module/Plugin/ProductPluginInterceptor.php
namespace Vendor\Module\Plugin;

class ProductPluginInterceptor
{
    public function aroundGetName(
        \Magento\Catalog\Model\Product $subject,
        callable $proceed
    ): string {
        // Plugin logic here
        $result = $proceed();
        return $result;
    }
}

How Interception Works

  1. DI generates interceptor classes for target classes with plugins
  2. ObjectManager returns the interceptor instead of the original class
  3. Each method is wrapped with plugin calls
  4. Execution order: before plugins -> original method -> after plugins
// ObjectManager returns interceptor
$product = $objectManager->get(\Magento\Catalog\Model\Product::class);
// $product is actually ProductInterceptor, not Product

Plugin Chain Execution

beforePlugin1 -> beforePlugin2 -> OriginalMethod -> afterPlugin2 -> afterPlugin1

Around plugins wrap the entire chain:

aroundPlugin1 -> { beforePlugin2 -> OriginalMethod -> afterPlugin2 } -> afterPlugin1

Regenerating Interceptors

After adding or removing plugins, regenerate:

php bin/magento setup:di:compile
php bin/magento cache:clean

Plugin Limitations

Cannot Plugin Non-Public Methods

Plugins only work on public methods:

// This will NOT work
<type name="Vendor\Module\Model\Product">
    <plugin name="plugin" type="Vendor\Module\Plugin\Plugin"/>
</type>

// Target class
public class Product {
    protected function validate() { /* ... */ }  // Cannot plugin this
    public function save() { /* ... */ }          // Can plugin this
}

Cannot Plugin Final Classes or Methods

final class Product {
    public function save() { /* ... */ }  // Cannot plugin
}

// Also cannot plugin if method is final
public final function save() { /* ... */ }

Cannot Plugin Static Methods

public static function factory() { /* ... */ }  // Cannot plugin

Cannot Plugin Methods Called via self::

class Product {
    public function save() {
        $this->validate();  // Can plugin this
        self::doSave();     // Cannot plugin self:: calls
        static::doSave();   // Can plugin this (late static binding)
    }
}

Cannot Plugin Constructor

Constructors are never intercepted by plugins.

Performance Impact

Each plugin adds overhead. Profiling shows:

  • Before plugin: ~0.1ms per call
  • After plugin: ~0.1ms per call
  • Around plugin: ~0.15ms per call

Heavy plugins on high-traffic methods accumulate overhead.

Generated Code Analysis

Interceptor Files Location

Generated interceptor classes are in:

var/classes/Vendor/Module/Plugin/ClassNameInterceptor.php

Analyzing Generated Code

// View generated interceptor
$file = BP . '/var/classes/Vendor/Module/Plugin/ProductInterceptor.php';
echo file_get_contents($file);

Plugin Configuration Methods

<type name="Magento\Catalog\Model\Product">
    <plugin name="my_plugin"
            type="Vendor\Module\Plugin\ProductPlugin"
            sortOrder="10"
            disabled="false"/>
</type>
Attribute Description
name Unique plugin identifier
type Plugin class
sortOrder Execution order
disabled Set true to disable

Disabling Plugins

<type name="Magento\Catalog\Model\Product">
    <plugin name="third_party_plugin" disabled="true"/>
</type>

Plugin on Interface

<!-- Plugin on interface applies to all implementations -->
<type name="Magento\Catalog\Api\ProductRepositoryInterface">
    <plugin name="repo_plugin" type="Vendor\Module\Plugin\RepoPlugin"/>
</type>

This intercepts all classes implementing ProductRepositoryInterface.

Practice Problems

0 / 1 solved
Plugin Not Executing

A plugin is declared but never executes. Diagnose the issue.

Quiz

1. What method must a before plugin return?

Question 1 options

2. What happens if an around plugin does not call $proceed()?

Question 2 options

3. Can you plugin a protected method?

Question 3 options

4. What command regenerates plugin interceptor classes?

Question 4 options

Flashcards

Question

What does a before plugin return?

Answer

Modified arguments for the original method

Question

What does an after plugin receive?

Answer

The original method's return value, which it can modify and return

Question

What does an around plugin receive?

Answer

A callable $proceed that invokes the original method

Question

Can you plugin final classes?

Answer

No, final classes and final methods cannot be plugged

Question

Where are generated interceptor classes stored?

Answer

var/classes/ directory as ClassNameInterceptor.php

Revision Notes

Key Takeaways

  • 1. Before plugins modify arguments, after plugins modify return values, around plugins wrap entire methods
  • 2. Magento generates interceptor classes that wrap target methods with plugin calls
  • 3. Plugins cannot intercept non-public, final, static, or constructor methods
  • 4. sortOrder controls execution order; before plugins run in ascending order, after in descending
  • 5. Run setup:di:compile after adding or removing plugins
  • 6. Plugins on interfaces apply to all implementing classes

Interview Tips

  • Explain the difference between before, after, and around plugins
  • Discuss how Magento generates interceptor code for plugins
  • Know the limitations: no final, no static, no protected methods
  • Explain plugin execution order with multiple plugins

Cheat Sheet

Plugins Deep Dive Cheat Sheet

Plugin types:

  • before: modify arguments, return modified args
  • after: receive return value, return modified result
  • around: wrap method with $proceed callable

Execution order:
before1 -> before2 -> original -> after2 -> after1

Limitations:

  • Public methods only
  • No final classes/methods
  • No static methods
  • No constructors
  • self:: calls not intercepted

Config:

<type name="TargetClass">
  <plugin name="name" type="PluginClass" sortOrder="10"/>
</type>

Regenerate: php bin/magento setup:di:compile