From f30864ebac6f41d499a8d8f7ed1f30214f11fe55 Mon Sep 17 00:00:00 2001 From: Igor Wulff Date: Wed, 30 Jul 2025 18:03:55 +0200 Subject: [PATCH] Cache the results of isCacheable calls to improve performance This function call is relatively expensive when $elements has the full layout in it. Especially the PageCache module is calling this function a lot from the following places: - Magento\PageCache\Model\Layout\LayoutPlugin - Magento\PageCache\Observer\ProcessLayoutRenderElement On my local machine I've seen the impact to be anywhere between 20-40 milliseconds, when the page has been loaded a few times to warm up cache (config, block, layout, etc...), but FPC is circumvented. We could also opt to move this to the PageCache module, but I think it is ok to optimize this within the Layout class within the framework. --- .../Magento/Framework/View/Layout.php | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Layout.php b/lib/internal/Magento/Framework/View/Layout.php index 3bc42f846a819..5851bb4be2d58 100644 --- a/lib/internal/Magento/Framework/View/Layout.php +++ b/lib/internal/Magento/Framework/View/Layout.php @@ -193,6 +193,11 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra */ private ResponseHttp $response; + /** + * Property used to cache the results of the isCacheable() method. + */ + private bool|null $isCacheableCache = null; + /** * @param ProcessorFactory $processorFactory * @param ManagerInterface $eventManager @@ -1138,18 +1143,22 @@ protected function _prepareMessageGroup($messageGroups) */ public function isCacheable() { - $this->build(); - $elements = $this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'); - $cacheable = $this->cacheable; - foreach ($elements as $element) { - $blockName = $element->getBlockName(); - if ($blockName !== false && $this->structure->hasElement($blockName)) { - $cacheable = false; - break; + if (!isset($this->isCacheableCache)) { + $this->build(); + $elements = $this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'); + $cacheable = $this->cacheable; + foreach ($elements as $element) { + $blockName = $element->getBlockName(); + if ($blockName !== false && $this->structure->hasElement($blockName)) { + $cacheable = false; + break; + } } + + $this->isCacheableCache = $cacheable; } - return $cacheable; + return $this->isCacheableCache; } /**