Title: Building a simple Cache system for Wordpress 3.0
Slug: building-a-simple-cache-system-for-wordpress-3-0
Date: 2011-06-25 09:06:00
Author: Kartones
Lang: en
Tags: Development, PHP, Wordpress, Resources
Description: Building a simple cache system for Wordpress 3.0 to speed up loading times.

<p><strong>Note</strong>: This is "an english post version" of <a href="https://slides.kartones.net">the slides</a> I presented at the <a href="https://blog.kartones.net/post/mindcamp-3-0-and-lightweight-wordpress-cache-component">Mindcamp 3.0 event</a>. It has some differences as the slides have to be condensed, but the content is mostly the same.</p> <p> </p> <p>Wordpress is without any doubt the most used blogging platform on the net right now. It has a historial of both appraisals and bad feedback about being sometimes buggy or insecure, but the third version seems to be more at least more stable. As I have been working with PHP for more than two years, I decided it was time to start checking some "decent" source codes and see if things have improved from the terrible PHPNuke/PostNuke I knew years ago.</p> <p>I decided to go for building a small caching component to speed up loading times of a small wordpress blog I have mostly for experimentation.</p> <p> </p> <p>Wordpress <a href="http://codex.wordpress.org/Main_Page">has a nice documentation hub</a>, being specially critical (at least for newcomers like me) <a href="http://codex.wordpress.org/Function_Reference">the function reference list</a>.</p> <p>To assest how well (or wrongly) built Wordpress is, and in order to make my cache system as clean as possible, I decided to follow this restrictions:</p> <ul> <li>Only check the official documentation. I don't want to check 100 tutorials, if is not on the official doc, I assume can't be done.  *  </li><li>Try to only touch the plugins and/or themes "layers". I want to keep the code updateable instead of hacking inside Wordpress itself.</li></ul> <p>* I had to break this rule once to get the post Id for the single-page template, and I only found the solution via searching the net and finding this magic lines:</p> <p><font face="Courier New">global $wp_query;<br>$postID = $wp_query-&gt;post-&gt;ID;</font>  </p><p> </p> <p>The main idea behind any typical cache layer is to avoid touching the real data layer (usually the database), so I will show how to do the caching of a Page reducing the number of DB queries to as much as 2 one row queries (one to check for a cached version of content, optional second one to set/update that cached content). <br>To keep the DB as small as possible I will cache almost all the generated HTML content in a physical file.</p> <p>I decided to cache specific pages (quite easy as in my blog I don't have comments in them), the homepage (a must, landing page should be as fast as possible) and the sidebar's "recent posts" fragment. This way I have three different scenarios (special page, pages by Id and a DB query). </p> <p>Adapting this to the homepage, to a Post or to partial sections (for example the "recent posts" section on a sidebar) is trivial.</p> <p> </p> <p>After some basic research, this is how Wordpress renders any non-administration page:</p> <p><img title="Wordpress output flow" alt="Wordpress output flow" src="https://images.kartones.net/posts/kartonesblog/wordpress_output.png"></p> <p>The huge design FAIL here are those <font face="Courier New">echos</font>. Wordpress assumes so much that you're going to "just render the page" that it works by "rendering" everything directly to the output buffer. So we cannot fine-control that output (and I definetly <strong>not</strong> want to cache the whole HTML generated, I want to decide what and whatnot).</p> <p>Compare it with this approach:</p> <p><img title="Proposed cacheable output flow" alt="Proposed cacheable output flow" src="https://images.kartones.net/posts/kartonesblog/wordpress_cached_output.png"></p> <p>Here, everything writes to a "buffer" (a simple string variable), and then when we're finished, we just echo/output. This is much more extensible: Wanna filter certain things for mobile browsers? no problem; wanna build a web service-like architecture and return "preview" views? no problem either...</p> <p>And what matters to us now: caching the <font face="Courier New">$content</font> variable equals to caching all the heavy part of the rendering.</p> <p> </p> <p>We know what we want to cache (we'll come to some implementation caveats later), now let's see and build it:</p> <p>I will use just one query to the database, by using Wordpress <font face="Courier New">update_option()</font> and <font face="Courier New">get_option()</font> to insert and check the <strong>cache keys</strong>.</p> <p>I will use file storage for storing the <strong>cache values</strong> (simple HTML dumps). In memory (memcached like) would be faster, but still is way faster to read and output one file than parse multiple PHP files, access the DB, etcetera.</p> <p>I will support sub-keys like this syntax: <font face="Courier New">array( key =&gt; timestamp)</font></p> <p>And the code should work like this:<br><font face="Courier New">$cache-&gt;Get(Kache::CACHE_KEY_PAGES, $postID);<br>$cache-&gt;Set(Kache::CACHE_KEY_PAGES, $content, $postID);</font></p> <p> </p> <p>Basic methods are pretty easy:<br></p> <p><font face="Courier New">public function Refresh($cacheKey) {<br>  update_option($cacheKey, time());<br>}<br>public function Invalidate($cacheKey) {<br>  update_option($cacheKey, false);<br>}<br>public function Set($cacheKey, $cacheContent, $arrayKey = null) {<br>  if ($this-&gt;<strong>StoreContents</strong>($cacheKey, $cacheContent, $arrayKey)) {<br>    $this-&gt;Refresh($cacheKey);<br>  }<br>}</font>  </p><p>I won't go into details, but <font face="Courier New">StoreContents()</font> is the nexus cached data storage, so would be trivial to change my idea of saving to plain files to use memcached or any other system.  </p><p>  </p><p>The Get() method is responsible for checking cache expiration times so has a bit more of logic:  </p><p><font face="Courier New">public function Get($cacheKey, $arrayKey = null) {<br>  $content = false;<br>  $lastTime = get_option($cacheKey);<br>  if ($lastTime) {<br>    if (!self::$keysConfig[$cacheKey][0]) {<br>      $lastTime = (int) $lastTime;<br>      if (time() - $lastTime &lt;= self::$keysConfig[$cacheKey][1]) {<br>        $content = $this-&gt;<strong>GrabContents</strong>($cacheKey);<br>      }<br>    } else {<br>      // Cast to array of key=&gt;value<br>       ...<br>    }<br>  }<br>  return $content;<br>}</font>  </p><p>Once again, <font face="Courier New">GrabContents()</font> reads from file the cached content and could be modified easily.  </p><p>  </p><p>One problem that I found, and one of my main reasons to hate Wordpress, is it's shitty and non object-oriented design, which forced me to do this ugly methods to be able to plug them as hooks:  </p><p><font face="Courier New">function InvalidatePages()<br>{<br>    $cache = Kache::GetInstance();    //<br>    $cache-&gt;Invalidate(Kache::CACHE_KEY_PAGES);<br>}<br>function InvalidateAll() <br>{<br>    $cache = Kache::GetInstance();<br>    $cache-&gt;InvalidateAll();<br>}<br>add_action ('publish_post', 'InvalidateAll');<br>add_action ('deleted_post', 'InvalidateAll');<br>add_action ('post_updated', 'InvalidateAll');<br>add_action ('comment_post', 'InvalidatePages');<br>add_action ('deleted_comment', 'InvalidatePages');</font>  </p><p> </p> <p>Everything is done, except modifying the theme to output to a <font face="Courier New">$content</font> variable (the diagram above, remember?). This is the worst part of this caching solution, as it is not optimal, but as I restrained myself to only themes and plugins I didn't had any other choice than to modify the themes page.php file:</p> <p><font face="Courier New">$cache = Kache::GetInstance();<br>$content = $cache-&gt;Get(Kache::CACHE_KEY_PAGES, $postID);<br>if (!$content)<br>{<br>  // Store everything<br>  $content = '';<br>  $content .= '&lt;div id="container"&gt;&lt;div id="main"&gt;';<br>  ...<br>}<br>// Always output content, either retrieved or just generated<br>echo $content;<br></font><font face="Courier New">...</font>  </p><p>  </p><p>As probably is better to see the full picture, I recommend you to grab <a href="https://github.com/Kartones/Kache">the PHP source code</a> and give it a look.  </p><p>  </p><p>Another example of why WP is so badly coded is the inconsistencies it has in its methods:  </p><ul> <li>Some methods do echo by default and have a param to return data:<br><font face="Courier New">the_title_attribute('echo=0');</font>  </li><li>Other methods have one function to echo, other to return:<br><font face="Courier New">the_ID();<br>get_the_ID();</font>  </li><li>Other methods have incongruent and misleading names:<br><font face="Courier New">foreach((get_the_category()) as $category) { ... }</font>  </li><li>And finally, some methods directly don't support returning data and always echo it:<br><font face="Courier New">&lt;?php comments_template(); ?&gt;</font></li></ul> <p>Sweet uh?  </p><p>  </p><p><u>Conclussions</u>  </p><p>Probably touching the core of wordpress I can come with a fully automated caching solution, not only for HTML but easily also for DB queries, but that means forking and patching constantly, and as Wordpress has too frequent fixes and updates, I don't want to mess with it.  </p><p>Even with this solutions I got really nice speed improvements, lowering the rendering time to less than one second average; add some HTTP compression, JS optimizations and CSS sprites or embedded images, and you get really nice loading times in a simple self-hosted WP blog with all images from your domain :)  </p><p>  </p><p>Wordpress is the perfect example of "<strong>widely used does not mean well done</strong>": It works, it is really easy to use, but internally is a mess and aggregation of bad coding practices.</p>
