diff --git a/app/code/Magento/PageCache/Controller/Block.php b/app/code/Magento/PageCache/Controller/Block.php index 7927284763ecb..e69614496c66d 100644 --- a/app/code/Magento/PageCache/Controller/Block.php +++ b/app/code/Magento/PageCache/Controller/Block.php @@ -9,6 +9,7 @@ use Magento\Framework\Serialize\Serializer\Base64Json; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\View\Layout\LayoutCacheKeyInterface; abstract class Block extends \Magento\Framework\App\Action\Action { @@ -27,17 +28,31 @@ abstract class Block extends \Magento\Framework\App\Action\Action */ private $base64jsonSerializer; + /** + * Layout cache keys to be able to generate different cache id for same handles + * + * @var LayoutCacheKeyInterface + */ + private $layoutCacheKey; + + /** + * @var string + */ + private $layoutCacheKeyName = 'mage_pagecache'; + /** * @param \Magento\Framework\App\Action\Context $context * @param \Magento\Framework\Translate\InlineInterface $translateInline * @param Json $jsonSerializer * @param Base64Json $base64jsonSerializer + * @param LayoutCacheKeyInterface $layoutCacheKey */ public function __construct( \Magento\Framework\App\Action\Context $context, \Magento\Framework\Translate\InlineInterface $translateInline, Json $jsonSerializer = null, - Base64Json $base64jsonSerializer = null + Base64Json $base64jsonSerializer = null, + LayoutCacheKeyInterface $layoutCacheKey = null ) { parent::__construct($context); $this->translateInline = $translateInline; @@ -45,6 +60,8 @@ public function __construct( ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Json::class); $this->base64jsonSerializer = $base64jsonSerializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Base64Json::class); + $this->layoutCacheKey = $layoutCacheKey + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(LayoutCacheKeyInterface::class); } /** @@ -63,10 +80,12 @@ protected function _getBlocks() $blocks = $this->jsonSerializer->unserialize($blocks); $handles = $this->base64jsonSerializer->unserialize($handles); + $layout = $this->_view->getLayout(); + $this->layoutCacheKey->addCacheKeys($this->layoutCacheKeyName); + $this->_view->loadLayout($handles, true, true, false); $data = []; - $layout = $this->_view->getLayout(); foreach ($blocks as $blockName) { $blockInstance = $layout->getBlock($blockName); if (is_object($blockInstance)) { diff --git a/app/code/Magento/PageCache/Test/Unit/Controller/Block/EsiTest.php b/app/code/Magento/PageCache/Test/Unit/Controller/Block/EsiTest.php index 4a7628c7ad839..391c827d89cb4 100644 --- a/app/code/Magento/PageCache/Test/Unit/Controller/Block/EsiTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Controller/Block/EsiTest.php @@ -39,6 +39,11 @@ class EsiTest extends \PHPUnit\Framework\TestCase */ protected $layoutMock; + /** + * @var \Magento\Framework\View\Layout\LayoutCacheKeyInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $layoutCacheKeyMock; + /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Translate\InlineInterface */ @@ -52,6 +57,8 @@ protected function setUp() $this->layoutMock = $this->getMockBuilder(\Magento\Framework\View\Layout::class) ->disableOriginalConstructor()->getMock(); + $this->layoutCacheKeyMock = $this->getMockForAbstractClass(\Magento\Framework\View\Layout\LayoutCacheKeyInterface::class); + $contextMock = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) ->disableOriginalConstructor()->getMock(); @@ -76,7 +83,8 @@ protected function setUp() 'context' => $contextMock, 'translateInline' => $this->translateInline, 'jsonSerializer' => new \Magento\Framework\Serialize\Serializer\Json(), - 'base64jsonSerializer' => new \Magento\Framework\Serialize\Serializer\Base64Json() + 'base64jsonSerializer' => new \Magento\Framework\Serialize\Serializer\Base64Json(), + 'layoutCacheKey' => $this->layoutCacheKeyMock ] ); } @@ -104,6 +112,11 @@ public function testExecute($blockClass, $shouldSetHeaders) $this->viewMock->expects($this->once())->method('getLayout')->will($this->returnValue($this->layoutMock)); + $this->layoutMock->expects($this->never()) + ->method('getUpdate'); + $this->layoutCacheKeyMock->expects($this->atLeastOnce()) + ->method('addCacheKeys'); + $this->layoutMock->expects($this->once()) ->method('getBlock') ->with($this->equalTo($block)) diff --git a/app/code/Magento/PageCache/Test/Unit/Controller/Block/RenderTest.php b/app/code/Magento/PageCache/Test/Unit/Controller/Block/RenderTest.php index 2b55a93473180..6c46aa7d29fa0 100644 --- a/app/code/Magento/PageCache/Test/Unit/Controller/Block/RenderTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Controller/Block/RenderTest.php @@ -44,14 +44,26 @@ class RenderTest extends \PHPUnit\Framework\TestCase */ protected $layoutMock; + /** + * @var \Magento\Framework\View\Layout\ProcessorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $layoutProcessorMock; + + /** + * @var \Magento\Framework\View\Layout\LayoutCacheKeyInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $layoutCacheKeyMock; + /** * Set up before test */ protected function setUp() { - $this->layoutMock = $this->getMockBuilder( - \Magento\Framework\View\Layout::class - )->disableOriginalConstructor()->getMock(); + $this->layoutMock = $this->getMockBuilder(\Magento\Framework\View\Layout::class) + ->disableOriginalConstructor()->getMock(); + + $this->layoutProcessorMock = $this->getMockForAbstractClass(\Magento\Framework\View\Layout\ProcessorInterface::class); + $this->layoutCacheKeyMock = $this->getMockForAbstractClass(\Magento\Framework\View\Layout\LayoutCacheKeyInterface::class); $contextMock = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) ->disableOriginalConstructor()->getMock(); @@ -65,6 +77,8 @@ protected function setUp() $this->viewMock = $this->getMockBuilder(\Magento\Framework\App\View::class) ->disableOriginalConstructor()->getMock(); + $this->layoutMock->expects($this->any())->method('getUpdate')->will($this->returnValue($this->layoutProcessorMock)); + $contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->requestMock)); $contextMock->expects($this->any())->method('getResponse')->will($this->returnValue($this->responseMock)); $contextMock->expects($this->any())->method('getView')->will($this->returnValue($this->viewMock)); @@ -78,7 +92,8 @@ protected function setUp() 'context' => $contextMock, 'translateInline' => $this->translateInline, 'jsonSerializer' => new \Magento\Framework\Serialize\Serializer\Json(), - 'base64jsonSerializer' => new \Magento\Framework\Serialize\Serializer\Base64Json() + 'base64jsonSerializer' => new \Magento\Framework\Serialize\Serializer\Base64Json(), + 'layoutCacheKey' => $this->layoutCacheKeyMock ] ); } @@ -88,6 +103,8 @@ public function testExecuteNotAjax() $this->requestMock->expects($this->once())->method('isAjax')->will($this->returnValue(false)); $this->requestMock->expects($this->once())->method('setActionName')->will($this->returnValue('noroute')); $this->requestMock->expects($this->once())->method('setDispatched')->will($this->returnValue(false)); + $this->layoutCacheKeyMock->expects($this->never()) + ->method('addCacheKeys'); $this->action->execute(); } @@ -105,6 +122,8 @@ public function testExecuteNoParams() ->method('getParam') ->with($this->equalTo('handles'), $this->equalTo('')) ->will($this->returnValue('')); + $this->layoutCacheKeyMock->expects($this->never()) + ->method('addCacheKeys'); $this->action->execute(); } @@ -150,6 +169,10 @@ public function testExecute() ->will($this->returnValue(base64_encode(json_encode($handles)))); $this->viewMock->expects($this->once())->method('loadLayout')->with($this->equalTo($handles)); $this->viewMock->expects($this->any())->method('getLayout')->will($this->returnValue($this->layoutMock)); + $this->layoutMock->expects($this->never()) + ->method('getUpdate'); + $this->layoutCacheKeyMock->expects($this->atLeastOnce()) + ->method('addCacheKeys'); $this->layoutMock->expects($this->at(0)) ->method('getBlock') ->with($this->equalTo($blocks[0])) diff --git a/app/code/Magento/PageCache/etc/di.xml b/app/code/Magento/PageCache/etc/di.xml index 92daefe046213..2a9abbb860805 100644 --- a/app/code/Magento/PageCache/etc/di.xml +++ b/app/code/Magento/PageCache/etc/di.xml @@ -32,6 +32,11 @@ + + + Magento\Framework\View\Layout\LayoutCacheKeyInterface + + diff --git a/app/etc/di.xml b/app/etc/di.xml index e17505e78da31..33e27305b78b9 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -68,6 +68,7 @@ + @@ -740,6 +741,7 @@ Magento\Framework\View\Layout\File\Collector\Aggregated\Proxy pageLayoutFileCollectorAggregated Magento\Framework\App\Cache\Type\Layout + Magento\Framework\View\Layout\LayoutCacheKeyInterface diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/Layout/MergeTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/Layout/MergeTest.php index 3ba43f7142e82..8404884a7cf5c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/Layout/MergeTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/Layout/MergeTest.php @@ -7,6 +7,7 @@ use Magento\Framework\App\State; use Magento\Framework\Phrase; +use Magento\Framework\View\Layout\LayoutCacheKeyInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -65,6 +66,11 @@ class MergeTest extends \PHPUnit\Framework\TestCase */ protected $pageConfig; + /** + * @var LayoutCacheKeyInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $layoutCacheKeyMock; + protected function setUp() { $files = []; @@ -119,6 +125,11 @@ function ($filename) use ($fileDriver) { ) ); + $this->layoutCacheKeyMock = $this->getMockForAbstractClass(LayoutCacheKeyInterface::class); + $this->layoutCacheKeyMock->expects($this->any()) + ->method('getCacheKeys') + ->willReturn([]); + $this->_model = $objectHelper->getObject( \Magento\Framework\View\Model\Layout\Merge::class, [ @@ -134,6 +145,7 @@ function ($filename) use ($fileDriver) { 'logger' => $this->_logger, 'readFactory' => $readFactory, 'pageConfig' => $this->pageConfig, + 'layoutCacheKey' => $this->layoutCacheKeyMock, ] ); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/Model/Layout/MergeTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/Model/Layout/MergeTest.php index ede1b33296ea4..12c8b1990fbbe 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/Model/Layout/MergeTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/View/Model/Layout/MergeTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Framework\View\Model\Layout; +use Magento\Framework\View\Layout\LayoutCacheKeyInterface; + class MergeTest extends \PHPUnit\Framework\TestCase { /** @@ -18,6 +20,11 @@ class MergeTest extends \PHPUnit\Framework\TestCase */ protected $model; + /** + * @var LayoutCacheKeyInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $layoutCacheKeyMock; + protected function setUp() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -62,9 +69,17 @@ protected function setUp() $link2->setLayoutUpdateId($layoutUpdate2->getId()); $link2->save(); + $this->layoutCacheKeyMock = $this->getMockForAbstractClass(LayoutCacheKeyInterface::class); + $this->layoutCacheKeyMock->expects($this->any()) + ->method('getCacheKeys') + ->willReturn([]); + $this->model = $objectManager->create( \Magento\Framework\View\Model\Layout\Merge::class, - ['theme' => $theme] + [ + 'theme' => $theme, + 'layoutCacheKey' => $this->layoutCacheKeyMock, + ] ); } diff --git a/lib/internal/Magento/Framework/View/Layout/LayoutCacheKeyInterface.php b/lib/internal/Magento/Framework/View/Layout/LayoutCacheKeyInterface.php new file mode 100644 index 0000000000000..1d4bb8aebe882 --- /dev/null +++ b/lib/internal/Magento/Framework/View/Layout/LayoutCacheKeyInterface.php @@ -0,0 +1,27 @@ +cacheKeys = array_merge($this->cacheKeys, $cacheKeys); + } + + /** + * Return cache keys array + * + * @return array + */ + public function getCacheKeys() + { + return $this->cacheKeys; + } +} diff --git a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php index 97cbeb83a7a86..ea8f74107954c 100644 --- a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php +++ b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php @@ -9,6 +9,7 @@ use Magento\Framework\Config\Dom\ValidationException; use Magento\Framework\Filesystem\DriverPool; use Magento\Framework\Filesystem\File\ReadFactory; +use Magento\Framework\View\Layout\LayoutCacheKeyInterface; use Magento\Framework\View\Model\Layout\Update\Validator; /** @@ -103,6 +104,13 @@ class Merge implements \Magento\Framework\View\Layout\ProcessorInterface */ private $appState; + /** + * Cache keys to be able to generate different cache id for same handles + * + * @var LayoutCacheKeyInterface + */ + private $layoutCacheKey; + /** * @var \Magento\Framework\Cache\FrontendInterface */ @@ -165,9 +173,10 @@ class Merge implements \Magento\Framework\View\Layout\ProcessorInterface * @param \Magento\Framework\Cache\FrontendInterface $cache * @param \Magento\Framework\View\Model\Layout\Update\Validator $validator * @param \Psr\Log\LoggerInterface $logger - * @param ReadFactory $readFactory, + * @param ReadFactory $readFactory , * @param \Magento\Framework\View\Design\ThemeInterface $theme Non-injectable theme instance * @param string $cacheSuffix + * @param LayoutCacheKeyInterface $layoutCacheKey * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -181,7 +190,8 @@ public function __construct( \Psr\Log\LoggerInterface $logger, ReadFactory $readFactory, \Magento\Framework\View\Design\ThemeInterface $theme = null, - $cacheSuffix = '' + $cacheSuffix = '', + LayoutCacheKeyInterface $layoutCacheKey = null ) { $this->theme = $theme ?: $design->getDesignTheme(); $this->scope = $scopeResolver->getScope(); @@ -193,6 +203,8 @@ public function __construct( $this->logger = $logger; $this->readFactory = $readFactory; $this->cacheSuffix = $cacheSuffix; + $this->layoutCacheKey = $layoutCacheKey + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(LayoutCacheKeyInterface::class); } /** @@ -715,7 +727,7 @@ protected function _loadFileLayoutUpdatesXml() $updateFiles = array_merge($updateFiles, $this->pageLayoutFileSource->getFiles($theme, '*.xml')); $useErrors = libxml_use_internal_errors(true); foreach ($updateFiles as $file) { - /** @var $fileReader \Magento\Framework\Filesystem\File\Read */ + /** @var $fileReader \Magento\Framework\Filesystem\File\Read */ $fileReader = $this->readFactory->create($file->getFilename(), DriverPool::FILE); $fileStr = $fileReader->readAll($file->getName()); $fileStr = $this->_substitutePlaceholders($fileStr); @@ -914,6 +926,7 @@ public function getScope() */ public function getCacheId() { - return $this->generateCacheId(md5(implode('|', $this->getHandles()))); + $layoutCacheKeys = $this->layoutCacheKey->getCacheKeys(); + return $this->generateCacheId(md5(implode('|', array_merge($this->getHandles(), $layoutCacheKeys)))); } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Model/Layout/MergeTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Model/Layout/MergeTest.php index 0cb3254e818a3..112d171f2574b 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Model/Layout/MergeTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Model/Layout/MergeTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Config\Dom\ValidationSchemaException; use Magento\Framework\Phrase; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Layout\LayoutCacheKeyInterface; class MergeTest extends \PHPUnit\Framework\TestCase { @@ -42,6 +43,11 @@ class MergeTest extends \PHPUnit\Framework\TestCase */ private $appState; + /** + * @var LayoutCacheKeyInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $layoutCacheKeyMock; + protected function setUp() { $this->objectManagerHelper = new ObjectManager($this); @@ -55,6 +61,11 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->layoutCacheKeyMock = $this->getMockForAbstractClass(LayoutCacheKeyInterface::class); + $this->layoutCacheKeyMock->expects($this->any()) + ->method('getCacheKeys') + ->willReturn([]); + $this->model = $this->objectManagerHelper->getObject( \Magento\Framework\View\Model\Layout\Merge::class, [ @@ -62,6 +73,7 @@ protected function setUp() 'layoutValidator' => $this->layoutValidator, 'logger' => $this->logger, 'appState' => $this->appState, + 'layoutCacheKey' => $this->layoutCacheKeyMock, ] ); }