Blog

Plugins (Interceptors) in Magento2

As defined by the official document,

“A plugin or interceptor is a class that modifies the behavior of public class functions by intercepting a function call and running code before, after, or around that function call and allows to substitute or extend the behavior of original, public methods for any class or interface.”

In the simple words,

“A plugin or interceptor is a way to insert code dynamically without changing the original class behavior. It allows extending the core functionality without any modification to the core files.”

Extensions that wish to intercept and change the behavior of a public method can create a Plugin class.

This interception approach reduces conflicts among extensions that change the behavior of the same class or method. Your Plugin class implementation changes the behavior of a class function, but it does not change the class itself. Magento calls these interceptors sequentially according to a configured sort order, so they do not conflict with one another.

Magento 1 allowed to customize different classes and methods by rewriting a class. Powerful, but in this way, no modules could rewrite the same class, and hence, no flexibility. To overcome the rewrite conflicts and instability, Magento 2 comes with inceptors or plugins! And, the post is everything about it from the reasons to use, its restrictions, types and the method to create plugin in Magento 2.

What are the Magento 2 plugins?

In Magento 2 you can create plugins (interceptors) that allow you to extend functionality and execute your own code before, after, or around any PHP class public method.

To prevent conflicts, plugins for the same class are accomplished in sequence based on their sort order. For a module developer, Magento 2 plugins bring some benefits:

  • Forwarding any method call that is utilized on an object manager controlled object and taken programmatic action
  • Modifying the return value of any method call that is utilized on an object manager controlled object
  • Adjusting the arguments of any method call that is used on an object manager controlled object
  • Proceeding likewise when other modules are in progress of the same method in the same or predictable way.

However, it comes with limitations

Magento 2 plugins limitation

For the same reason, plugins have certain limitations. Magento 2 Interception plugin doesn’t work with:

  • Objects that are instantiated before Magento\Framework\Interception is bootstrapped
  • Final methods
  • Final classes
  • Any class that contains at least one final public method
  • Non-public methods
  • Class methods (such as static methods)
  • __construct and __destruct
  • Virtual types

Why should you use Interceptor or Plugins in Magento 2?

  • Minimum confliction among extensions that change the behavior of the same class or method
  • We can prevent collisions between plugins by using sort order attribute of plugin. Plugin can be called sequentially according to a sort order, so it does not conflict with other plugin class.
  • No issue of rewriting the system
  • Customize the same method in different modules
  • Ability to modify the return value of any method call that is used on an object manager controlled object
  • Ability to modify the arguments of any method call that is used on an object manager controlled object
  • Plugin class does not change class itself but it only modifies the behavior of class function.

Declaring a plugin

The di.xml file in your module declares a plugin for a class object:

<config> 
    <type name="{ObservedType}"> 
        <plugin name="{pluginName}" type="{PluginClassName}" sortOrder="1" disabled="false" /> 
    </type> 
</config>

You must specify these elements:

  • type name. A class or interface which the plugin observes.
  • plugin name. An arbitrary plugin name that identifies a plugin. Also used to merge the configurations for the plugin.
  • plugin type. The name of a plugin’s class or its virtual type. Use the following naming convention when you specify this element: \Vendor\Module\Plugin\<ClassName>.

The following elements are optional:

  • plugin sortOrder. Plugins that call the same method run them using this order.
  • plugin disabled. To disable a plugin, set this element to true. The default value is false.

As the following example, we will edit app\code\Vendor\Module\etc\di.xml, you need to insert the snippet:


<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <type name="Vendor\Module\Controller\Index\Example"> <plugin name="Vendor_Module_Plugin" type="Vendor\Module\Plugin\ExamplePlugin" sortOrder="10" disabled="false" /> </type> </config> 

For example, the following code define type name, we created ExamplePlugin.php file at app/code/Vendor/Module/Controller/Index/

Contents would be:


<?php

namespace Vendor\Module\Controller\Index; class ExamplePlugin extends \Magento\Framework\App\Action\Action { protected $title; public function execute() { echo $this->setTitle('Welcome'); echo $this->getTitle(); } public function setTitle($title) { return $this->title = $title; } public function getTitle() { return $this->title; } } 

With plugin name, we created Example.php file at app/code/Vendor/Module/Plugin/

Contents would be:

<?php

namespace Vendor\Module\Plugin; class TestPlugin{ } 

Defining a plugin

By applying code before, after, or around a public method, a plugin extends or modifies that method’s behavior.

The first argument for the before, after, and around methods is an object that provides access to all public methods of the observed method’s class.

Plugin method naming convention

It is a Magento best practice to capitalize the first letter of the class method name for which you want to create a plugin before adding beforearound or after prefixes to it.

