Plugins are one of the new concepts in Magento world – they were introduced in Magento2 and are here to stay. Taking into account that Magento team wants developers to go by ‘composition over inheritance’ principle means that every developer should become familiar with plugins. In my opinion, calling this feature a “plugin” is a bit unfortunate, as it can be mistaken for “module”. That’s how modules are called in WordPress development. How about we use only “interceptors”?

Ok, so what are the said interceptors? Well… They intercept selected public methods and modify them. Instead of extending a class and overloading some methods, we can just create a class, write a method and use it to modify the original one! We can also configure the order in which the plugins will be executed. And that’s pretty cool. The fight between modules for rewriting core functions is now a thing of the dark and cold past.

Types of interceptors:

before interceptors
run before the original method
let you modify the parameters that will be used

after interceptors
run after the original method
let you modify the output

around interceptors
run both before and after the original method
let you overload the original function

One of the config parameters you have to provide when registering your interceptor is sortOrder. It determines whether your plugin is run before other plugins intercepting the same method or after them. What’s important to mention is that you can actually stop the plugin propagation in an around interceptor. That prevents the other around methods from firing.

Example interceptors

Let’s write a module that will change user redirection, after successfully sending mail through the contact form. Without a plugin, we would have to write a preference for Magento\Contact\Controller\Index\Post and create a class that would extend the original controller. Such a class will allow us to overload the execute method. We would have to rewrite the most important method of the class simply to change the redirect!

I’ll assume that you already have the Magently_TableTennis module from the previous article or that you can take care of the module boilerplate by yourself.

Since we want to modify the behaviour of Magento\Contact\Controller\Index\Post class, we have to tell Magento that it’s our target. We can do this by creating the di.xml file under /etc//di.xml. The code is pretty much self-explanatory:

//etc/frontend/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\Contact\Controller\Index\Post">
        <plugin name="contactPostControllerPlugin" type="Magently\TableTennis\Plugin\Post" sortOrder="1" />
    </type>
</config>

It’s a good practice to imitate the path of the original class when creating an interceptor class file.

Now, let’s create our interceptor:
//Plugin/PostPlugin.php

<?php

namespace Magently\TableTennis\Plugin;

class Post
{
    // It's just a regular constructor with classes we want to use in our code
    public function __construct(
        Magento\Framework\App\Request\DataPersistorInterface $dataPersistor,
        Magento\Framework\App\Action\Context $context
    ) {
        $this->dataPersistor = $dataPersistor;
        $this->context = $context;
    }

    // We have to pass the original class as the first parameter of every interceptor
    // The $result parameter is the result of the original execute method and is present only in the after plugins
    public function afterExecute(\Magento\Contact\Controller\Index\Post $subject, $result)
    {
        // In the original method, dataPersistor is cleared if a message has been sent
        // so we can use it as an indicator to whether a user can be redirected somewhere else
        // or fall back to the result of the original method
        if (!$this->dataPersistor->get('contact_us')) {
            return $this->context->getResultRedirectFactory()->create()->setPath('/');
        }
        return $result;
    }
}

And that’s it, our interceptor doesn’t even have to extend any other class. It is a simple and clean code, easy to write and maintain.
More information and some other examples you can find in an article on interceptors at Magento Dev Docs.