The shiny new dynamic content in WP Super Cache

With the next release of WP Super Cache in a day or two the long awaited move away from mfunc, mclude and friends will be complete.

This means that if you have been using mfunc, mclude or dynamic-cached-content the dynamic portions of your sites will go blank if you upgrade WP Super Cache without updating that dynamic code. This may seem complicated but there’s an example script included and detailed explanations below. A lot of effort was made to make this backwards compatible but unfortunately it wasn’t possible.

In their place is a new cacheaction filter called wpsc_cachedata and it’s sidekick wpsc_cachedata_safety. In the future when a site owner using WP Super Cache wants to make part of their website dynamic they will use those filters to modify pre-defined text strings and replace them with the data they want displayed to the end user. There’s an example script ready to be ripped apart to help you figure all this out.

There are two ways of using this:

  1. The dynamic content is the return value of a simple process, be it date() or any of the numerous get_*() functions in WordPress. That data can simply be slotted in place of the pre-defined text mentioned above.
  2. The more difficult bit comes when you need to use an output buffer to collect the data for display. Due to a limitation in PHP it’s impossible to run an output buffer in the callback function of another output buffer, which is when the wpsc_cachedata filter runs. We need to collect that data before the callback function executes.

The first way above is easy. Simply add a text string of random characters to your theme where you want the dynamic content to appear, then hook a function on to the wpsc_cachedata filter to str_replace() it with your dynamic content. These functions in this script do that:

  • dynamic_cache_test_filter()
  • dynamic_cache_test_template_tag()
  • dynamic_cache_test_init()

You’ll have to hook on to the wpsc_cachedata_safety action and return the numeral 1 to actually run the wpsc_cachedata filter. This is a fail safe used by the plugin to make sure things are ok when the filter runs.

Unfortunately if you want to use an output buffer it’s a lot harder. As stated above an output buffer can’t run in the callback function of another output buffer. This means you have to generate your dynamic content earlier in the PHP process.
This could be as easy as calling your dynamic content function (dynamic_output_buffer_test() in the example script) from the wp_footer action, or calling it from any action before shutdown, whichever is appropriate. You’ll also need to add a template tag of your own choosing to your theme.
Your dynamic content function will run just fine for cached pages, so after the new page has run it store the output in a constant or global variable that the same function can look for when the wpsc_cacheaction runs it. If it finds that information, it can do the search and replace of your template tag without running the output buffer again.
Some pages won’t run the dynamic content function however. This includes feeds and sitemaps. Those pages will generate a PHP error because the output buffer will try to run in the callback!

“PHP Fatal error: ob_start(): Cannot use output buffering in output buffering display handlers in…”

To stop that happening you must check that there’s text to shove in the cached page. That’s what happens in the function dynamic_output_buffer_test_safety() in the example script. It fires on the wpsc_cachedata_safety filter and returns the numeral 1 if successful.

Anatomy of an Output Buffer

I’m going to try and explain the output buffer functions in more detail here. They’re only example functions and if you spot a bug or can suggest improvements please do!

define( 'DYNAMIC_OUTPUT_BUFFER_TAG', '' ); // CHANGE THIS!

This is the string you add to your theme where your dynamic content will appear. The plugin will replace this string with the code generated by your dynamic content function.

function dynamic_output_buffer_test( &$cachedata = 0 ) {
	if ( defined( 'DYNAMIC_OB_TEXT' ) )
		return str_replace( DYNAMIC_OUTPUT_BUFFER_TAG, DYNAMIC_OB_TEXT, $cachedata );
	ob_start();
	// call the sidebar function, do something dynamic
	echo "<p>This is a test. The current time on the server is: " . date( 'H:i:s' ) . "</p>";
	$text = ob_get_contents();
	ob_end_clean();

	if ( $cachedata === 0 ) // called directly from the theme so store the output
		define( 'DYNAMIC_OB_TEXT', $text );
	else // called via the wpsc_cachedata filter. We only get here in cached pages in wp-cache-phase1.php
		return str_replace( DYNAMIC_OUTPUT_BUFFER_TAG, $text, $cachedata );

}
add_cacheaction( 'wpsc_cachedata', 'dynamic_output_buffer_test' );
  • 1. dynamic_output_buffer_test() is the function that will generate the dynamic content inserted into the cached page.
  • 2-3. DYNAMIC_OB_TEXT stores the previous output of this function. If it’s set that means we’re in the wpsc_cachedata filter and should insert it into the cached page and return it.
  • 4-8. This creates the output buffer, echoes a string with the current time and copies the buffer into a variable called $text before closing the output buffer.
  • 10-13. This is how we decide to return data. If $cachedata is 0 that means this function was called from the theme so we should define the constant DYNAMIC_OB_TEXT for later use. Otherwise, we must be dealing with an already cached page so insert the dynamic content into the page and return it.
  • 16. Add the dynamic_output_buffer_test() function to the wpsc_cachedata action. “add_cacheaction” is used as it loads before the regular WordPress action code loads.
