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:
finalmethodsfinalclasses- 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 Type | Runs | Use When |
|---|---|---|
| Before | Before original method | Modifying input arguments |
| After | After original method | Modifying return value |
| Around | Wraps entire method | Controlling 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.
