CacheHelper replacement - markstory/cakephp GitHub Wiki

This page is not complete.

Full page caching is useful for optimizing the performance of content based sites like news sites or e-commerce sites. CakePHP has historically provided this functionality through CacheHelper.

Problems with CacheHelper

While CacheHelper has proven useful it has many problems:

  • It uses non-standard comments to mark uncached regions. The regions between these comments are left uncached and execute PHP code when cache files are loaded. This makes caching more difficult as the entire view context and request object need to be serializable.
  • The helper cannot effectively cache views without a layout.
  • The helper cannot effectively preserve all the response headers.
  • The solution CacheHelper provides leaves no 'room to grow'. You cannot easily replace it with a beefier system as it is completely custom.

FullPage cache Plugin

I think that ESI(Edge Side Includes) will provide a robust, flexible and scalable solution to full page caching. Using an ESI based model allows applications to replace a PHP implementation with full fledged servers like Varnish. Only the esi:include tag is necessary, as the other parts of the spec pertain to CDN features.

There is no solid reason why full-page caching should be included in CakePHP core, when all the features it needs are available to plugins. Making full page caching a separate plugin will let us keep CakePHP smaller and iterate more quickly on the full page caching features.

Edge side includes

ESI(Edge Side Includes) are a standardized way to insert dynamic content into cached pages. By adopting ESI CakePHP users will still be able to mark regions of content as dynamic in their cached pages as they did with <!--nocache-->.

The ESI implementation in CakePHP could be built as a Helper and DispatcherFilter. The helper would make it easier for developers to generate ESI tags without having to know the details of ESI tags.

$this->Esi->render(['controller' => 'News', 'action' => 'latest']);

The above would resolve the route, and generate an ESI tag for the /news/latest URL. In addition to using the Helper, developers could enable the PageCache.ReverseProxyFilter which will resolve & replace <esi:include /> tags into their respective blocks of content. In addition, the ReverseProxyFilter will provide a basic caching reverse proxy for development environments, or hosting where varnish is not available.

DispatcherFactory::add('PageCache.ReverseProxy');

Would enable ESI tag replacement, and basic reverse proxying. Using configuration options you can customize how the ReverseProxy works:

  • esi - set to false to disable ESI processing.
  • esiIterations - the maximum number of times to run ESI replacements.
  • cacher - provide a CacheEngine instance to use. By default a FileEngine instance will be created and used.
  • defaultDuration - The default duration to store responses that lack caching headers.

The reverse proxy will only handle GET and HEAD requests. All other requests will not be handled by the ReverseProxy.

ESI tag replacement

When a request is received that has a cached response, the response body will be scanned for <esi:include /> tags and replace those. When processed each esi:include tag will:

  1. Make a sub-request. The sub-request will not have additional headers set, and will be inserted as rendered.
  2. The esi tag will be replaced with the content from the sub-request.
  3. The resulting page will be scanned for additional ESI directives.
  4. If additional ESI tags are found the process will be repeated up to 5 times.

Basic HTTP reverse proxy

Because ESI requires a reverse proxy to be built, we'll need a very basic reverse proxy implementation to afford scenarios where Varnish or Squid is not available. This reverse proxy will need to support basic caching scenarios like:

  • Using response headers to create cache file durations. If a response does not include caching headers a default of 1 minute would be used.
  • Allowing clients to request fresh data by sending Cache-Control: no-cache in the headers.