Categories


Archives


Recent Posts


Categories


Magento 2 Full Page Caching Code Points

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

Magento 2, even community edition, comes with a “full page cache” out of the box. This caching system lives near the top of Magento’s application dispatch. The frontcontroller will skip action controller dispatch if it can find a cached version of the page.

You can turn this feature off in the backend at

System -> Cache Managment -> Page Cache

The full page cache is implemented via a Magento 2 plugin on the front controller

#File: app/code/Magento/PageCache/Model/App/FrontController/BuiltinPlugin.php
public function aroundDispatch(
    MagentoFrameworkAppFrontControllerInterface $subject,
    Closure $proceed,
    MagentoFrameworkAppRequestInterface $request
) {
    if (!$this->config->isEnabled() || $this->config->getType() != MagentoPageCacheModelConfig::BUILT_IN) {
        return $proceed($request);
    }
    $this->version->process();
    $result = $this->kernel->load();

    if ($result === false) {
        $result = $proceed($request);
        if ($result instanceof ResponseHttp) {
            $this->addDebugHeaders($result);
            $this->kernel->process($result);
        }
    } else {
        $this->addDebugHeader($result, 'X-Magento-Cache-Debug', 'HIT', true);
    }
    return $result;
}    

The plugin’s logic is implemented in this MagentoFrameworkAppPageCacheKernel class’s process method

#File: lib/internal/Magento/Framework/App/PageCache/Kernel.php
public function process(MagentoFrameworkAppResponseHttp $response)
{
    if (preg_match('/public.*s-maxage=(d+)/', $response->getHeader('Cache-Control')->getFieldValue(), $matches)) {
        $maxAge = $matches[1];
        $response->setNoCacheHeaders();
        if ($response->getHttpResponseCode() == 200 && ($this->request->isGet() || $this->request->isHead())) {
            $tagsHeader = $response->getHeader('X-Magento-Tags');
            $tags = $tagsHeader ? explode(',', $tagsHeader->getFieldValue()) : [];

            $response->clearHeader('Set-Cookie');
            $response->clearHeader('X-Magento-Tags');
            if (!headers_sent()) {
                header_remove('Set-Cookie');
            }
            $this->cache->save(serialize($response), $this->identifier->getValue(), $tags, $maxAge);
        }
    }    
}

And, finally, the Cache-Control header that process looks for is set in another plugin.

#File: app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php
public function afterGenerateXml(MagentoFrameworkViewLayout $subject, $result)
{
    if ($subject->isCacheable() && $this->config->isEnabled()) {
        $this->response->setPublicHeaders($this->config->getTtl());
    }
    return $result;
}

Which, at the time of this writing, checks the layout object’s isCacheable method

#File: lib/internal/Magento/Framework/View/Layout.php
public function isCacheable()
{
    $this->build();
    $cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'));
    return $this->cacheable && $cacheableXml;
}

So, what this means is

  1. If your request doesn’t use the layout object (i.e. JSON responses) your request skips the full page cache
  2. If your request does use the layout object (a normal page factory response), but there’s a single layout block with a cacheable=false attribute, Magento will skip full page caching.

Copyright © Alan Storm 1975 – 2017 All Rights Reserved

Originally Posted: 8th December 2015