For example, to create a plugin for the setName method of some class:

...
    public function setName($name)
    {
        ...
    }
...

In the plugin class, the setName method may have one of the following names:

  • beforeSetName
  • aroundSetName
  • afterSetName

If the first letter in the name of the class method name for which you want to create a plugin is the underscore character, then you do not need to capitalize it in the plugin class.

For example, to create a plugin for the _abc method of some class:

... 
public function _abc() { 
...
 }
 ...

Use the following method names for the _abc method in the plugin class:

  • before_abc 
  • around_abc 
  • after_abc 

Types of Plugins in Magento 2:

    1. Before listener
    2. After listener
    3. Around listener

1. Before Listener

Before listeners are used whenever we want to change the arguments of an original method or want to add some behavior before an original method is called.

Before methods are the first methods to run in an observed method, and these methods must have the same name to the observed one’s name while the prefix label is before.

To apply the before methods for modifying the arguments of an observed method, you can return a modified argument. If there are multiple arguments, the returning will be carried out according to a range of those arguments. If the returning is invalid, that means the arguments for the observed method should not be modified.

  1. Before listeners are used whenever we want to change the arguments of an original method or want to add some behavior before an original method is called.
  2. First methods to run in an observed method
  3. Before listener is called by adding the prefix ‘before’ to the method name and setting the first letter of original method to capital.
  4. It lets you modify the parameters that will be used.
  5. Syntax: beforeMethodname()
Namespace Vendor\Module\Plugin;
class Plugin
{
 public function beforeMethodName(\Vendor\Module\Model\TargetClass $subject, $arg1, $arg2, $arg3 = null)
 {
 return [$arg1, $arg2, $arg3];
 }
}

Example:-

<?php

namespace Vendor\Module\Plugin;

class TestPlugin
{
	public function beforeSetTitle(\Vendor\Module\Controller\Index\Example $subject, $title)
	{
		$title = $title . " to ";
		echo __METHOD__ . "</br>";
		return [$title];
	}
}

Example 2: – When product is added, add qty 10 in the cart

We will use before listeners to change behavior of addProduct method of Magento\Checkout\Model\Cart.

This method is called whenever we add product to cart.

Original Method is public function addProduct($productInfo, $requestInfo = null)

Step 1: Create Configuration File

To use Plugins first of all we have to define it in di.xml.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
	<type name="Magento\Checkout\Model\Cart">
		<plugin name="MyCart" type="Thecoachsmb\QuantityChange\Plugin\Model\Cart" sortOrder="1" />
	</type>
</config>

Step 2: Write Function to change the quantity change logic

Now create file Cart.php in folder ‘Thecoachsmb\QuantityChange\Plugin\Model’.

Before listener is called by adding prefix ‘before’ to the method name and setting first letter of original method to capital.
Now method addProduct will become beforeAddProduct.

<?php
	
	namespace Thecoachsmb\QuantityChange\Plugin\Model;

	class Cart
	{
		public function beforeAddProduct(
			\Magento\Checkout\Model\Cart $subject,
			$productInfo,
			$requestInfo = null
		) {
			$requestInfo['qty'] = 10; // increasing quantity to 10
			return array($productInfo, $requestInfo);
		}
	}

Here we are changing parameters. We set quantity to 10. Now it will always add 10 quantities of the product whenever we add product to cart. So we will use before listener when we want to change parameter of an method.

2. After Listener

After methods start running right after the observed method is finished, and these methods must have the same name to the observed one’s name while the prefix label is “after”.

After methods have responsibility for editing the results of an observed method in the correct way and being asked to get a return value. Because Magento 2 after plugins also gain the access to input parameters. The ‘after’ prefix must be included in the name of this method.

  1. After listeners are used whenever we want to change the arguments of an original method or want to add some behavior after an original method is called.
  2. Starts running after the observed method is finished
  3. After listener is called by adding the prefix ‘after’ to the method name and setting the first letter of original method to capital.
  4. It let you modify the output
  5. Responsible for editing the results of an observed method in the right way and must have a return value.
  6. Syntax: afterMethodname()

For example:

Namespace Vendor\Module\Plugin;
class Plugin
{
public function afterMethodName(\Vendor\Module\Model\TargetClass $subject, $result)
{
return $result;
}
}

Example:-

<?php

namespace Vendor\Module\Plugin;

class TestPlugin
{
	public function afterSetTitle(\Vendor\Module\Controller\Index\Example $subject, $result)
	{
		echo __METHOD__ . "</br>";
		return '<h1>'. $result . 'Thecoachsmb Site' .'</h1>';
	}
}

Example 2: –

We will use after listeners to change behavior of getName method of Magento\Catalog\Model\Product.

