Fragment caching is useful for caching HTML snippets that are expensive to generate and exist in multiple places on your site. It’s like full page HTML caching, but more granular, and it speeds up dynamic views.
I’ve been using this fragment caching class for a few years now. I optimized it around ease of implementation. I wanted, as much as possible, to be able to identify a slow HTML-outputting block of code, and just wrap this code around it without having to refactor anything about the code inside.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Usage: | |
$frag = new CWS_Fragment_Cache( 'unique-key', 3600 ); // Second param is TTL | |
if ( !$frag->output() ) { // NOTE, testing for a return of false | |
functions_that_do_stuff_live(); | |
these_should_echo(); | |
// IMPORTANT | |
$frag->store(); | |
// YOU CANNOT FORGET THIS. If you do, the site will break. | |
} | |
*/ | |
class CWS_Fragment_Cache { | |
const GROUP = 'cws-fragments'; | |
var $key; | |
var $ttl; | |
public function __construct( $key, $ttl ) { | |
$this->key = $key; | |
$this->ttl = $ttl; | |
} | |
public function output() { | |
$output = wp_cache_get( $this->key, self::GROUP ); | |
if ( !empty( $output ) ) { | |
// It was in the cache | |
echo $output; | |
return true; | |
} else { | |
ob_start(); | |
return false; | |
} | |
} | |
public function store() { | |
$output = ob_get_flush(); // Flushes the buffers | |
wp_cache_add( $this->key, $output, self::GROUP, $this->ttl ); | |
} | |
} |
Implementation is pretty easy, and you can reference the comment at the start of the code for that. The only thing to consider is that any variables that alter the output need to be build into the key. It should also be noted that this code assumes you have a persistent object cache backend.
Errrr…. where’s the code?
In the post. Are you reading this on a Blackberry or something!?
Nope. On the web. http://cl.ly/image/2Q2H220U2t29
Ugh. WordPress.com embed failure I guess. Will add a link.
had the same issue. sweet. thanks mark!
It’s a script — maybe GitHub was/is having issues for you two.
Ironically, I see it on my iOS Safari (but the embed isn’t responsive so it scales outside the viewport) http://cl.ly/image/2m0x0B0G340V
Is there any particular reason to not use transients to eliminate this requirement?
I wonder the same thing. From the codex:
“Use the Transients API instead of these functions if you need to guarantee that your data will be cached. If persistent caching is configured, then the transients functions will use the wp_cache functions described in this document. However if persistent caching has not been enabled, then the data will instead be cached to the options table.”
Doesn’t this basically move a step above the wp_cache* functions and make the correct decision between database and memory for us based on the presence or absence of an object caching plugin?
Well, maybe. The number of things you’re storing would be one reason, and the possibility of not accessing items again would be another. DB transients are not, to my knowledge, actively garbage collected. So if you have some variable cache key portion that may eventually roll over and not be used again, you could end up with a ton of crud in your options table. All object cache backends I know of have some sort of garbage collection, even if it’s just triggered on hitting the max cache size.
Wouldn’t you consider set_transient’s ‘expiration’ parameter a form of garbage collection? Even if you didn’t, it’d be pretty easy to hook into cron to collect leftovers considering you know every transient key that you’ve set. Pardon me if I’m totally off, that’s just how I would look at it.
Database-stored transients are indeed not garbage collected at present. So if you use variable keys, then you can unwittingly grow your options table very large indeed.
The solution to this is simply to not use variable keys. Not a perfect solution, I grant you, but the use of variable keys is pretty edge-casey anyway, I think.
> The solution to this is simply to not use variable keys. Not a perfect solution, I grant you, but the use of variable keys is pretty edge-casey anyway, I think.
I learnt for the first time just now that variable keys aren’t garbage-collected. I’d been using them all the time, to hand out unique, automatically expiring download URLs. My options table was growing by 40,000 entries a week! I’ve used transients as expiring access tokens on lots of sites, so this discovery is a bit worrying!
This is brilliant. I’m not sure what dynamic parts of my sites would benefit from it though. I tend to “full-page” cache everything for an hour, regardless of how dynamic it might be. Of course, I’m using the APC object cache and that helps a lot with the first visitor problem.
Typically things like footers and menus are good candidates. Or, say you have some “related posts” function that isn’t doing its own caching. I just inject some timers throughout and look for the slow parts.
@markjaquith You wrote “I just inject some timers throughout and look for the slow parts.” Would you provide some examples of how this is accomplished? Or, if there’s a tutorial you can point to, that would be appreciated.
We’ve done something very similar to this in Pods:
http://podsframework.org/docs/pods-view/
I call it partial page caching, where it actually does what get_template_part does, but caches it too. I find it much easier to work with than output buffering on it’s own. I’ll think about adding this kind of output() / store() functionality too, looks really quite useful for conditional PHP where it gets expensive.
Thanks Mark. This is a handy little class you’ve made here. I usually write this sort of stuff from scratch each time, but I’ll try to remember to use this from now on.
You post often, but when you do it is always guaranteed to be top notch 🙂
I already tweeted this, but I just wanted to share my fork. It seems a simpler API than instantiating a class would be to use a wrapper function for a closure:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
gistfile1.php
hosted with ❤ by GitHub
WordPress menus was what made me to come up with something similar. I have since submitted a bug report with a suggested fix/patch and written a blog post explaining the problem. Would be great to hear what you think.
I’m not too familiar with the wordpress codebase, would this be easy to port to vanilla php?
It would require some sort of fragment caching library to be included. There’s probably something out there for this already rather than you needing to adapt Mark’s code.
One of your link building strategies that you can use, to acquire good quality links from other
sites, is definitely the link wheel strategy. It is a pay per sales affiliate program
which is more transparent and risk free to partners. Read
on to see if the Schwinn Joyrider Jogging Stroller
is able to really provide what the hardcore joggers
need in their jogging strollers.
This comment seems relevant… 😉
What’s health care your male score?
Reblogged this on Pixline.