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. 

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 */
}
}

How to create plugin in Magento 2?

1. Create the etc/di.xml file in your module folder and add the following code there:

<?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="VendorName\ModuleName\Folder\SomeModel">
        <plugin name="mycompany_mymodule_plugin_modulename_Folder_somemodel"
                type="MyCompany\MyModule\Plugin\ModuleName\Folder\SomeModelPlugin" sortOrder="10" />
    </type>
</config>

VendorName\ModuleName\Folder\SomeModel - the name of the class whose method the plugin will be involved with;
mycompany_mymodule_plugin_modulename_Folder_somemodel - unique name of the plugin, e.g. formed with the plugin class name;
MyCompany\MyModule\Plugin\ModuleName\Folder\SomeModel - plugin class;
sortOrder - sets the execution priority of the plugin, among other plugins for this class.

Attention! The di.xml file can be created in the "etc" folder and in the "ect/frontend", "etc/adminhtml", etc., depending on the application area.

2. Also create the Plugin/ModuleName/Folder/SomeModelPlugin.php file for the interceptor class MyCompany\MyModule\Plugin\ModuleName\Folder\SomeModelPlugin in the module folder.

3. Add the following code in it:

<?php

namespace MyCompany\MyModule\Plugin\ModuleName\Folder;

use \VendorName\ModuleName\Folder\SomeModel;

class SomeModelPlugin
{
/* Magento 2 before plugin example */   public function beforeSetName(SomeModel $subject, $name, $storeId = 0)
    {
        return ['(' . $name . ')'];
    }

/* Magento 2 after plugin example */   public function afterGetName(SomeModel $subject, $result, $name, $storeId = 0)
    {
        return '|' . $result . '|';
    }

/* Magento 2 around plugin example */ public function aroundSave(SomeModel $subject, callable $proceed, $fields, $customData)     {
if ($someCondition){
       return $proceed($fields, $customData);
         }
return null;
}
}

Magento 2 Before Plugin

Before plugin is used to change the input data of the original function (setName).

The name of the function consists of the word "before" plus the original function name with the capital first letter, for example: before + SetName = beforeSetName.

The first input argument is $subject - the original class object, next input parameters of the original function.

Plugin function returns an array of modified input parameters (arguments). Result items order in the array should correspond to the input arguments order. If you don't want to change arguments and just run some code before original function execution then the plugin function should return a null value.

Magento 2 After Plugin

After plugin is used to modify the original function result or allows to run the code after the original function (getName) execution.

The name of the function consists of the word "after" plus the original function name with the capital first letter, for example: after + GetName = afterGetName.

The first input argument is $subject - the original class object, the second one is $result - the result of the original function or plugins that were executed earlier due to the sort order priority, next input parameters of the original function.

The plugin function returns a new result.

Important!

If you don't want to change the result and just want to run some code after the original function execution then you CANNOT return a null value as it was with the "before" plugin, since returning null, you actively change the result to NULL. Don't miss this.

Magento 2 Around Plugin

Around plugin - changes the original function result based on the input data or disables the execution of the original function (save).

It is a dangerous one.

Magento recommends to avoid using around plugins when they are not required, as their use can prevent execution of the original function or other plugins in the queue and also increase stack traces and affect performance. 

Around plugins were useful before Magento 2.2 release, since earlier that version "after" plugins were not allowed to receive the input function arguments. Around plugin was mainly used to modify the result based on the input arguments. 

The name of the function consists of the world "around" plus the original function name with the capital first letter, for example: around + Save = aroundSave.

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.

The around function returns a new result.

Important!

If the $proceed() function is not executed, then the standard code, as well as the lower priority plugins, will not be enabled. 

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

Remember that Magento 2 plugins cannot be applied to:

- final methods;
- final classes;
- non-public methods;
- static methods;
- constructor;
- virtual types;
- objects that are initialized before plugins loading;

 

If you want to learn more about the plugins sort order read how to  prioritize them.