function dynamic_output_buffer_test_safety( $safety ) {
	if ( defined( 'DYNAMIC_OB_TEXT' ) )
		return 1; // ready to replace tag with dynamic content.
	else
		return 0; // tag cannot be replaced.
}
add_cacheaction( 'wpsc_cachedata_safety', 'dynamic_output_buffer_test_safety' );
  • 1-6. dynamic_output_buffer_test_safety() is a function that checks if dynamic content was generated correctly. If that constant is not defined the output buffer will run within the callback function of the main WP Super Cache output buffer and generate a PHP error.
  • 7. Add the wpsc_cachedata_safety() function to the dynamic_output_buffer_test_safety action.

sidebar.php

As an example, if I wanted to display my dynamic content in the sidebar of my blog I would load sidebar.php in my theme’s directory and add the following code.

if ( function_exists( 'dynamic_output_buffer_test' ) )
    dynamic_output_buffer_test();
?>WORDPRESS ROCKS THE WORLD<?php

I had previously edited the example script, uncommented it and changed the appropriate tag:

define( 'DYNAMIC_OUTPUT_BUFFER_TAG', 'WORDPRESS ROCKS THE WORLD' ); // CHANGE THIS!

Final Note and Download Link

Please grab the development version of the plugin and try it on a staging server before you put it live. Feedback would be appreciated!

Warning! Keep the tags you use secret. You don’t want someone leaving a comment on your blog with that string! Do not use the same function names or constant names as in this post or example script. They’re in this very public post. Someone is bound to use them and cause you problems when you install their plugin.

Finally, barring any last minute major bugs this version of WP Super Cache will be released on Wednesday. Be careful upgrading. Pass the word around if you know someone is using mfunc as their site will stop working!


You might also like

If you like this post then please subscribe to my full RSS feed. You can also click here to subscribe by email. There are also my fabulous photos to explore too!

18 thoughts on “The shiny new dynamic content in WP Super Cache


  1. Hi Donnacha and a very big thank you for WP Super Cache and your continued support and improvements to it.

    1. To use these new functions do I need to run PHP mode or can I continue with mod_rewrite?
    2. I’m currently working on a site that I have painstakingly been converting all dynamic portions into ajax calls. Would you recommend that I try out these new functions or continue with creating ajax calls?

    Many thanks,

    wpmnger


    1. wpmnger – you’ve always needed to use PHP mode or legacy mode for the dynamic content functionality, so there’s no change there.

      I would say stick with the ajax calls or use a more granular caching system. A full page caching plugin like WP Super Cache or W3 Total Cache cache the whole page. If you use an object cache like memcached or any of the mysql caching plugins on wordpress.org it might be easier to keep parts of the page dynamic.
      Having said that, the filter based system here works well and I’m happier about it than the old eval() method which wasn’t that secure.


      1. Thanks Donncha!

        For this project I have to use a full page caching system as the server doesn’t support object caching. On other projects I use APC and memcache and am happy with the results.

        ajax it is!

        Thanks again,


  2. So just for my own clarity. If I use WP Super Cache in a std install, do not modify any of my theme and use the plugins settings the upgrade should be fairly straight forward right?


    1. Yes. If you don’t use the dynamic functionality then it’ll be a straight forward upgrade. It even has a few bug fixes and improvements too!


      1. Superb – upgrade went very smooth. However, when I tried to post to my site using Windows Live Writer I was getting back a 500 error on xmlrpc.php. Since one of my first steps to troubleshooting these types of things is to switch off recently update plugins. So after turning off the caching my post uploaded without issue to the site.

        Any idea how the two may be related?

        Thanks.

        Rich


  3. I added a new feature this evening. If wp-cache-phase1.php hasn’t been loaded when the settings page loads the plugin will recreate advanced-cache.php and fix the WPCACHEHOME constant in wp-config.php.

    It does check if caching is enabled as part of that check but if there’s an error in advanced-cache.php it should be caught!

    I’m nervous about tomorrow’s release. Getting people to test the plugin is hard and when a release is made suddenly tens of thousands of sites are running brand new code. *gulp*

  4. Pingback: WP Super Cache 1.4

  5. All you have to do is download the development version I’ve linked to above, but it doesn’t matter now as I released version 1.4 this morning. :)


  6. That’s annoying. Have you got PHP error logging enabled? Is there an error in your error_log? Did it take longer than usual to post or did the 500 error show immediately?

    If it took longer it could be because of deleting the category and tag archive pages.


  7. Hey Donncha, I was wondering that when I use the “mode_rewrite” cache and some pages don’t have super cache pages created. That’s not a problem for me, but I came up with an idea/question.

    Could I somehow say to the plugin that fallback to PHP-cache on this page “X”? Then I would use your new awesome wpsc_cachedata filter.


    1. The plugin should automatically fallback to PHP mode if the mod_rewrite rules have failed for some reason. Maybe use the debug log to figure out why it’s not working right?


      1. Ok, got it. :) The reason is because I’m using data outside wordpress via query parameters. eg. http://www.somedomain.com/vod/video_id=15.

        By the way it would very nice have some error filter when caching page, which would stop the process and no cache would be created if a “red” flag was issued through the error filter. Or is this allready implemented and I missed this one?


        1. There is a way. Have your code define the constant DONOTCACHEPAGE and the plugin won’t cache that page.

          The GET parameters might screw things up but give it a try anyway.

Leave a Reply

Loading Facebook Comments ...