This method returns the name of Product.

Original Method is

public function getName()

<?xml version="1.0"?>
<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="MyCart" type="Thecoachsmb\Check\Model\Product" sortOrder="1" />
	</type>
</config>

Now create file Product.php in folder ‘Thecoachsmb\Check\Model’.
After listener is called by adding prefix ‘after’ to the method name and setting first letter of original method to capital.
Now method addProduct will become afterGetName.

<?php
	
	namespace Thecoachsmb\Check\Model;

	class Product
	{
		public function afterGetName(\Magento\Catalog\Model\Product $subject, $result) {
			return "Apple ".$result; // Adding Apple in product name
		}
	}

3. Around Listener

Around listeners are used when we want to change both the arguments and the returned values of an original method or add some behavior before and after an original method is called.

Around methods enables the code to run before and after the observed method, so you can override a method. The ‘around’ prefix is included in the name of this method.

  1. Inceptors run before and after the observed method.
  2. Allows overriding a method
  3. Used when you want to change both the arguments and the returned values of the observed method or add behavior before and after the observed method is called.
  4. Around listener is called by adding the prefix ‘around’ to the method name and setting the first letter of original method to capital.
  5. It must have a return value
  6. Syntax: aroundMethodname()

Before the arrange of the original method’s argument, a callable from around methods will be called to the next method in the chain, that means the next plugin or the observed function is also called.

The first input argument is $subject – the original class object, the second one is $proceed – the callable variable that allows executing original function and other plugins in a queue, next input parameters of the original function.

Note: In case the callable is not declared, the calling to neither the next plugin nor the original method is achieved.

For example:

Namespace Vendor\Module\Plugin;
class Plugin
{
    public function aroundMethodName(\Vendor\Module\Model\TargetClass $subject, callable $proceed, $arg1, $arg2, $arg3)
    {
        $result = $proceed();
        return $result;
    }
}

Around methods enables to the execution of code both before and after the target method in one place.

$proceed argument is PHP closure, which in turn calls the target method.

Such methods help you to completely replace the target method.

Example:-

<?php

namespace Vendor\Module\Plugin;

class TestPlugin
{
	public function aroundSetTitle(\Vendor\Module\Controller\Index\Example $subject, callable $proceed, $title)
	{
		echo __METHOD__ . " - Before proceed() </br>";
		 $result = $proceed($title);
		echo __METHOD__ . " - After proceed() </br>";
		return $result;
	}
}

Example 2:-

We will use around listeners to change behavior of addProduct method of Magento\Checkout\Model\Cart.

Around listener is called by adding prefix ‘arround’ to the method name and setting first letter of original method to capital.
Now method addProduct will become aroundAddProduct.

<?php
	
	namespace Thecoachsmb\Check\Model;

	class Cart
	{
		public function aroundAddProduct(
			\Magento\Checkout\Model\Cart $subject,
			\Closure $proceed,
			$productInfo,
			$requestInfo = null
		) {
			$requestInfo['qty'] = 10; // setting quantity to 10
			$result = $proceed($productInfo, $requestInfo);
			// change result here
			return $result;
		}
	}

The around listener methods must have a return value.
The return value is formed in such way that the parameters following the $closure parameter in the around listener method definition are passed to the $closure function call in a sequential order.

How to disable Magento 2 plugins?

Magento 2 plugins can be disabled in a di.xml file. To disable a plugin, set the disabled parameter of the plugin declaration to true.

<type name="Magento\Checkout\Block\Checkout\LayoutProcessor">
 <plugin name="ProcessPaymentConfiguration" disabled="true"/>
</type>

When ProcessPaymentConfiguration is the name of the plugin declared in the vendor/magento/module-payment/etc/frontend/di.xml.

Remember that the same class can be called two ways: with the leading slash or without.

\Magento\Checkout\Block\Checkout\LayoutProcessor and Magento\Checkout\Block\Checkout\LayoutProcessor

are both valid.

When you want to disable the plugin, you need to use the same path format to call and disable the plugin.

Example:

For example there is a PHP class with public functions “setName”, “getName”, “save”:

<?php

namespace VendorName\ModuleName\Folder;

class SomeModel
{
    private $name;
   
    public function setName($name, $storeId = 0) {
        $this->name[0] = $name;
    }

    public function getName($storeId = 0) {
        return $this->name[$storeId];
    }

    public function save($fields, $customData) {
        /* some save logic here */
    }
}

Dependency Injections Compilation

After creating a new plugin, do not forget to regenerate the Dependency injection to make it work. Run this CLI command:

php bin/magento setup:di:compile

Leave a Reply

Your email address will not be published. Required fields are marked *