Magento 2 Plugins Explained: Before, After, and Around with Real Examples

If you’ve spent any time doing Magento 2 development, you’ve probably run into a situation where you needed to tweak how an existing method works — without touching the core files.

In Magento 1, the go-to solution was class rewrites. And if you ever worked with them, you know how quickly they turned into a nightmare. Two modules rewriting the same class? Good luck figuring out which one wins and why everything’s broken.

Magento 2 fixed this with plugins — also called interceptors. They’re cleaner, they play nicer with other modules, and once you understand how they work, they become your default tool for most customizations.

Let me walk through what they are, how to set one up, and where developers usually go wrong.

What a Magento 2 Plugin Actually Is

A plugin lets you hook into a public method of any class and run your own code — before it executes, after it executes, or wrapped completely around it.

You’re not replacing the class. You’re not copying it into your module and modifying it. You’re just telling Magento: “hey, when this specific method gets called, I want to do something extra.”

That distinction matters a lot. It means multiple modules can all have plugins on the same method without stomping on each other. The core file stays untouched. Upgrades don’t blow up your customizations.

The Three Types of Plugins

Before Plugin

Runs before the original method. The main use case is modifying the arguments being passed in.

php

public function beforeSetName(
    \Magento\Catalog\Model\Product $subject,
    $name
) {
    return ['Custom ' . $name];
}

One thing that gets people early — you have to return the arguments as an array, even if there’s only one. Forget that and your modification just won’t apply.

After Plugin

Runs after the original method finishes. You get access to whatever the method returned, and you can change it before it goes anywhere.

php

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

This is probably the most commonly used type. Adding a prefix to product names, modifying return values, appending something to a response — after plugins handle all of that cleanly.

Around Plugin

Wraps the entire method. You control what happens before, whether the original method even runs, and what happens after.

php

public function aroundGetName(
    \Magento\Catalog\Model\Product $subject,
    callable $proceed
) {
    $result = $proceed();

    return $result . ' Modified';
}

The $proceed() call is what actually runs the original method. If you don’t call it, the original never executes — which is sometimes what you want, but can cause serious issues if you do it by accident.

Honestly, around plugins are overused. A lot of developers reach for them by default when a before or after plugin would do the job with less complexity. I’d say if you can solve the problem with before or after, do that instead.

Setting Up a Plugin — Step by Step

Step 1: Create the Plugin Class

php

// app/code/Vendor/Module/Plugin/ProductPlugin.php

namespace Vendor\Module\Plugin;

class ProductPlugin
{
    public function afterGetName(
        \Magento\Catalog\Model\Product $subject,
        $result
    ) {
        return 'Sale - ' . $result;
    }
}

Step 2: Register It in di.xml

xml

<!-- app/code/Vendor/Module/etc/di.xml -->

<type name="Magento\Catalog\Model\Product">
    <plugin
        name="vendor_product_plugin"
        type="Vendor\Module\Plugin\ProductPlugin"
        sortOrder="10"/>
</type>

That’s it. Magento picks it up automatically. Every time getName() is called on a Product, your plugin fires.

The sortOrder controls execution order when multiple plugins exist on the same method. Lower number runs first. Worth paying attention to this when you’re in a project with several third-party modules — conflicts here can be subtle and annoying to debug.

What Plugins Can’t Do

This part is worth knowing upfront because running into these limitations mid-build is frustrating.

Plugins only work on public methods. That rules out protected methods, private methods, and constructors entirely.

They also can’t intercept:

  • final methods
  • final classes
  • Static methods

If you need to modify constructor behavior, Dependency Injection is the right path. If you’re dealing with a final class or static method, you’ll need to look at preferences or a different approach altogether.

A Real Example That Makes Sense

Say a store wants every product name to show with a “Sale — ” prefix sitewide. No core file edits, nothing that’ll break on the next Magento upgrade.

After plugin, ten lines of code:

php

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

Register it in di.xml, flush cache, done. Every product name across the store now has the prefix. And when Magento releases an update, this customization survives because nothing in core was touched.

That’s the practical value of plugins — not just that they’re “the right way,” but that they actually make your life easier six months later.

Mistakes That Come Up More Than They Should

Using around plugins when before or after would work — Around plugins add overhead and make the execution chain harder to follow. Use them only when you genuinely need to control whether the original method runs.

Forgetting to return arguments as an array in before plugins — This one’s silent. Your before plugin runs, nothing errors out, but the modification just doesn’t happen. Easy to miss if you’re not testing carefully.

Not calling $proceed() in an around plugin — The original method never runs. Sometimes this causes obvious breakage, sometimes it causes weird subtle issues that take a while to trace back to the plugin.

Ignoring sort order — If you’ve got multiple plugins on the same method and the order matters for your logic, set sortOrder explicitly. Don’t assume.

Overcomplicating things — A plugin that’s doing too much is usually a sign the logic should live somewhere else. Keep them focused.

When to Use Something Else Instead

Plugins are the right tool most of the time, but not always.

If you need to completely replace a class — swap in different logic entirely, not just modify a method — a preference in di.xml is more appropriate.

If the method you need to modify is protected or private, plugins won’t work at all. You’ll need to look at extending the class or restructuring your approach.

And if you’re dealing with constructor-level changes, that’s Dependency Injection territory.

Quick Reference

Plugin TypeRunsUse When
BeforeBefore original methodModifying input arguments
AfterAfter original methodModifying return value
AroundWraps entire methodControlling if/how method runs

Plugins aren’t complicated once you’ve written a few. The first one always takes longer than expected — mostly just getting the di.xml registration right and remembering the method signature format. After that it becomes pretty routine.

If you’re coming from Magento 1 and you’ve been avoiding plugins because rewrites felt more familiar, it’s worth investing an afternoon to get comfortable with them. The compatibility benefits alone make it worth it.

Working on a specific Magento customization and not sure if a plugin is the right approach? Drop it in the comments.

