Add same plugin to all public methods of a type
-
01-10-2020 - |
Question
Is there any chance to add a plugin to all public methods of a type without writing a separate method for all of them?
I would like to use that to log method calls. The original method is always called with unmodified parameters.
As it is intended for development/debugging, any dirty hack is appreciated.
Solution 2
Here's a dirty hack I came up with: The generated interceptor classes contain all methods of the original class, if there are plugins or not. In every method, PluginList::getNext()
is called to get the first plugin.
Unfortunately I cannot create a plugin for the plugin list itself because it will run into infinite recursion during initialization.
But with a one line edit in the generated interceptor file, I could replace the PluginList. This is added at the end of the constructor:
$this->pluginList = new PluginListDecorator($this->pluginList, $this->_logger);
Note: If you change generated files, be prepared to loose the changes any time! This is just a temporary addition!
And the decorator class looks as follows:
class PluginListDecorator implements PluginListInterface
{
/**
* @var PluginListInterface
*/
private $pluginList;
/**
* @var LoggerInterface
*/
private $logger;
public function __construct(PluginListInterface $pluginList, LoggerInterface $logger)
{
$this->pluginList = $pluginList;
$this->logger = $logger;
}
public function getNext($type, $method, $code = null)
{
$callee = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1];
if ($callee['class'] == Interceptor::class) {
$this->logger->info('METHOD CALLED: ' . $method);
}
return $this->pluginList->getNext($type, $method, $code);
}
public function getPlugin($type, $code)
{
return $this->pluginList->getPlugin($type, $code);
}
}
If getNext()
is called from the interceptor itself, I do my logging, then pass the call along to the original PluginList.
OTHER TIPS
I don't know if this works, but it's an idea.
The Interceptor
class is generated containing all the original class public methods and all interceptor methods look like this:
public function doSomething(parameters list here)
{
$pluginInfo = $this->pluginList->getNext($this->subjectType, 'doSomething');
if (!$pluginInfo) {
return parent::doSomething($object);
} else {
return $this->___callPlugins('doSomething', func_get_args(), $pluginInfo);
}
}
where $this->subjectType
is the name of the original class and $this->pluginList
is an implementation of Magento\Framework\Interception\PluginListInterface
(most probably Magento\Framework\Interception\PluginList\PluginList
).
Now you can try something sneaky.
(Here is where my idea may fail.)
Since $this->pluginList->getNext()
is a public method, it can be pluginized.
So create a (before or around) plugin for getNext
and if the value of the first parameter of getNext
(second parameter in your plugin) is your intended class name, then do your logging.