diff --git a/app/etc/di.xml b/app/etc/di.xml
index f0f27ea0e7d83..bf45058261fd2 100644
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -788,7 +788,7 @@
- \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceFactoryGenerator
- \Magento\Framework\ObjectManager\Code\Generator\Factory
- \Magento\Framework\ObjectManager\Code\Generator\Proxy
- - \Magento\Framework\Interception\Code\Generator\Interceptor
+ - Magento\Framework\Interception\Code\Generator\InterceptorInterface
- \Magento\Framework\ObjectManager\Profiler\Code\Generator\Logger
- \Magento\Framework\Api\Code\Generator\Mapper
- \Magento\Framework\ObjectManager\Code\Generator\Persistor
@@ -802,6 +802,21 @@
+
+
+ \Magento\Framework\CompiledInterception\Generator\FileCache
+ compiled_plugins
+ \Magento\Framework\CompiledInterception\Generator\NoSerialize
+
+
+
+
+ \Magento\Framework\CompiledInterception\PluginList\PluginList
+
+
+
+
@@ -1825,9 +1840,6 @@
type="Magento\Framework\Mail\MimeMessage" />
-
-
-
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Application.php b/dev/tests/integration/framework/Magento/TestFramework/Application.php
index 43bb7852e2441..14dcf4dd4b9a6 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Application.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Application.php
@@ -20,6 +20,7 @@
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyFields)
+ * phpcs:disable Magento2.Functions.DiscouragedFunction
*/
class Application
{
@@ -725,6 +726,7 @@ protected function getCustomDirs()
DirectoryList::STATIC_VIEW => [$path => "{$this->installDir}/pub/static"],
DirectoryList::TMP_MATERIALIZATION_DIR => [$path => "{$var}/view_preprocessed/pub/static"],
DirectoryList::GENERATED_CODE => [$path => "{$generated}/code"],
+ DirectoryList::STATIC_CACHE => [$path => "{$generated}/static_cache"],
DirectoryList::CACHE => [$path => "{$var}/cache"],
DirectoryList::LOG => [$path => "{$var}/log"],
DirectoryList::SESSION => [$path => "{$var}/session"],
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Interception/CompiledPluginList.php b/dev/tests/integration/framework/Magento/TestFramework/Interception/CompiledPluginList.php
new file mode 100644
index 0000000000000..178e58fcb4f77
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Interception/CompiledPluginList.php
@@ -0,0 +1,66 @@
+pluginList = $objectManager->create(
+ PluginList::class,
+ [
+ $objectManager->get(Dom::class),
+ $objectManager->get(ScopeInterface ::class),
+ $objectManager->get(FileCache ::class),
+ $objectManager->get(ObjectManagerRelationsRuntime ::class),
+ $objectManager->get(ConfigInterface::class),
+ $objectManager->get(InterceptionDefinitionRuntime ::class),
+ $objectManager,
+ $objectManager->get(ObjectManagerDefinitionRuntime ::class),
+ ['global'],
+ 'compiled_plugins',
+ $objectManager->get(NoSerialize ::class),
+ ]
+ );
+ parent::__construct($this->pluginList);
+ }
+
+ /**
+ * Reset internal cache
+ */
+ public function reset()
+ {
+ $this->pluginList->reset();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/CompiledInterceptorTest.php b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/CompiledInterceptorTest.php
new file mode 100644
index 0000000000000..c18f92b0d3afc
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/CompiledInterceptorTest.php
@@ -0,0 +1,182 @@
+ioGenerator = $this->getMockBuilder(Io::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->areaList = $this->getMockBuilder(AreaList::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ /**
+ * @return array
+ */
+ public function createScopeReaders()
+ {
+ $readerMap = include __DIR__ . '/_files/reader_mock_map.php';
+ $readerMock = $this->createMock(Dom::class);
+ $readerMock->method('read')->willReturnMap($readerMap);
+
+ $omMock = $this->createMock(ObjectManager::class);
+
+ $omConfigMock = $this->getMockForAbstractClass(
+ ConfigInterface::class
+ );
+
+ $omConfigMock->method('getOriginalInstanceType')->willReturnArgument(0);
+ $ret = [];
+ $objectManagerHelper = new ObjectManagerHelper($this);
+ $directoryList = ObjectManager::getInstance()->get(DirectoryList::class);
+ //clear static cache
+ $fileCache = new FileCache($directoryList);
+ $fileCache->clean();
+ foreach ($readerMap as $readerLine) {
+ $pluginList = ObjectManager::getInstance()->create(
+ PluginList::class,
+ [
+ 'objectManager' => $omMock,
+ 'configScope' => new StaticScope($readerLine[0]),
+ 'reader' => $readerMock,
+ 'omConfig' => $omConfigMock,
+ 'cache' => $fileCache,
+ 'cachePath' => false,
+ 'serializer' => new NoSerialize()
+ ]
+ );
+
+ $ret[$readerLine[0]] = $objectManagerHelper->getObject(
+ CompiledPluginList::class,
+ [
+ 'pluginList' => $pluginList
+ ]
+ );
+ }
+ return $ret;
+ }
+
+ /**
+ * Checks a test case when interceptor generates code for the specified class.
+ *
+ * @param string $className
+ * @param string $resultClassName
+ * @param string $fileName
+ * @dataProvider interceptorDataProvider
+ */
+ public function testGenerate($className, $resultClassName, $fileName)
+ {
+ $objectManagerHelper = new ObjectManagerHelper($this);
+ /** @var AreasPluginList $areaPlugins */
+ $areaPlugins = $objectManagerHelper->getObject(
+ AreasPluginList::class,
+ [
+ 'areaList' => $this->areaList,
+ 'scopeInterfaceFactory' => $objectManagerHelper->getObject(ScopeInterfaceFactory::class),
+ 'compiledPluginListFactory' => $objectManagerHelper->getObject(CompiledPluginListFactory::class),
+ 'plugins' => $this->createScopeReaders()
+ ]
+ );
+
+ /** @var CompiledInterceptor|MockObject $interceptor */
+ $interceptor = $this->getMockBuilder(CompiledInterceptor::class)
+ ->setMethods(['_validateData'])
+ ->setConstructorArgs(
+ [
+ $areaPlugins,
+ $className,
+ $resultClassName,
+ $this->ioGenerator,
+ null,
+ null
+ ]
+ )
+ ->getMock();
+
+ $this->ioGenerator->method('generateResultFileName')->with('\\' . $resultClassName)
+ ->willReturn($fileName . '.php');
+
+ $code = file_get_contents(__DIR__ . '/_out_interceptors/' . $fileName . '.txt');
+
+ $this->ioGenerator->method('writeResultFile')->with($fileName . '.php', $code);
+ $interceptor->method('_validateData')->willReturn(true);
+
+ $generated = $interceptor->generate();
+ $this->assertEquals($fileName . '.php', $generated, 'Generated interceptor is invalid.');
+ }
+
+ /**
+ * Gets list of interceptor samples.
+ *
+ * @return array
+ */
+ public function interceptorDataProvider()
+ {
+ return [
+ [
+ Item::class,
+ Item::class . '\Interceptor',
+ 'Item'
+ ],
+ [
+ ComplexItem::class,
+ ComplexItem::class . '\Interceptor',
+ 'ComplexItem'
+ ],
+ [
+ ComplexItemTyped::class,
+ ComplexItemTyped::class . '\Interceptor',
+ 'ComplexItemTyped'
+ ],
+ [
+ SecondItem::class,
+ SecondItem::class . '\Interceptor',
+ 'SecondItem'
+ ],
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/ComplexItem.php b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/ComplexItem.php
new file mode 100644
index 0000000000000..313ca3462a41b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/ComplexItem.php
@@ -0,0 +1,65 @@
+attribute;
+ }
+
+ /**
+ * @param $value
+ */
+ public function setValue($value)
+ {
+ $this->attribute = $value;
+ }
+
+ public function & getReference()
+ {
+ }
+
+ /**
+ * @param mixed ...$variadicValue
+ */
+ public function firstVariadicParameter(...$variadicValue)
+ {
+ $this->variadicAttribute = $variadicValue;
+ }
+
+ /**
+ * @param $value
+ * @param mixed ...$variadicValue
+ */
+ public function secondVariadicParameter($value, ...$variadicValue)
+ {
+ $this->attribute = $value;
+ $this->variadicAttribute = $variadicValue;
+ }
+
+ /**
+ * @param mixed ...$variadicValue
+ */
+ public function byRefVariadic(& ...$variadicValue)
+ {
+ $this->variadicAttribute = $variadicValue;
+ }
+
+ /**
+ *
+ */
+ public function returnsSelf()
+ {
+ return $this;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/ComplexItemTyped.php b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/ComplexItemTyped.php
new file mode 100644
index 0000000000000..35874d688fe31
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/ComplexItemTyped.php
@@ -0,0 +1,85 @@
+value;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setValue(string $value)
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * @param string ...$variadicValue
+ */
+ public function firstVariadicParameter(string ...$variadicValue)
+ {
+ $this->variadicValue = $variadicValue;
+ }
+
+ /**
+ * @param string $value
+ * @param string ...$variadicValue
+ */
+ public function secondVariadicParameter(string $value, string ...$variadicValue)
+ {
+ $this->value = $value;
+ $this->variadicValue = $variadicValue;
+ }
+
+ /**
+ * @param string ...$variadicValue
+ */
+ public function byRefVariadic(string & ...$variadicValue)
+ {
+ $this->variadicValue = $variadicValue;
+ }
+
+ /**
+ *
+ */
+ public function returnsSelf(): self
+ {
+ return $this;
+ }
+
+ /**
+ *
+ */
+ public function returnsType(): \Magento\Framework\Something
+ {
+ return null;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/Item.php b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/Item.php
new file mode 100644
index 0000000000000..71b11fce7c714
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/Custom/Module/Model/Item.php
@@ -0,0 +1,19 @@
+ [
+ 'plugins' => [
+ 'simple_plugin' => [
+ 'sortOrder' => 10,
+ 'instance' => Simple::class,
+ ],
+ ],
+ ],
+ ComplexItem::class => [
+ 'plugins' => [
+ 'advanced_plugin' => [
+ 'sortOrder' => 5,
+ 'instance' => Advanced::class,
+ ],
+ ],
+ ],
+ ],
+ ],
+ [
+ 'backend',
+ [
+ Item::class => [
+ 'plugins' => [
+ 'advanced_plugin' => [
+ 'sortOrder' => 5,
+ 'instance' => Advanced::class,
+ ],
+ ],
+ ],
+ ComplexItem::class => [
+ 'plugins' => [
+ 'complex_plugin' => [
+ 'sortOrder' => 15,
+ 'instance' => Complex::class,
+ ],
+ 'advanced_plugin' => [
+ 'sortOrder' => 5,
+ 'instance' => Advanced::class,
+ ],
+ ],
+ ],
+ ComplexItemTyped::class => [
+ 'plugins' => [
+ 'complex_plugin' => [
+ 'sortOrder' => 25,
+ 'instance' => Complex::class,
+ ],
+ 'advanced_plugin' => [
+ 'sortOrder' => 5,
+ 'instance' => Advanced::class,
+ ],
+ ],
+ ],
+ ]
+ ],
+ [
+ 'frontend',
+ [
+ Item::class => [
+ 'plugins' => ['simple_plugin' => ['disabled' => true]],
+ ],
+ Enhanced::class => [
+ 'plugins' => [
+ 'advanced_plugin' => [
+ 'sortOrder' => 5,
+ 'instance' => Advanced::class,
+ ],
+ ],
+ ],
+ 'SomeType' => [
+ 'plugins' => [
+ 'simple_plugin' => [
+ 'instance' => 'NonExistingPluginClass',
+ ],
+ ],
+ ],
+ 'typeWithoutInstance' => [
+ 'plugins' => [
+ 'simple_plugin' => [],
+ ],
+ ],
+ SecondItem::class => [
+ 'plugins' => [
+ 'simple_plugin1' => [
+ 'sortOrder' => 5,
+ 'instance' => Simple::class,
+ ],
+ 'advanced_plugin1' => [
+ 'sortOrder' => 5,
+ 'instance' => Advanced::class,
+ ],
+ 'advanced_plugin2' => [
+ 'sortOrder' => 10,
+ 'instance' => Advanced::class,
+ ],
+ 'simple_plugin2' => [
+ 'sortOrder' => 11,
+ 'instance' => Simple::class,
+ ],
+ 'simple_plugin3' => [
+ 'sortOrder' => 12,
+ 'instance' => Simple::class,
+ ],
+ 'advanced_plugin3' => [
+ 'sortOrder' => 15,
+ 'instance' => Advanced::class,
+ ],
+ 'advanced_plugin4' => [
+ 'sortOrder' => 25,
+ 'instance' => Advanced::class,
+ ],
+ ],
+ ]
+ ]
+ ],
+ [
+ 'emptyscope',
+ [
+
+ ]
+ ]
+];
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/ComplexItem.txt b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/ComplexItem.txt
new file mode 100644
index 0000000000000..dd0c9155e95f4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/ComplexItem.txt
@@ -0,0 +1,171 @@
+namespace Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ComplexItem;
+
+use Magento\Framework\Config\ScopeInterface;
+use Magento\Framework\ObjectManagerInterface;
+
+/**
+ * Interceptor class for @see \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ComplexItem
+ */
+class Interceptor extends \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ComplexItem
+{
+ /**
+ * @var ScopeInterface
+ */
+ private $____scope = null;
+
+ /**
+ * @var ObjectManagerInterface
+ */
+ private $____om = null;
+
+ /**
+ * @inheritdoc
+ */
+ public function __construct()
+ {
+ $this->____om = \Magento\Framework\App\ObjectManager::getInstance();
+ $this->____scope = \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Framework\Config\ScopeInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName()
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'global':
+ case 'frontend':
+ case 'emptyscope':
+ $this->____plugin_advanced_plugin()->beforeGetName($this);
+
+ $result = $this->____plugin_advanced_plugin()->aroundGetName($this, function(){
+ return parent::getName();
+ });
+
+ return $this->____plugin_advanced_plugin()->afterGetName($this, $result);
+ default:
+ $this->____plugin_advanced_plugin()->beforeGetName($this);
+
+ $result = $this->____plugin_advanced_plugin()->aroundGetName($this, function(){
+ $result = $this->____plugin_complex_plugin()->aroundGetName($this, function(){
+ return parent::getName();
+ });
+
+ return $this->____plugin_complex_plugin()->afterGetName($this, $result);
+ });
+
+ return $this->____plugin_advanced_plugin()->afterGetName($this, $result);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setValue($value)
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ $beforeResult = $this->____plugin_complex_plugin()->beforeSetValue($this, $value);
+ if ($beforeResult !== null) list($value) = (array)$beforeResult;
+
+ return $this->____plugin_complex_plugin()->aroundSetValue($this, function($value){
+ return parent::setValue($value);
+ }, $value);
+ default:
+ return parent::setValue($value);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function & getReference()
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ return $this->____plugin_complex_plugin()->aroundGetReference($this, function(){
+ return parent::getReference();
+ });
+ default:
+ return parent::getReference();
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function firstVariadicParameter(... $variadicValue)
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ return $this->____plugin_complex_plugin()->aroundFirstVariadicParameter($this, function($variadicValue){
+ return parent::firstVariadicParameter($variadicValue);
+ }, $variadicValue);
+ default:
+ return parent::firstVariadicParameter($variadicValue);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function secondVariadicParameter($value, ... $variadicValue)
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ return $this->____plugin_complex_plugin()->aroundSecondVariadicParameter($this, function($value, $variadicValue){
+ return parent::secondVariadicParameter($value, $variadicValue);
+ }, $value, $variadicValue);
+ default:
+ return parent::secondVariadicParameter($value, $variadicValue);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function byRefVariadic(&... $variadicValue)
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ return $this->____plugin_complex_plugin()->aroundByRefVariadic($this, function(&$variadicValue){
+ return parent::byRefVariadic($variadicValue);
+ }, $variadicValue);
+ default:
+ return parent::byRefVariadic($variadicValue);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function returnsSelf()
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ $this->____plugin_complex_plugin()->beforeReturnsSelf($this);
+
+ return parent::returnsSelf();
+ default:
+ return parent::returnsSelf();
+ }
+ }
+
+ /**
+ * plugin "advanced_plugin"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced
+ */
+ private function ____plugin_advanced_plugin()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced::class);
+ }
+
+ /**
+ * plugin "complex_plugin"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Complex
+ */
+ private function ____plugin_complex_plugin()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Complex::class);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/ComplexItemTyped.txt b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/ComplexItemTyped.txt
new file mode 100644
index 0000000000000..2ba7c318be025
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/ComplexItemTyped.txt
@@ -0,0 +1,195 @@
+namespace Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ComplexItemTyped;
+
+use Magento\Framework\Config\ScopeInterface;
+use Magento\Framework\ObjectManagerInterface;
+
+/**
+ * Interceptor class for @see \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ComplexItemTyped
+ */
+class Interceptor extends \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ComplexItemTyped
+{
+ /**
+ * @var ScopeInterface
+ */
+ private $____scope = null;
+
+ /**
+ * @var ObjectManagerInterface
+ */
+ private $____om = null;
+
+ /**
+ * @inheritdoc
+ */
+ public function __construct()
+ {
+ $this->____om = \Magento\Framework\App\ObjectManager::getInstance();
+ $this->____scope = \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Framework\Config\ScopeInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function returnVoid() : void
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ parent::returnVoid();
+
+ $this->____plugin_complex_plugin()->afterReturnVoid($this, null);
+ break;
+ default:
+ parent::returnVoid();
+ break;
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getNullableValue() : ?string
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ $result = parent::getNullableValue();
+
+ return $this->____plugin_complex_plugin()->afterGetNullableValue($this, $result);
+ default:
+ return parent::getNullableValue();
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName() : string
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ $this->____plugin_advanced_plugin()->beforeGetName($this);
+
+ $result = $this->____plugin_advanced_plugin()->aroundGetName($this, function(){
+ $result = $this->____plugin_complex_plugin()->aroundGetName($this, function(){
+ return parent::getName();
+ });
+
+ return $this->____plugin_complex_plugin()->afterGetName($this, $result);
+ });
+
+ return $this->____plugin_advanced_plugin()->afterGetName($this, $result);
+ default:
+ return parent::getName();
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setValue(string $value)
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ $beforeResult = $this->____plugin_complex_plugin()->beforeSetValue($this, $value);
+ if ($beforeResult !== null) list($value) = (array)$beforeResult;
+
+ return $this->____plugin_complex_plugin()->aroundSetValue($this, function($value){
+ return parent::setValue($value);
+ }, $value);
+ default:
+ return parent::setValue($value);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function firstVariadicParameter(string ... $variadicValue)
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ return $this->____plugin_complex_plugin()->aroundFirstVariadicParameter($this, function($variadicValue){
+ return parent::firstVariadicParameter($variadicValue);
+ }, $variadicValue);
+ default:
+ return parent::firstVariadicParameter($variadicValue);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function secondVariadicParameter(string $value, string ... $variadicValue)
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ return $this->____plugin_complex_plugin()->aroundSecondVariadicParameter($this, function($value, $variadicValue){
+ return parent::secondVariadicParameter($value, $variadicValue);
+ }, $value, $variadicValue);
+ default:
+ return parent::secondVariadicParameter($value, $variadicValue);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function byRefVariadic(string &... $variadicValue)
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ return $this->____plugin_complex_plugin()->aroundByRefVariadic($this, function(&$variadicValue){
+ return parent::byRefVariadic($variadicValue);
+ }, $variadicValue);
+ default:
+ return parent::byRefVariadic($variadicValue);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function returnsSelf() : \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ComplexItemTyped
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ $this->____plugin_complex_plugin()->beforeReturnsSelf($this);
+
+ return parent::returnsSelf();
+ default:
+ return parent::returnsSelf();
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function returnsType() : \Magento\Framework\Something
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'backend':
+ $this->____plugin_complex_plugin()->beforeReturnsType($this);
+
+ return parent::returnsType();
+ default:
+ return parent::returnsType();
+ }
+ }
+
+ /**
+ * plugin "complex_plugin"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Complex
+ */
+ private function ____plugin_complex_plugin()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Complex::class);
+ }
+
+ /**
+ * plugin "advanced_plugin"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced
+ */
+ private function ____plugin_advanced_plugin()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced::class);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/Item.txt b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/Item.txt
new file mode 100644
index 0000000000000..b14d36d5f0e23
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/Item.txt
@@ -0,0 +1,71 @@
+namespace Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\Item;
+
+use Magento\Framework\Config\ScopeInterface;
+use Magento\Framework\ObjectManagerInterface;
+
+/**
+ * Interceptor class for @see \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\Item
+ */
+class Interceptor extends \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\Item
+{
+ /**
+ * @var ScopeInterface
+ */
+ private $____scope = null;
+
+ /**
+ * @var ObjectManagerInterface
+ */
+ private $____om = null;
+
+ /**
+ * @inheritdoc
+ */
+ public function __construct()
+ {
+ $this->____om = \Magento\Framework\App\ObjectManager::getInstance();
+ $this->____scope = \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Framework\Config\ScopeInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName()
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'global':
+ case 'emptyscope':
+ $result = parent::getName();
+
+ return $this->____plugin_simple_plugin()->afterGetName($this, $result);
+ default:
+ $this->____plugin_advanced_plugin()->beforeGetName($this);
+
+ $result = $this->____plugin_advanced_plugin()->aroundGetName($this, function(){
+ $result = parent::getName();
+
+ return $this->____plugin_simple_plugin()->afterGetName($this, $result);
+ });
+
+ return $this->____plugin_advanced_plugin()->afterGetName($this, $result);
+ }
+ }
+
+ /**
+ * plugin "simple_plugin"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Simple
+ */
+ private function ____plugin_simple_plugin()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Simple::class);
+ }
+
+ /**
+ * plugin "advanced_plugin"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced
+ */
+ private function ____plugin_advanced_plugin()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced::class);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/SecondItem.txt b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/SecondItem.txt
new file mode 100644
index 0000000000000..4df8943d0974a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledInterceptor/_out_interceptors/SecondItem.txt
@@ -0,0 +1,135 @@
+namespace Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\SecondItem;
+
+use Magento\Framework\Config\ScopeInterface;
+use Magento\Framework\ObjectManagerInterface;
+
+/**
+ * Interceptor class for @see \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\SecondItem
+ */
+class Interceptor extends \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\SecondItem
+{
+ /**
+ * @var ScopeInterface
+ */
+ private $____scope = null;
+
+ /**
+ * @var ObjectManagerInterface
+ */
+ private $____om = null;
+
+ /**
+ * @inheritdoc
+ */
+ public function __construct()
+ {
+ $this->____om = \Magento\Framework\App\ObjectManager::getInstance();
+ $this->____scope = \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Framework\Config\ScopeInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName()
+ {
+ switch ($this->____scope->getCurrentScope()) {
+ case 'frontend':
+ $this->____plugin_advanced_plugin1()->beforeGetName($this);
+
+ $result = $this->____plugin_advanced_plugin1()->aroundGetName($this, function(){
+ $this->____plugin_advanced_plugin2()->beforeGetName($this);
+
+ $result = $this->____plugin_advanced_plugin2()->aroundGetName($this, function(){
+ $this->____plugin_advanced_plugin3()->beforeGetName($this);
+
+ $result = $this->____plugin_advanced_plugin3()->aroundGetName($this, function(){
+ $this->____plugin_advanced_plugin4()->beforeGetName($this);
+
+ $result = $this->____plugin_advanced_plugin4()->aroundGetName($this, function(){
+ return parent::getName();
+ });
+
+ return $this->____plugin_advanced_plugin4()->afterGetName($this, $result);
+ });
+
+ $result = $this->____plugin_simple_plugin2()->afterGetName($this, $result);
+
+ $result = $this->____plugin_simple_plugin3()->afterGetName($this, $result);
+
+ return $this->____plugin_advanced_plugin3()->afterGetName($this, $result);
+ });
+
+ return $this->____plugin_advanced_plugin2()->afterGetName($this, $result);
+ });
+
+ $result = $this->____plugin_simple_plugin1()->afterGetName($this, $result);
+
+ return $this->____plugin_advanced_plugin1()->afterGetName($this, $result);
+ default:
+ return parent::getName();
+ }
+ }
+
+ /**
+ * plugin "advanced_plugin1"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced
+ */
+ private function ____plugin_advanced_plugin1()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced::class);
+ }
+
+ /**
+ * plugin "simple_plugin1"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Simple
+ */
+ private function ____plugin_simple_plugin1()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Simple::class);
+ }
+
+ /**
+ * plugin "advanced_plugin2"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced
+ */
+ private function ____plugin_advanced_plugin2()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced::class);
+ }
+
+ /**
+ * plugin "advanced_plugin3"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced
+ */
+ private function ____plugin_advanced_plugin3()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced::class);
+ }
+
+ /**
+ * plugin "simple_plugin2"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Simple
+ */
+ private function ____plugin_simple_plugin2()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Simple::class);
+ }
+
+ /**
+ * plugin "simple_plugin3"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Simple
+ */
+ private function ____plugin_simple_plugin3()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Simple::class);
+ }
+
+ /**
+ * plugin "advanced_plugin4"
+ * @return \Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced
+ */
+ private function ____plugin_advanced_plugin4()
+ {
+ return $this->____om->get(\Magento\Framework\CompiledInterception\CompiledInterceptor\Custom\Module\Model\ItemPlugin\Advanced::class);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledPluginList/CompiledPluginListTest.php b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledPluginList/CompiledPluginListTest.php
new file mode 100644
index 0000000000000..82367bdd99952
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledPluginList/CompiledPluginListTest.php
@@ -0,0 +1,184 @@
+objects = $this->createScopeReaders();
+ }
+
+ public function createScopeReaders()
+ {
+ $readerMap = include __DIR__ . '/_files/reader_mock_map.php';
+ $readerMock = $this->createMock(Dom::class);
+ $readerMock->method('read')->willReturnMap($readerMap);
+
+ $omMock = $this->createMock(ObjectManager::class);
+ $omMock->method('get')->with(LoggerInterface::class)->willReturn(new NullLogger());
+
+ $omConfigMock = $this->getMockForAbstractClass(
+ ConfigInterface::class
+ );
+
+ $omConfigMock->method('getOriginalInstanceType')->willReturnArgument(0);
+ $ret = [];
+ $objectManagerHelper = new ObjectManagerHelper($this);
+ $directoryList = ObjectManager::getInstance()->get(DirectoryList::class);
+ //clear static cache
+ $fileCache = new FileCache($directoryList);
+ $fileCache->clean();
+ foreach ($readerMap as $readerLine) {
+ $pluginList = ObjectManager::getInstance()->create(
+ PluginList::class,
+ [
+ 'objectManager' => $omMock,
+ 'configScope' => new StaticScope($readerLine[0]),
+ 'reader' => $readerMock,
+ 'omConfig' => $omConfigMock,
+ 'cache' => $fileCache,
+ 'cachePath' => false,
+ 'serializer' => new NoSerialize()
+ ]
+ );
+ $ret[$readerLine[0]] = $objectManagerHelper->getObject(
+ CompiledPluginList::class,
+ [
+ 'pluginList' => $pluginList,
+ ]
+ );
+ }
+ return $ret;
+ }
+
+ public function testGetPlugin()
+ {
+ $this->objects['backend']->getNext(Item::class, 'getName');
+ $this->assertEquals(
+ Simple::class,
+ $this->objects['backend']->getPluginType(
+ Item::class,
+ 'simple_plugin'
+ )
+ );
+ $this->assertEquals(
+ Advanced::class,
+ $this->objects['backend']->getPluginType(
+ Item::class,
+ 'advanced_plugin'
+ )
+ );
+ }
+
+ /**
+ * @param $expectedResult
+ * @param $type
+ * @param $method
+ * @param $scopeCode
+ * @param string $code
+ * @dataProvider getPluginsDataProvider
+ */
+ public function testGetPlugins($expectedResult, $type, $method, $scopeCode, $code = '__self')
+ {
+ $this->assertEquals($expectedResult, $this->objects[$scopeCode]->getNext($type, $method, $code));
+ }
+
+ /**
+ * @return array
+ */
+ public function getPluginsDataProvider()
+ {
+ return [
+ [
+ [4 => ['simple_plugin']], Item::class,
+ 'getName',
+ 'global',
+ ],
+ [
+ // advanced plugin has lower sort order
+ [2 => 'advanced_plugin', 4 => ['advanced_plugin'], 1 => ['advanced_plugin']],
+ Item::class,
+ 'getName',
+ 'backend'
+ ],
+ [
+ // advanced plugin has lower sort order
+ [4 => ['simple_plugin']],
+ Item::class,
+ 'getName',
+ 'backend',
+ 'advanced_plugin'
+ ],
+ // simple plugin is disabled in configuration for
+ // \Magento\Framework\CompiledInterception\CompiledPluginList\Custom\Module\Model\Item
+ // in frontend
+ [null, Item::class, 'getName', 'frontend'],
+ // test plugin inheritance
+ [
+ [4 => ['simple_plugin']],
+ Enhanced::class,
+ 'getName',
+ 'global'
+ ],
+ [
+ // simple plugin is disabled in configuration for parent
+ [2 => 'advanced_plugin', 4 => ['advanced_plugin'], 1 => ['advanced_plugin']],
+ Enhanced::class,
+ 'getName',
+ 'frontend'
+ ]
+ ];
+ }
+
+ /**
+ * @covers \Magento\Framework\Interception\PluginList\PluginList::getNext
+ * @covers \Magento\Framework\Interception\PluginList\PluginList::_inheritPlugins
+ */
+ public function testInheritPluginsWithNonExistingClass()
+ {
+ $this->objects['frontend']->getNext('SomeType', 'someMethod');
+ $this->expectException(InvalidArgumentException::class);
+ }
+
+ /**
+ * @covers \Magento\Framework\Interception\PluginList\PluginList::getNext
+ * @covers \Magento\Framework\Interception\PluginList\PluginList::_inheritPlugins
+ */
+ public function testInheritPluginsWithNotExistingPlugin()
+ {
+ $this->assertNull($this->objects['frontend']->getNext('typeWithoutInstance', 'someMethod'));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledPluginList/Custom/Module/Model/Item.php b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledPluginList/Custom/Module/Model/Item.php
new file mode 100644
index 0000000000000..2a74773067307
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/CompiledInterception/CompiledPluginList/Custom/Module/Model/Item.php
@@ -0,0 +1,22 @@
+ [
+ 'plugins' => [
+ 'simple_plugin' => [
+ 'sortOrder' => 10,
+ 'instance' => Simple::class,
+ ],
+ ],
+ ],
+ ],
+ ],
+ [
+ 'backend',
+ [
+ Item::class => [
+ 'plugins' => [
+ 'advanced_plugin' => [
+ 'sortOrder' => 5,
+ 'instance' => Advanced::class,
+ ],
+ ],
+ ],
+ ]
+ ],
+ [
+ 'frontend',
+ [
+ Item::class => [
+ 'plugins' => ['simple_plugin' => ['disabled' => true]],
+ ],
+ Enhanced::class => [
+ 'plugins' => [
+ 'advanced_plugin' => [
+ 'sortOrder' => 5,
+ 'instance' => Advanced::class,
+ ],
+ ],
+ ],
+ 'SomeType' => [
+ 'plugins' => [
+ 'simple_plugin' => [
+ 'instance' => 'NonExistingPluginClass',
+ ],
+ ],
+ ],
+ 'typeWithoutInstance' => [
+ 'plugins' => [
+ 'simple_plugin' => [],
+ ],
+ ]
+ ]
+ ],
+ [
+ 'emptyscope',
+ [
+
+ ]
+ ]
+];
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Interception/AbstractPlugin.php b/dev/tests/integration/testsuite/Magento/Framework/Interception/AbstractPlugin.php
index b9deeb3bb968f..fad44b5999dab 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Interception/AbstractPlugin.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Interception/AbstractPlugin.php
@@ -5,6 +5,8 @@
*/
namespace Magento\Framework\Interception;
+use Magento\Framework\Code\Generator\Autoloader;
+use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
/**
@@ -54,6 +56,7 @@ protected function setUp(): void
protected function tearDown(): void
{
\Magento\Framework\App\ObjectManager::setInstance($this->applicationObjectManager);
+ $this->injectObjectManager($this->applicationObjectManager);
}
/**
@@ -128,10 +131,35 @@ public function setUpInterceptionConfig($pluginConfig)
'preferences' => [
\Magento\Framework\Interception\PluginListInterface::class =>
\Magento\Framework\Interception\PluginList\PluginList::class,
+ \Magento\Framework\Interception\ChainInterface::class =>
+ \Magento\Framework\Interception\Chain\Chain::class,
+ \Magento\Framework\Interception\Code\Generator\InterceptorInterface::class =>
+ \Magento\Framework\Interception\Code\Generator\Interceptor::class,
\Magento\Framework\Interception\ConfigWriterInterface::class =>
\Magento\Framework\Interception\PluginListGenerator::class
],
]
);
+
+ $this->injectObjectManager($this->_objectManager);
+ }
+
+ /**
+ * Inject object manager into autoloader.
+ *
+ * @param ObjectManagerInterface $objectManager
+ * @throws \ReflectionException
+ */
+ private function injectObjectManager(ObjectManagerInterface $objectManager): void
+ {
+ foreach (spl_autoload_functions() as $autoloader) {
+ if (is_array($autoloader) && $autoloader[0] instanceof Autoloader) {
+ $autoloaderReflection = new \ReflectionClass($autoloader[0]);
+ $generatorProperty = $autoloaderReflection->getProperty('_generator');
+ $generatorProperty->setAccessible(true);
+ $generatorProperty->getValue($autoloader[0])->setObjectManager($objectManager);
+ break;
+ }
+ }
}
}
diff --git a/lib/internal/Magento/Framework/App/Filesystem/DirectoryList.php b/lib/internal/Magento/Framework/App/Filesystem/DirectoryList.php
index ce150b00a6665..6768c29e8ac29 100644
--- a/lib/internal/Magento/Framework/App/Filesystem/DirectoryList.php
+++ b/lib/internal/Magento/Framework/App/Filesystem/DirectoryList.php
@@ -12,6 +12,11 @@
*/
class DirectoryList extends \Magento\Framework\Filesystem\DirectoryList
{
+ /**
+ * Path for compiled interceptors static cache directory.
+ */
+ public const STATIC_CACHE = 'static_cache';
+
/**
* Code base root
*/
@@ -176,6 +181,7 @@ public static function getDefaultConfig()
self::GENERATED => [parent::PATH => 'generated'],
self::GENERATED_CODE => [parent::PATH => Io::DEFAULT_DIRECTORY],
self::GENERATED_METADATA => [parent::PATH => 'generated/metadata'],
+ self::STATIC_CACHE => [parent::PATH => 'generated/static_cache'],
self::VAR_IMPORT_EXPORT => [parent::PATH => 'var', parent::URL_PATH => 'import_export'],
];
return parent::getDefaultConfig() + $result;
diff --git a/lib/internal/Magento/Framework/App/State/CleanupFiles.php b/lib/internal/Magento/Framework/App/State/CleanupFiles.php
index c95caf8310b77..bd4988f55dc51 100644
--- a/lib/internal/Magento/Framework/App/State/CleanupFiles.php
+++ b/lib/internal/Magento/Framework/App/State/CleanupFiles.php
@@ -54,7 +54,8 @@ public function clearCodeGeneratedClasses()
{
return array_merge(
$this->emptyDir(DirectoryList::GENERATED_CODE),
- $this->emptyDir(DirectoryList::GENERATED_METADATA)
+ $this->emptyDir(DirectoryList::GENERATED_METADATA),
+ $this->emptyDir(DirectoryList::STATIC_CACHE)
);
}
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/State/CleanupFilesTest.php b/lib/internal/Magento/Framework/App/Test/Unit/State/CleanupFilesTest.php
index d55ca62407c33..dc53e15417716 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/State/CleanupFilesTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/State/CleanupFilesTest.php
@@ -38,12 +38,14 @@ public function testClearCodeGeneratedClasses()
{
$dir1 = $this->getDirectoryCleanMock();
$dir2 = $this->getDirectoryCleanMock();
- $this->filesystem->expects($this->exactly(2))
+ $dir3 = $this->getDirectoryCleanMock();
+ $this->filesystem->expects($this->exactly(3))
->method('getDirectoryWrite')
->willReturnMap(
[
[DirectoryList::GENERATED_CODE, DriverPool::FILE, $dir1],
[DirectoryList::GENERATED_METADATA, DriverPool::FILE, $dir2],
+ [DirectoryList::STATIC_CACHE, DriverPool::FILE, $dir3],
]
);
$this->object->clearCodeGeneratedClasses();
diff --git a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php
index 35c138147e9d3..8a57710afc700 100644
--- a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php
+++ b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php
@@ -89,6 +89,7 @@ private function tryToLogException(\Exception $exception): void
try {
$logger = ObjectManager::getInstance()->get(LoggerInterface::class);
$logger->debug($exception->getMessage(), ['exception' => $exception]);
+ // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock
} catch (\Exception $ignoreThisException) {
// Do not take an action here, since the original exception might have been caused by logger
}
diff --git a/lib/internal/Magento/Framework/CompiledInterception/Generator/AreasPluginList.php b/lib/internal/Magento/Framework/CompiledInterception/Generator/AreasPluginList.php
new file mode 100644
index 0000000000000..1995ed4965224
--- /dev/null
+++ b/lib/internal/Magento/Framework/CompiledInterception/Generator/AreasPluginList.php
@@ -0,0 +1,87 @@
+scope = $scope;
+ $this->staticScopeFactory = $staticScopeFactory;
+ $this->compiledPluginListFactory = $compiledPluginListFactory;
+ $this->plugins = $plugins;
+ }
+
+ /**
+ * Get array of plugins config indexed by scope code
+ *
+ * @return array
+ */
+ public function getPluginsConfigForAllAreas()
+ {
+ if ($this->plugins === null) {
+ $this->plugins = [];
+ //this is to emulate order M2 is reading scopes config to use scope cache
+ //"global|primary" should be loaded first and then "global|primary|frontend" etc.
+ $defaultScopePluginList = $defaultScope = null;
+ foreach ($this->scope->getAllScopes() as $scope) {
+ $configScope = $this->staticScopeFactory->create(
+ [
+ 'scope' => $scope,
+ ]
+ );
+ if ($defaultScopePluginList === null) {
+ $defaultScopePluginList = $this->compiledPluginListFactory->create();
+ $defaultScopePluginList->setScope($configScope);
+ $defaultScopePluginList->getNext('dummy', 'dummy');
+ $defaultScope = $scope;
+ } else {
+ $this->plugins[$scope] = clone $defaultScopePluginList;
+ $this->plugins[$scope]->setScope($configScope);
+ }
+ }
+ $this->plugins[$defaultScope] = $defaultScopePluginList;
+ }
+
+ return $this->plugins;
+ }
+}
diff --git a/lib/internal/Magento/Framework/CompiledInterception/Generator/CompiledInterceptor.php b/lib/internal/Magento/Framework/CompiledInterception/Generator/CompiledInterceptor.php
new file mode 100644
index 0000000000000..d69275fc39c1d
--- /dev/null
+++ b/lib/internal/Magento/Framework/CompiledInterception/Generator/CompiledInterceptor.php
@@ -0,0 +1,702 @@
+areasPlugins = $areasPlugins;
+ }
+
+ /**
+ * Unused function required by production mode interface
+ *
+ * @param mixed $interceptedMethods
+ */
+ public function setInterceptedMethods($interceptedMethods)
+ {
+ // this is not used as methods are read from reflection
+ $this->interceptedMethods = $interceptedMethods;
+ }
+
+ /**
+ * Get properties to be set in constructor.
+ *
+ * @return array
+ */
+ public static function propertiesToSetInConstructor()
+ {
+ return [
+ ScopeInterface::class => '____scope',
+ ObjectManagerInterface::class => '____om',
+ ];
+ }
+
+ /**
+ * Get all class methods
+ *
+ * @return array|null
+ * @throws \ReflectionException
+ */
+ protected function _getClassMethods()
+ {
+ $this->generateMethodsAndProperties();
+ return $this->classMethods;
+ }
+
+ /**
+ * Get all class properties
+ *
+ * @return array|null
+ * @throws \ReflectionException
+ */
+ protected function _getClassProperties()
+ {
+ $this->generateMethodsAndProperties();
+ return $this->classProperties;
+ }
+
+ /**
+ * Get default constructor definition for generated class
+ *
+ * @return array
+ * @throws \ReflectionException
+ */
+ protected function _getDefaultConstructorDefinition()
+ {
+ return $this->injectPropertiesSettersToConstructor(
+ $this->getSourceClassReflection()->getConstructor(),
+ static::propertiesToSetInConstructor()
+ );
+ }
+
+ /**
+ * Generate class source
+ *
+ * @return bool|string
+ * @throws \ReflectionException
+ */
+ protected function _generateCode()
+ {
+ if ($this->getSourceClassReflection()->isInterface()) {
+ return false;
+ } else {
+ $this->_classGenerator->setExtendedClass($this->getSourceClassName());
+ }
+ $this->generateMethodsAndProperties();
+ return parent::_generateCode();
+ }
+
+ /**
+ * Generate all methods and properties
+ *
+ * @throws \ReflectionException
+ */
+ private function generateMethodsAndProperties()
+ {
+ if ($this->classMethods === null) {
+ $this->classMethods = [];
+ $this->classProperties = [];
+
+ foreach (static::propertiesToSetInConstructor() as $type => $name) {
+ $this->_classGenerator->addUse($type);
+ $this->classProperties[] = [
+ 'name' => $name,
+ 'visibility' => 'private',
+ 'docblock' => [
+ 'tags' => [['name' => 'var', 'description' => substr(strrchr($type, "\\"), 1)]],
+ ]
+ ];
+ }
+ $this->classMethods[] = $this->_getDefaultConstructorDefinition();
+ $this->overrideMethodsAndGeneratePluginGetters($this->getSourceClassReflection());
+ }
+ }
+
+ /**
+ * Get reflection of source class
+ *
+ * @return \ReflectionClass
+ * @throws \ReflectionException
+ */
+ private function getSourceClassReflection()
+ {
+ if ($this->baseReflection === null) {
+ $this->baseReflection = new \ReflectionClass($this->getSourceClassName());
+ }
+ return $this->baseReflection;
+ }
+
+ /**
+ * Whether method is intercepted
+ *
+ * @param \ReflectionMethod $method
+ * @return bool
+ */
+ private function isInterceptedMethod(\ReflectionMethod $method)
+ {
+ return !($method->isConstructor() || $method->isFinal() || $method->isStatic() || $method->isDestructor()) &&
+ !in_array($method->getName(), ['__sleep', '__wakeup', '__clone']);
+ }
+
+ /**
+ * Generate compiled methods and plugin getters
+ *
+ * @param \ReflectionClass $reflection
+ */
+ private function overrideMethodsAndGeneratePluginGetters(\ReflectionClass $reflection)
+ {
+ $publicMethods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
+
+ $allPlugins = [];
+ foreach ($publicMethods as $method) {
+ if ($this->isInterceptedMethod($method)) {
+ $config = $this->getPluginsConfig($method, $allPlugins);
+ if (!empty($config)) {
+ $this->classMethods[] = $this->getCompiledMethodInfo($method, $config);
+ }
+ }
+ }
+ foreach ($allPlugins as $plugins) {
+ foreach ($plugins as $plugin) {
+ $this->classMethods[] = $this->getPluginGetterInfo($plugin);
+ }
+ }
+ }
+
+ /**
+ * Generate class constructor adding required properties when types are not present in parent constructor
+ *
+ * @param \ReflectionMethod|null $parentConstructor
+ * @param array $properties
+ * @return array
+ */
+ private function injectPropertiesSettersToConstructor(\ReflectionMethod $parentConstructor = null, $properties = [])
+ {
+ if ($parentConstructor == null) {
+ $parameters = [];
+ $body = [];
+ } else {
+ $parameters = $parentConstructor->getParameters();
+ $parentCallParams = [];
+ foreach ($parameters as $parameter) {
+ $parentCallParams[] = '$' . $parameter->getName();
+ }
+ $body = ["parent::__construct(" . implode(', ', $parentCallParams) . ");"];
+ }
+ $extraSetters = array_fill_keys(array_values($properties), null);
+ foreach ($parameters as $parameter) {
+ if ($parameter->getType()) {
+ $type = $parameter->getType()->getName();
+ if (isset($properties[$type])) {
+ $extraSetters[$properties[$type]] = $parameter->getName();
+ }
+ }
+ }
+ $parameters = array_map([$this, '_getMethodParameterInfo'], $parameters);
+ foreach ($extraSetters as $name => $paramName) {
+ $class = array_search($name, $properties);
+ if ($paramName !== null) {
+ array_unshift(
+ $body,
+ "\$this->$name = \$$paramName;"
+ );
+ } elseif ($class === ObjectManagerInterface::class) {
+ array_unshift(
+ $body,
+ "\$this->$name = \Magento\Framework\App\ObjectManager::getInstance();"
+ );
+ } else {
+ array_unshift(
+ $body,
+ "\$this->$name = \Magento\Framework\App\ObjectManager::getInstance()->get(\\$class::class);"
+ );
+ }
+ }
+ return [
+ 'name' => '__construct',
+ 'parameters' => $parameters,
+ 'body' => implode("\n", $body),
+ 'docblock' => ['shortDescription' => '@inheritdoc'],
+ ];
+ }
+
+ /**
+ * Adds tabulation to nested code block
+ *
+ * @param array $body
+ * @param array $sub
+ * @param int $indent
+ */
+ private function addCodeSubBlock(&$body, $sub, $indent = 1)
+ {
+ foreach ($sub as $line) {
+ $body[] = ('' === $line) ? '' : str_repeat("\t", $indent) . $line;
+ }
+ }
+
+ /**
+ * Generate source of before plugins
+ *
+ * @param array $plugins
+ * @param string $methodName
+ * @param string $extraParams
+ * @param string $parametersList
+ * @return array
+ */
+ private function compileBeforePlugins($plugins, $methodName, $extraParams, $parametersList)
+ {
+ $lines = [];
+ foreach ($plugins as $plugin) {
+ $call = "\$this->" . $this->getGetterName($plugin) . "()->$methodName(\$this$extraParams);";
+
+ if (!empty($parametersList)) {
+ $lines[] = "\$beforeResult = " . $call;
+ $lines[] = "if (\$beforeResult !== null) list({$parametersList}) = (array)\$beforeResult;";
+ } else {
+ $lines[] = $call;
+ }
+ $lines[] = "";
+ }
+ return $lines;
+ }
+
+ /**
+ * Generate source of around plugin
+ *
+ * @param string $methodName
+ * @param array $plugin
+ * @param string $capitalizedName
+ * @param string $extraParams
+ * @param array $parameters
+ * @param bool $returnVoid
+ * @return array
+ */
+ private function compileAroundPlugin($methodName, $plugin, $capitalizedName, $extraParams, $parameters, $returnVoid)
+ {
+ $lines = [];
+ $lines[] = "\$this->{$this->getGetterName($plugin)}()->around$capitalizedName" .
+ "(\$this, function({$this->getParameterListForNextCallback($parameters)}){";
+ $this->addCodeSubBlock(
+ $lines,
+ $this->getMethodSourceFromConfig($methodName, $plugin['next'] ?: [], $parameters, $returnVoid)
+ );
+ $lines[] = "}$extraParams);";
+ return $lines;
+ }
+
+ /**
+ * Generate source of after plugins
+ *
+ * @param array $plugins
+ * @param string $methodName
+ * @param string $extraParams
+ * @param bool $returnVoid
+ * @return array
+ */
+ private function compileAfterPlugins($plugins, $methodName, $extraParams, $returnVoid)
+ {
+ $lines = [];
+ foreach ($plugins as $plugin) {
+ $call = "\$this->" . $this->getGetterName($plugin) . "()->$methodName(\$this, ";
+
+ if (!$returnVoid) {
+ $lines[] = ["$call\$result$extraParams);"];
+ } else {
+ $lines[] = ["{$call}null$extraParams);"];
+ }
+ }
+ return $lines;
+ }
+
+ /**
+ * Generate interceptor source using config
+ *
+ * @param string $methodName
+ * @param array $conf
+ * @param array $parameters
+ * @param bool $returnVoid
+ * @return array
+ */
+ private function getMethodSourceFromConfig($methodName, $conf, $parameters, $returnVoid)
+ {
+ $capitalizedName = ucfirst($methodName);
+ $parametersList = $this->getParameterList($parameters);
+ $extraParams = empty($parameters) ? '' : (', ' . $parametersList);
+
+ if (isset($conf[DefinitionInterface::LISTENER_BEFORE])) {
+ $body = $this->compileBeforePlugins(
+ $conf[DefinitionInterface::LISTENER_BEFORE],
+ 'before' . $capitalizedName,
+ $extraParams,
+ $parametersList
+ );
+ } else {
+ $body = [];
+ }
+
+ $resultChain = [];
+ if (isset($conf[DefinitionInterface::LISTENER_AROUND])) {
+ $resultChain[] = $this->compileAroundPlugin(
+ $methodName,
+ $conf[DefinitionInterface::LISTENER_AROUND],
+ $capitalizedName,
+ $extraParams,
+ $parameters,
+ $returnVoid
+ );
+ } else {
+ $resultChain[] = ["parent::{$methodName}({$this->getParameterList($parameters)});"];
+ }
+
+ if (isset($conf[DefinitionInterface::LISTENER_AFTER])) {
+ $resultChain = array_merge(
+ $resultChain,
+ $this->compileAfterPlugins(
+ $conf[DefinitionInterface::LISTENER_AFTER],
+ 'after' . $capitalizedName,
+ $extraParams,
+ $returnVoid
+ )
+ );
+ }
+ return array_merge($body, $this->getResultChainLines($resultChain, $returnVoid));
+ }
+
+ /**
+ * Implode result chain into list of assignments
+ *
+ * @param array $resultChain
+ * @param bool $returnVoid
+ * @return array
+ */
+ private function getResultChainLines($resultChain, $returnVoid)
+ {
+ $lines = [];
+ $first = true;
+ foreach ($resultChain as $lp => $piece) {
+ if ($first) {
+ $first = false;
+ } else {
+ $lines[] = "";
+ }
+ if (!$returnVoid) {
+ $piece[0] = (($lp + 1 == count($resultChain)) ? "return " : "\$result = ") . $piece[0];
+ }
+ foreach ($piece as $line) {
+ $lines[] = $line;
+ }
+ }
+ return $lines;
+ }
+
+ /**
+ * Get parameters definition for next callback
+ *
+ * @param array $parameters
+ * @return string
+ */
+ private function getParameterListForNextCallback(array $parameters)
+ {
+ $ret = [];
+ foreach ($parameters as $parameter) {
+ $ret [] =
+ ($parameter->isPassedByReference() ? '&' : '') .
+ "\${$parameter->getName()}" .
+ ($parameter->isDefaultValueAvailable() ?
+ ' = ' . ($parameter->isDefaultValueConstant() ?
+ $parameter->getDefaultValueConstantName() :
+ str_replace("\n", '', var_export($parameter->getDefaultValue(), true))) :
+ '');
+ }
+ return implode(', ', $ret);
+ }
+
+ /**
+ * Implodes parameters into list for call
+ *
+ * @param array(\ReflectionParameter) $parameters
+ * @return string
+ */
+ private function getParameterList(array $parameters)
+ {
+ $ret = [];
+ foreach ($parameters as $parameter) {
+ $ret [] = "\${$parameter->getName()}";
+ }
+ return implode(', ', $ret);
+ }
+
+ /**
+ * Get plugin getter name
+ *
+ * @param array $plugin
+ * @return string
+ */
+ private function getGetterName($plugin)
+ {
+ return '____plugin_' . $plugin['clean_name'];
+ }
+
+ /**
+ * Prepares plugin getter for code generator
+ *
+ * @param array $plugin
+ * @return array
+ */
+ private function getPluginGetterInfo($plugin)
+ {
+ return [
+ 'name' => $this->getGetterName($plugin),
+ 'visibility' => 'private',
+ 'parameters' => [],
+ 'body' => "return \$this->____om->get(\\" . "{$plugin['class']}::class);",
+ 'docblock' => [
+ 'shortDescription' => 'plugin "' . $plugin['code'] . '"' . "\n" . '@return \\' . $plugin['class']
+ ],
+ ];
+ }
+
+ /**
+ * Get compiled method data for code generator
+ *
+ * @param \ReflectionMethod $method
+ * @param array $config
+ * @return array
+ */
+ private function getCompiledMethodInfo(\ReflectionMethod $method, $config)
+ {
+ $parameters = $method->getParameters();
+ $returnsVoid = ($method->hasReturnType() && $method->getReturnType()->getName() == 'void');
+
+ $cases = $this->getScopeCasesFromConfig($config);
+
+ if (count($cases) == 1) {
+ $body = $this->getMethodSourceFromConfig($method->getName(), $cases[0]['conf'], $parameters, $returnsVoid);
+ } else {
+ $body = [
+ 'switch ($this->____scope->getCurrentScope()) {'
+ ];
+
+ foreach ($cases as $case) {
+ // phpcs:ignore Magento2.Performance.ForeachArrayMerge
+ $body = array_merge($body, $case['cases']);
+ $this->addCodeSubBlock(
+ $body,
+ $this->getMethodSourceFromConfig($method->getName(), $case['conf'], $parameters, $returnsVoid),
+ 2
+ );
+ if ($returnsVoid) {
+ $body[] = "\t\tbreak;";
+ }
+ }
+
+ $body[] = "}";
+ }
+
+ $returnType = $method->getReturnType();
+ $returnTypeValue = $returnType
+ ? ($returnType->allowsNull() ? '?' : '') . $returnType->getName()
+ : null;
+ if ($returnTypeValue === 'self') {
+ $returnTypeValue = $this->_getFullyQualifiedClassName($method->getDeclaringClass()->getName());
+ }
+ return [
+ 'name' => ($method->returnsReference() ? '& ' : '') . $method->getName(),
+ 'parameters' =>array_map([$this, '_getMethodParameterInfo'], $parameters),
+ 'body' => implode("\n", $body),
+ 'returnType' => $returnTypeValue,
+ 'docblock' => ['shortDescription' => '@inheritdoc'],
+ ];
+ }
+
+ /**
+ * Get scope cases from config
+ *
+ * @param array $config
+ * @return array
+ */
+ private function getScopeCasesFromConfig($config)
+ {
+ $cases = [];
+ //group cases by config
+ foreach ($config as $scope => $conf) {
+ $caseStr = "\tcase '$scope':";
+ foreach ($cases as &$case) {
+ if ($case['conf'] == $conf) {
+ $case['cases'][] = $caseStr;
+ continue 2;
+ }
+ }
+ $cases[] = ['cases'=>[$caseStr], 'conf'=>$conf];
+ }
+ $cases[count($cases) - 1]['cases'] = ["\tdefault:"];
+ return $cases;
+ }
+
+ /**
+ * Generate array with plugin info
+ *
+ * @param CompiledPluginList $plugins
+ * @param string $code
+ * @param string $className
+ * @param array $allPlugins
+ * @param string|null $next
+ * @return mixed
+ */
+ private function getPluginInfo(CompiledPluginList $plugins, $code, $className, &$allPlugins, $next = null)
+ {
+ $className = $plugins->getPluginType($className, $code);
+ if (!isset($allPlugins[$code])) {
+ $allPlugins[$code] = [];
+ }
+ if (empty($allPlugins[$code][$className])) {
+ $suffix = count($allPlugins[$code]) ? count($allPlugins[$code]) + 1 : '';
+ $allPlugins[$code][$className] = [
+ 'code' => $code,
+ 'class' => $className,
+ 'clean_name' => preg_replace("/[^A-Za-z0-9_]/", '_', $code . $suffix)
+ ];
+ }
+ $result = $allPlugins[$code][$className];
+ $result['next'] = $next;
+ return $result;
+ }
+
+ /**
+ * Get next set of plugins
+ *
+ * @param CompiledPluginList $plugins
+ * @param string $className
+ * @param string $method
+ * @param array $allPlugins
+ * @param string $next
+ * @return array
+ */
+ private function getPluginsChain(CompiledPluginList $plugins, $className, $method, &$allPlugins, $next = '__self')
+ {
+ $result = $plugins->getNext($className, $method, $next);
+ if (!empty($result[DefinitionInterface::LISTENER_BEFORE])) {
+ foreach ($result[DefinitionInterface::LISTENER_BEFORE] as $k => $code) {
+ $result[DefinitionInterface::LISTENER_BEFORE][$k] = $this->getPluginInfo(
+ $plugins,
+ $code,
+ $className,
+ $allPlugins
+ );
+ }
+ }
+ if (!empty($result[DefinitionInterface::LISTENER_AFTER])) {
+ foreach ($result[DefinitionInterface::LISTENER_AFTER] as $k => $code) {
+ $result[DefinitionInterface::LISTENER_AFTER][$k] = $this->getPluginInfo(
+ $plugins,
+ $code,
+ $className,
+ $allPlugins
+ );
+ }
+ }
+ if (isset($result[DefinitionInterface::LISTENER_AROUND])) {
+ $result[DefinitionInterface::LISTENER_AROUND] = $this->getPluginInfo(
+ $plugins,
+ $result[DefinitionInterface::LISTENER_AROUND],
+ $className,
+ $allPlugins,
+ $this->getPluginsChain(
+ $plugins,
+ $className,
+ $method,
+ $allPlugins,
+ $result[DefinitionInterface::LISTENER_AROUND]
+ )
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Generates recursive maps of plugins for given method
+ *
+ * @param \ReflectionMethod $method
+ * @param array $allPlugins
+ * @return array
+ */
+ private function getPluginsConfig(\ReflectionMethod $method, &$allPlugins)
+ {
+ $className = ltrim($this->getSourceClassName(), '\\');
+
+ $result = [];
+ foreach ($this->areasPlugins->getPluginsConfigForAllAreas() as $scope => $pluginsList) {
+ $pluginChain = $this->getPluginsChain($pluginsList, $className, $method->getName(), $allPlugins);
+ if ($pluginChain) {
+ $result[$scope] = $pluginChain;
+ }
+ }
+ //if plugins are not empty make sure default case will be handled
+ if (!empty($result) && !$pluginChain) {
+ $result[$scope] = [];
+ }
+ return $result;
+ }
+}
diff --git a/lib/internal/Magento/Framework/CompiledInterception/Generator/CompiledPluginList.php b/lib/internal/Magento/Framework/CompiledInterception/Generator/CompiledPluginList.php
new file mode 100644
index 0000000000000..062449125f7d6
--- /dev/null
+++ b/lib/internal/Magento/Framework/CompiledInterception/Generator/CompiledPluginList.php
@@ -0,0 +1,98 @@
+pluginList = $pluginList;
+ }
+
+ /**
+ * Retrieve plugin Instance
+ *
+ * @param string $type
+ * @param string $code
+ * @return mixed
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function getPlugin($type, $code)
+ {
+ return null;
+ }
+
+ /**
+ * Merge configuration
+ *
+ * @param array $config
+ * @return void
+ */
+ public function merge(array $config)
+ {
+ $this->pluginList->merge($config);
+ }
+
+ /**
+ * Get class of a plugin
+ *
+ * @param string $type
+ * @param string $code
+ * @return mixed
+ */
+ public function getPluginType(string $type, string $code)
+ {
+ return $this->pluginList->getPluginType($type, $code);
+ }
+
+ /**
+ * Set current scope
+ *
+ * @param ScopeInterface $scope
+ */
+ public function setScope(ScopeInterface $scope)
+ {
+ $this->pluginList->setScope($scope);
+ }
+
+ /**
+ * Retrieve next plugins in chain
+ *
+ * @param string $type
+ * @param string $method
+ * @param string $code
+ * @return array
+ */
+ public function getNext($type, $method, $code = null)
+ {
+ return $this->pluginList->getNext($type, $method, $code);
+ }
+
+ /**
+ * PluginList instance should not be shared.
+ */
+ public function __clone()
+ {
+ $this->pluginList = clone $this->pluginList;
+ }
+}
diff --git a/lib/internal/Magento/Framework/CompiledInterception/Generator/FileCache.php b/lib/internal/Magento/Framework/CompiledInterception/Generator/FileCache.php
new file mode 100644
index 0000000000000..3b94c1b7f5982
--- /dev/null
+++ b/lib/internal/Magento/Framework/CompiledInterception/Generator/FileCache.php
@@ -0,0 +1,167 @@
+directoryList = $directoryList;
+ $this->cachePath = $cachePath;
+ }
+
+ /**
+ * Test if a cache is available for the given id
+ *
+ * @param string $identifier Cache id
+ * @return int|bool Last modified time of cache entry if it is available, false otherwise
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function test($identifier)
+ {
+ return file_exists($this->getCacheFilePath($identifier));
+ }
+
+ /**
+ * Load cache record by its unique identifier
+ *
+ * @param string $identifier
+ * @return string|bool
+ * @SuppressWarnings(PHPMD)
+ */
+ public function load($identifier)
+ {
+ // @codingStandardsIgnoreLine
+ return $this->getCachePath() ? @include $this->getCacheFilePath($identifier) : false;
+ }
+
+ /**
+ * Save cache record
+ *
+ * @param string $data
+ * @param string $identifier
+ * @param array $tags
+ * @param int|bool|null $lifeTime
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function save($data, $identifier, array $tags = [], $lifeTime = null)
+ {
+ if ($this->getCachePath()) {
+ $path = $this->getCacheFilePath($identifier);
+ if (!is_dir(dirname($path))) {
+ mkdir(dirname($path), 0777, true);
+ }
+ file_put_contents(
+ $path,
+ ''
+ );
+ return true;
+ }
+ }
+
+ /**
+ * Remove cache record by its unique identifier
+ *
+ * @param string $identifier
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function remove($identifier)
+ {
+ return false;
+ }
+
+ /**
+ * Clean cache records matching specified tags
+ *
+ * @param string $mode
+ * @param array $tags
+ * @return bool
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, array $tags = [])
+ {
+ if ($this->getCachePath()) {
+ foreach (glob($this->getCachePath() . '/*') as $file) {
+ if (is_file($file)) {
+ unlink($file);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve backend instance
+ *
+ * @return \Zend_Cache_Backend_Interface
+ */
+ public function getBackend()
+ {
+ return null;
+ }
+
+ /**
+ * Retrieve frontend instance compatible with Zend Locale Data setCache() to be used as a workaround
+ *
+ * @return \Zend_Cache_Core
+ */
+ public function getLowLevelFrontend()
+ {
+ return null;
+ }
+
+ /**
+ * Get file cache path.
+ *
+ * @param string $identifier
+ * @return string
+ */
+ private function getCacheFilePath($identifier): string
+ {
+ $identifier = str_replace('|', '_', $identifier);
+ return $this->getCachePath() . DIRECTORY_SEPARATOR . $identifier . '.php';
+ }
+
+ /**
+ * Get cache path.
+ *
+ * @return string
+ */
+ private function getCachePath(): string
+ {
+ if (!$this->cachePath) {
+ $this->cachePath = $this->directoryList->getPath(DirectoryList::STATIC_CACHE);
+ }
+
+ return $this->cachePath;
+ }
+}
diff --git a/lib/internal/Magento/Framework/CompiledInterception/Generator/NoSerialize.php b/lib/internal/Magento/Framework/CompiledInterception/Generator/NoSerialize.php
new file mode 100644
index 0000000000000..dca9428d1e6b5
--- /dev/null
+++ b/lib/internal/Magento/Framework/CompiledInterception/Generator/NoSerialize.php
@@ -0,0 +1,40 @@
+scope = $scope;
+ }
+
+ /**
+ * Get current configuration scope identifier
+ *
+ * @return string
+ */
+ public function getCurrentScope()
+ {
+ return $this->scope;
+ }
+
+ /**
+ * Unused interface method
+ *
+ * @param string $scope
+ */
+ public function setCurrentScope($scope)
+ {
+ $this->scope = $scope;
+ }
+}
diff --git a/lib/internal/Magento/Framework/CompiledInterception/README.md b/lib/internal/Magento/Framework/CompiledInterception/README.md
new file mode 100644
index 0000000000000..77b0bb52f47f1
--- /dev/null
+++ b/lib/internal/Magento/Framework/CompiledInterception/README.md
@@ -0,0 +1,88 @@
+### ABOUT
+
+This component changes the way Magento 2 generates Interceptor classes (a mechanism that allows plugins to work together).
+
+Instead of generating boilerplate code it compiles the Interceptor using information from source code.
+This makes plugins slightly faster and as Magento uses a lot of plugins, even at its core, it lowers request time by ~10%.
+This is important in places where there is a lot of non-cached PHP logic going on (for example admin panel is noticeably faster).
+
+The default method uses code that is called on nearly each method to see if there are any plugins connected, in generated code this is not required and call-stack is reduced.
+
+Having plugins called directly also makes code easier to debug and bugs easier to find.
+
+The Interceptors generated by this plugin are 100% compatible with the ones generated by Magento by default, so there is no need to change anything in your plugins.
+
+### ENABLING
+
+To use compiled interceptors please add following preference to your di.xml
+```
+
+
+
+
+ Magento\Framework\CompiledInterception\Generator\CompiledInterceptor
+
+
+```
+
+Clear generated files and cache:
+
+`rm -rf generated/* && bin/magento cache:clean`
+
+### DISABLING
+
+Remove preferences from `app/etc/di.xml`, remove module and clear cache and generated files.
+
+### TECHNICAL DETAILS
+
+Instead of interceptors that read plugins config at runtime like this:
+
+```
+public function methodX($arg) {
+ $pluginInfo = $this->pluginList->getNext($this->subjectType, 'methodX');
+ if (!$pluginInfo) {
+ return parent::methodX($arg);
+ } else {
+ return $this->___callPlugins('methodX', func_get_args(), $pluginInfo);
+ }
+}
+```
+
+This generator generates static interceptors like this:
+
+
+```
+public function methodX($arg) {
+ switch(getCurrentScope()){
+ case 'frontend':
+ $this->_get_example_plugin()->beforeMethodX($this, $arg);
+ $this->_get_another_plugin()->beforeMethodX($this, $arg);
+ $result = $this->_get_around_plugin()->aroundMethodX($this, function($arg){
+ return parent::methodX($arg);
+ });
+ return $this->_get_after_plugin()->afterMethodX($this, $result);
+ case 'adminhtml':
+ // ...
+ default:
+ return parent::methodX($arg);
+ }
+}
+```
+
+
+#### PROS
+
+* Easier debugging.
+ * If you ever stumbled upon `___callPlugins` when debugging you should know how painful it is to debug issues inside plugins.
+ * Generated code is decorated with PHPDoc for easier debugging in IDE
+
+* Fastest response time (5%-15% faster in developer and production mode)
+ * No redundant calls to `___callPlugins` in call stack.
+ * Methods with no plugins are not overridden in parent at all.
+
+* Implemented as a module and can be easily reverted to the default `Generator\Interceptor`
+
+#### CONS
+
+* Each time after making change in etc plugins config, `generated/code/*` and `var/cache/*` needs to be purged
+* As this does not load plugins at runtime, might not work in an edge case of plugging into core Magento classes like `PluginsList` etc.
diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/CallbackPool.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/CallbackPool.php
new file mode 100644
index 0000000000000..d35e4ddf948d7
--- /dev/null
+++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/CallbackPool.php
@@ -0,0 +1,57 @@
+string = $string;
$this->dateTime = $dateTime;
@@ -278,6 +286,8 @@ public function __construct(
} catch (Zend_Db_Adapter_Exception $e) {
throw new \InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
+ $this->executeCommitCallbacks = $executeCommitCallbacks
+ ?? ObjectManager::getInstance()->get(ExecuteCommitCallbacks::class);
}
/**
@@ -321,7 +331,8 @@ public function commit()
throw new \Exception(AdapterInterface::ERROR_ROLLBACK_INCOMPLETE_MESSAGE);
}
--$this->_transactionLevel;
- return $this;
+
+ return $this->executeCommitCallbacks->afterCommit($this);
}
/**
@@ -344,7 +355,8 @@ public function rollBack()
$this->_isRolledBack = true;
}
--$this->_transactionLevel;
- return $this;
+
+ return $this->executeCommitCallbacks->afterRollBack($this);
}
/**
diff --git a/lib/internal/Magento/Framework/EntityManager/CallbackHandler.php b/lib/internal/Magento/Framework/EntityManager/CallbackHandler.php
index 255257fd4ac3d..8d401ca8cb536 100644
--- a/lib/internal/Magento/Framework/EntityManager/CallbackHandler.php
+++ b/lib/internal/Magento/Framework/EntityManager/CallbackHandler.php
@@ -6,12 +6,9 @@
namespace Magento\Framework\EntityManager;
-use Magento\Framework\Model\CallbackPool;
+use Magento\Framework\DB\Adapter\Pdo\CallbackPool;
use Psr\Log\LoggerInterface;
-/**
- * Class CallbackHandler
- */
class CallbackHandler
{
/**
@@ -39,6 +36,8 @@ public function __construct(
}
/**
+ * Process entity type.
+ *
* @param string $entityType
* @throws \Exception
* @return void
@@ -62,6 +61,8 @@ public function process($entityType)
}
/**
+ * Attach entity type to callback pool.
+ *
* @param string $entityType
* @param array $callback
* @throws \Exception
@@ -74,6 +75,8 @@ public function attach($entityType, $callback)
}
/**
+ * Remove entity type from callback pool.
+ *
* @param string $entityType
* @throws \Exception
* @return void
diff --git a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php
index 4a3ed34810723..14f9563f43fbc 100644
--- a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php
+++ b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php
@@ -9,7 +9,7 @@
use Magento\Framework\Code\Generator\EntityAbstract;
-class Interceptor extends EntityAbstract
+class Interceptor extends EntityAbstract implements InterceptorInterface
{
public const ENTITY_TYPE = 'interceptor';
diff --git a/lib/internal/Magento/Framework/Interception/Code/Generator/InterceptorInterface.php b/lib/internal/Magento/Framework/Interception/Code/Generator/InterceptorInterface.php
new file mode 100644
index 0000000000000..4cf23952d8864
--- /dev/null
+++ b/lib/internal/Magento/Framework/Interception/Code/Generator/InterceptorInterface.php
@@ -0,0 +1,21 @@
+_data = $this->pluginListGenerator->merge($config, $this->_data);
}
+
+ /**
+ * Get class of a plugin
+ *
+ * @param string $type
+ * @param string $code
+ * @return mixed
+ */
+ public function getPluginType(string $type, string $code)
+ {
+ return $this->_inherited[$type][$code]['instance'];
+ }
+
+ /**
+ * Set current scope
+ *
+ * @param ScopeInterface $scope
+ */
+ public function setScope(ScopeInterface $scope)
+ {
+ $this->_configScope = $scope;
+ }
}
diff --git a/lib/internal/Magento/Framework/Model/CallbackPool.php b/lib/internal/Magento/Framework/Model/CallbackPool.php
index c4693e5575020..e908f368f8e06 100644
--- a/lib/internal/Magento/Framework/Model/CallbackPool.php
+++ b/lib/internal/Magento/Framework/Model/CallbackPool.php
@@ -3,51 +3,48 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Framework\Model;
+use Magento\Framework\DB\Adapter\Pdo\CallbackPool as PdoCallbackPool;
+
/**
- * Class CallbackPool
+ * @deprecated please use Magento\Framework\DB\Adapter\Pdo\CallbackPool.
*/
class CallbackPool
{
/**
- * Array of callbacks subscribed to commit transaction commit
+ * Add callback by hash key.
*
- * @var array
- */
- private static $commitCallbacks = [];
-
- /**
* @param string $hashKey
* @param array $callback
* @return void
*/
public static function attach($hashKey, $callback)
{
- self::$commitCallbacks[$hashKey][] = $callback;
+ PdoCallbackPool::attach($hashKey, $callback);
}
/**
+ * Remove callbacks by hash key.
+ *
* @param string $hashKey
* @return void
*/
public static function clear($hashKey)
{
- self::$commitCallbacks[$hashKey] = [];
+ PdoCallbackPool::clear($hashKey);
}
/**
+ * Get callbacks by hash key.
+ *
* @param string $hashKey
* @return array
*/
public static function get($hashKey)
{
- if (!isset(self::$commitCallbacks[$hashKey])) {
- return [];
- }
- $callbacks = self::$commitCallbacks[$hashKey];
- self::$commitCallbacks[$hashKey] = [];
- return $callbacks;
+ return PdoCallbackPool::get($hashKey);
}
}
diff --git a/lib/internal/Magento/Framework/Model/ExecuteCommitCallbacks.php b/lib/internal/Magento/Framework/Model/ExecuteCommitCallbacks.php
index 799f8ffda253c..96837d57225f5 100644
--- a/lib/internal/Magento/Framework/Model/ExecuteCommitCallbacks.php
+++ b/lib/internal/Magento/Framework/Model/ExecuteCommitCallbacks.php
@@ -8,6 +8,7 @@
namespace Magento\Framework\Model;
use Magento\Framework\DB\Adapter\AdapterInterface;
+use Magento\Framework\DB\Adapter\Pdo\CallbackPool;
use Psr\Log\LoggerInterface;
/**
@@ -31,14 +32,13 @@ public function __construct(LoggerInterface $logger)
/**
* Execute callbacks after commit.
*
- * @param AdapterInterface $subject
- * @param AdapterInterface $result
+ * @param AdapterInterface $adapter
* @return AdapterInterface
*/
- public function afterCommit(AdapterInterface $subject, AdapterInterface $result): AdapterInterface
+ public function afterCommit(AdapterInterface $adapter): AdapterInterface
{
- if ($result->getTransactionLevel() === 0) {
- $callbacks = CallbackPool::get(spl_object_hash($subject));
+ if ($adapter->getTransactionLevel() === 0) {
+ $callbacks = CallbackPool::get(spl_object_hash($adapter));
foreach ($callbacks as $callback) {
try {
call_user_func($callback);
@@ -48,20 +48,19 @@ public function afterCommit(AdapterInterface $subject, AdapterInterface $result)
}
}
- return $result;
+ return $adapter;
}
/**
* Drop callbacks after rollBack.
*
- * @param AdapterInterface $subject
- * @param AdapterInterface $result
+ * @param AdapterInterface $adapter
* @return AdapterInterface
*/
- public function afterRollBack(AdapterInterface $subject, AdapterInterface $result): AdapterInterface
+ public function afterRollBack(AdapterInterface $adapter): AdapterInterface
{
- CallbackPool::clear(spl_object_hash($subject));
+ CallbackPool::clear(spl_object_hash($adapter));
- return $result;
+ return $adapter;
}
}
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php b/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php
index 221110217a200..691d97c9e957b 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/AbstractResource.php
@@ -7,7 +7,7 @@
use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
-use Magento\Framework\Model\CallbackPool;
+use Magento\Framework\DB\Adapter\Pdo\CallbackPool;
use Magento\Framework\Serialize\Serializer\Json;
/**
diff --git a/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php b/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php
index 492af22815311..d0f498e26db6b 100644
--- a/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php
+++ b/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php
@@ -5,9 +5,9 @@
*/
namespace Magento\Framework\ObjectManager;
+use Magento\Framework\Code\Generator\Autoloader;
use Magento\Framework\Filesystem\DriverInterface;
use Magento\Framework\ObjectManager\Definition\Runtime;
-use Magento\Framework\Code\Generator\Autoloader;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -52,8 +52,16 @@ public function __construct(
*/
public function createClassDefinition()
{
+ foreach (spl_autoload_functions() as $autoloader) {
+ if (is_array($autoloader) && $autoloader[0] instanceof \Magento\Framework\Code\Generator\Autoloader) {
+ spl_autoload_unregister($autoloader);
+ break;
+ }
+ }
+
$autoloader = new Autoloader($this->getCodeGenerator());
spl_autoload_register([$autoloader, 'load']);
+
return new Runtime();
}
@@ -91,6 +99,7 @@ public function getCodeGenerator()
);
$this->codeGenerator = new \Magento\Framework\Code\Generator($generatorIo);
}
+
return $this->codeGenerator;
}
}
diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/Compiled.php b/lib/internal/Magento/Framework/ObjectManager/Factory/Compiled.php
index b219d93b0f0fa..64a67c0bb39fe 100644
--- a/lib/internal/Magento/Framework/ObjectManager/Factory/Compiled.php
+++ b/lib/internal/Magento/Framework/ObjectManager/Factory/Compiled.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Framework\ObjectManager\Factory;
+/**
+ * Compiled factory.
+ */
class Compiled extends AbstractFactory
{
/**
diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
index 4c50a3de4fb31..e2231ed142285 100644
--- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
@@ -10,19 +10,21 @@
use Magento\Framework\Filesystem\Io\File;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Magento\Framework\Filesystem;
-use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\DeploymentConfig;
+use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\ObjectManager\ConfigWriterInterface;
use Magento\Framework\Component\ComponentRegistrar;
use Magento\Framework\Config\ConfigOptionsListConstants;
+use Magento\Framework\Console\Cli;
+use Magento\Framework\Filesystem;
use Magento\Setup\Model\ObjectManagerProvider;
use Magento\Setup\Module\Di\App\Task\Manager;
-use Magento\Setup\Module\Di\App\Task\OperationFactory;
use Magento\Setup\Module\Di\App\Task\OperationException;
+use Magento\Setup\Module\Di\App\Task\OperationFactory;
use Magento\Setup\Module\Di\App\Task\OperationInterface;
+use Magento\Setup\Module\Di\Compiler\Config\Chain\InterceptorSubstitutionInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
-use Magento\Framework\Console\Cli;
/**
* Command to run compile in single-tenant mode
@@ -135,7 +137,7 @@ private function checkEnvironment()
$config = $this->deploymentConfig->get(ConfigOptionsListConstants::KEY_MODULES);
if (!$config) {
$messages[] = 'You cannot run this command because modules are not enabled. You can enable modules by'
- . ' running the \'module:enable --all\' command.';
+ . ' running the \'module:enable --all\' command.';
}
return $messages;
@@ -320,39 +322,40 @@ private function configureObjectManager(OutputInterface $output)
{
$this->objectManager->configure(
[
- 'preferences' => [\Magento\Framework\App\ObjectManager\ConfigWriterInterface::class =>
- \Magento\Framework\App\ObjectManager\ConfigWriter\Filesystem::class,
- ], \Magento\Setup\Module\Di\Compiler\Config\ModificationChain::class => [
+ 'preferences' => [
+ ConfigWriterInterface::class => \Magento\Framework\App\ObjectManager\ConfigWriter\Filesystem::class,
+ ],
+ \Magento\Setup\Module\Di\Compiler\Config\ModificationChain::class => [
'arguments' => [
'modificationsList' => [
'BackslashTrim' => [
- 'instance' =>
- \Magento\Setup\Module\Di\Compiler\Config\Chain\BackslashTrim::class
+ 'instance' => \Magento\Setup\Module\Di\Compiler\Config\Chain\BackslashTrim::class
],
'PreferencesResolving' => [
- 'instance' =>
- \Magento\Setup\Module\Di\Compiler\Config\Chain\PreferencesResolving::class
+ 'instance' => \Magento\Setup\Module\Di\Compiler\Config\Chain\PreferencesResolving::class
],
'InterceptorSubstitution' => [
- 'instance' =>
- \Magento\Setup\Module\Di\Compiler\Config\Chain\InterceptorSubstitution::class
+ 'instance' => InterceptorSubstitutionInterface::class
],
'InterceptionPreferencesResolving' => [
'instance' => \Magento\Setup\Module\Di\Compiler\Config\Chain\PreferencesResolving::class
],
]
]
- ], \Magento\Setup\Module\Di\Code\Generator\PluginList::class => [
+ ],
+ \Magento\Setup\Module\Di\Code\Generator\PluginList::class => [
'arguments' => [
'cache' => [
'instance' => \Magento\Framework\App\Interception\Cache\CompiledConfig::class
]
]
- ], \Magento\Setup\Module\Di\Code\Reader\ClassesScanner::class => [
+ ],
+ \Magento\Setup\Module\Di\Code\Reader\ClassesScanner::class => [
'arguments' => [
'excludePatterns' => $this->excludedPathsList
]
- ], \Magento\Setup\Module\Di\Compiler\Log\Writer\Console::class => [
+ ],
+ \Magento\Setup\Module\Di\Compiler\Log\Writer\Console::class => [
'arguments' => [
'output' => $output,
]
diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/Operation/Interception.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/Interception.php
index 9c629998e555d..e07535f532c4a 100644
--- a/setup/src/Magento/Setup/Module/Di/App/Task/Operation/Interception.php
+++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/Interception.php
@@ -5,10 +5,11 @@
*/
namespace Magento\Setup\Module\Di\App\Task\Operation;
+use Magento\Framework\App;
+use Magento\Framework\Interception\Code\Generator\Interceptor;
use Magento\Setup\Module\Di\App\Task\OperationInterface;
use Magento\Setup\Module\Di\Code\Generator\InterceptionConfigurationBuilder;
-use Magento\Framework\Interception\Code\Generator\Interceptor;
-use Magento\Framework\App;
+use Magento\Setup\Module\Di\Code\Generator\Interceptor as InterceptorGenerator;
use Magento\Setup\Module\Di\Code\GeneratorFactory;
use Magento\Setup\Module\Di\Code\Reader\ClassesScanner;
@@ -39,11 +40,17 @@ class Interception implements OperationInterface
*/
private $generatorFactory;
+ /**
+ * @var string
+ */
+ private $interceptorGeneratorClass;
+
/**
* @param InterceptionConfigurationBuilder $interceptionConfigurationBuilder
* @param App\AreaList $areaList
* @param ClassesScanner $classesScanner
* @param GeneratorFactory $generatorFactory
+ * @param string $interceptorGeneratorClass
* @param array $data
*/
public function __construct(
@@ -51,6 +58,7 @@ public function __construct(
App\AreaList $areaList,
ClassesScanner $classesScanner,
GeneratorFactory $generatorFactory,
+ string $interceptorGeneratorClass = InterceptorGenerator::class,
$data = []
) {
$this->interceptionConfigurationBuilder = $interceptionConfigurationBuilder;
@@ -58,10 +66,11 @@ public function __construct(
$this->data = $data;
$this->classesScanner = $classesScanner;
$this->generatorFactory = $generatorFactory;
+ $this->interceptorGeneratorClass = $interceptorGeneratorClass;
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function doOperation()
{
@@ -80,6 +89,7 @@ public function doOperation()
$paths = (array)$paths;
}
foreach ($paths as $path) {
+ // phpcs:ignore Magento2.Performance.ForeachArrayMerge
$classesList = array_merge($classesList, $this->classesScanner->getList($path));
}
}
@@ -92,7 +102,7 @@ public function doOperation()
[
'ioObject' => $generatorIo,
'generatedEntities' => [
- Interceptor::ENTITY_TYPE => \Magento\Setup\Module\Di\Code\Generator\Interceptor::class,
+ Interceptor::ENTITY_TYPE => $this->interceptorGeneratorClass,
]
]
);
diff --git a/setup/src/Magento/Setup/Module/Di/Compiler/Config/Chain/InterceptorSubstitution.php b/setup/src/Magento/Setup/Module/Di/Compiler/Config/Chain/InterceptorSubstitution.php
index 66cd7b1a595e6..9f99d5da90a84 100644
--- a/setup/src/Magento/Setup/Module/Di/Compiler/Config/Chain/InterceptorSubstitution.php
+++ b/setup/src/Magento/Setup/Module/Di/Compiler/Config/Chain/InterceptorSubstitution.php
@@ -7,7 +7,7 @@
use Magento\Setup\Module\Di\Compiler\Config\ModificationInterface;
-class InterceptorSubstitution implements ModificationInterface
+class InterceptorSubstitution implements ModificationInterface, InterceptorSubstitutionInterface
{
/**
* Modifies input config
diff --git a/setup/src/Magento/Setup/Module/Di/Compiler/Config/Chain/InterceptorSubstitutionInterface.php b/setup/src/Magento/Setup/Module/Di/Compiler/Config/Chain/InterceptorSubstitutionInterface.php
new file mode 100644
index 0000000000000..52bb286a87033
--- /dev/null
+++ b/setup/src/Magento/Setup/Module/Di/Compiler/Config/Chain/InterceptorSubstitutionInterface.php
@@ -0,0 +1,13 @@
+