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.

有帮助吗?

解决方案 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.

其他提示

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.

许可以下: CC-BY-SA归因
scroll top