Skip to content

Commit 6194581

Browse files
committed
ACP2E-4040: 500 error occurs on frontend, due to incorrect layout structure is cached in layout
1 parent 76e2a92 commit 6194581

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

lib/internal/Magento/Framework/View/Layout.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Magento\Framework\View\Layout\Reader\ContextFactory;
2323
use Magento\Framework\View\Layout\ReaderPool;
2424
use Psr\Log\LoggerInterface as Logger;
25+
use Magento\Framework\View\Layout\Reader\Context;
2526

2627
/**
2728
* Layout model
@@ -375,13 +376,41 @@ public function generateElements()
375376
);
376377

377378
\Magento\Framework\Profiler::start('generate_elements');
378-
$this->generatorPool->process($this->getReaderContext(), $generatorContext);
379+
380+
$isolatedReaderContext = $this->createIsolatedReaderContext();
381+
382+
$this->generatorPool->process($isolatedReaderContext, $generatorContext);
379383
\Magento\Framework\Profiler::stop('generate_elements');
380384

381385
$this->addToOutputRootContainers();
382386
\Magento\Framework\Profiler::stop(__CLASS__ . '::' . __METHOD__);
383387
}
384388

389+
/**
390+
* Create isolated reader context to prevent race condition
391+
*
392+
* @return Context
393+
*/
394+
private function createIsolatedReaderContext(): Context
395+
{
396+
$originalContext = $this->getReaderContext();
397+
398+
$originalScheduledStructure = $originalContext->getScheduledStructure();
399+
$isolatedScheduledStructure = new Layout\ScheduledStructure(
400+
$originalScheduledStructure->__toArray()
401+
);
402+
403+
$originalPageConfig = $originalContext->getPageConfigStructure();
404+
$isolatedPageConfig = new \Magento\Framework\View\Page\Config\Structure();
405+
$isolatedPageConfig->populateWithArray($originalPageConfig->__toArray());
406+
407+
// Create new reader context with isolated structures
408+
return $this->readerContextFactory->create([
409+
'scheduledStructure' => $isolatedScheduledStructure,
410+
'pageConfigStructure' => $isolatedPageConfig
411+
]);
412+
}
413+
385414
/**
386415
* Add parent containers to output
387416
*

lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,7 @@ public static function isCacheableDataProvider(): array
897897
*/
898898
public function testGenerateElementsWithoutCache(): void
899899
{
900-
$this->readerContextFactoryMock->expects($this->once())
900+
$this->readerContextFactoryMock->expects($this->atMost(2))
901901
->method('create')
902902
->willReturn($this->readerContextMock);
903903
$layoutCacheId = 'layout_cache_id';
@@ -952,6 +952,11 @@ public function testGenerateElementsWithoutCache(): void
952952
$this->layoutScheduledSructure->expects($this->any())
953953
->method('__toArray')
954954
->willReturn($layoutScheduledStructureData);
955+
956+
// Ensure __toArray returns valid data for defensive copying
957+
$this->pageConfigStructure->expects($this->any())
958+
->method('__toArray')
959+
->willReturn($pageConfigStructureData);
955960
$data = [
956961
'pageConfigStructure' => $pageConfigStructureData,
957962
'scheduledStructure' => $layoutScheduledStructureData,
@@ -999,7 +1004,7 @@ public function testGenerateElementsWithCache(): void
9991004
$xml = simplexml_load_string('<layout/>', Element::class);
10001005
$this->model->setXml($xml);
10011006

1002-
$this->readerContextFactoryMock->expects($this->once())
1007+
$this->readerContextFactoryMock->expects($this->atMost(2))
10031008
->method('create')
10041009
->willReturn($this->readerContextMock);
10051010
$themeMock = $this->getMockForAbstractClass(ThemeInterface::class);
@@ -1035,6 +1040,14 @@ public function testGenerateElementsWithCache(): void
10351040
$this->layoutScheduledSructure->expects($this->once())
10361041
->method('populateWithArray')
10371042
->with($layoutScheduledStructureData);
1043+
1044+
// Ensure __toArray returns valid data for defensive copying
1045+
$this->layoutScheduledSructure->expects($this->any())
1046+
->method('__toArray')
1047+
->willReturn($layoutScheduledStructureData);
1048+
$this->pageConfigStructure->expects($this->any())
1049+
->method('__toArray')
1050+
->willReturn($pageConfigStructureData);
10381051
$data = [
10391052
'pageConfigStructure' => $pageConfigStructureData,
10401053
'scheduledStructure' => $layoutScheduledStructureData,

0 commit comments

Comments
 (0)