“Website Speed Part 3 – Caching WordPress” plus 1 more - Speckyboy Design Magazine Feed |
| Website Speed Part 3 – Caching WordPress Posted: 14 Dec 2011 12:09 AM PST As so many people have WordPress installed, I thought I would share with you a key technique integral to WordPress that will help your website speed. W3 Total Cache is a dream plugin for achieving caching, I would recommend it to anybody. Yet there are times when you do not want to cache a whole page for long periods, but to cache sections of it so that a page rebuild is not as costly in server time. For example I had a site where they did not want caching on the homepage as a whole as they wanted to have the following features :
It was chosen that a “sub caching” (to use the customers preferred term) strategy would be best. Now you understand the scene of what we need to achieve, a cached homepage which could have items within it updated regularly, that did not use the page cache of W3TC. To achieve all of what the client wanted, I needed to use the Transients API for WordPress WordPress Transients APIThe WordPress transients API is a simple set of three commands that work very much like Options API, but when a timer that you set runs out, the transient is automatically deleted. Inside a transient you can save any kind of data you like. It can be an array, object or just some text, it really does not matter what you store in there as WordPress takes care of it by serializing it as necessary. What the Transients API also does is automatically save the transient in the quickest responding place on the server. This could be in the database, on the disk or in memcache depending on your servers configuration. Here is the basic syntax for getting and setting a transient: <?php $value = get_transient( 'value' ); if ( false === $value ) { // if transient not set, do this! // create the data that needs to be saved. $value = 1; // save the newly created transient value // 60 seconds * 60 minutes * 24 hours = 1 day set_transient('value', $value, 60*60*24); } ?> Notice how they work in tandem with each other. If there is nothing set in the transient called ‘value’ then the code knows to set a new ‘value’ from the code inside the if statement. As you can see, all we do to set a transient is use Using transients on a single loop inside WordPressUnless you are an ultra busy site with 100′s of content creators, you can be pretty sure that the loop that shows your posts in your blog does not change that often. Be that days or only minutes, caching will speed up the loop, as the server will not have to ask any complex SQL queries, but a simple single request to get the pre-found data, a much quicker and less intensive thing to do. <?php $loop = get_transient( 'loop' ); if ( false === $loop ) { // Show the last 100 tweets from the custom post type tweets. $query = array('post_per_page' => 100, 'post_type' => 'tweets', 'post_status' => 'publish' ) ; $loop = new WP_Query($query); // transient set to last for 1 hour set_transient('loop', $loop, 60*60); } // do normal loop stuff if ($loop->have_posts()) : while ($loop->have_posts()) : $loop->the_post(); // show content or whatever you like the_content(); endwhile;endif; ?> The above query for the 100 tweets from the custom post type of tweets will now be saved inside the transient called ‘loop’ A Question of Data or InformationUsing the above transient we are dealing with Data, or un-formatted information. This is all fine, and works just great, but you could take things a little further. Instead of manipulating the data every time that the page is loaded, it can be better to add all of the HTML to the loop and then save it to the transient so that it is even quicker to load. As shown in the below widget, all of the widget is built as a full HTML section before it is saved. This is my preferred method of implementation as there is less processing to do. Caching Widgets with transientsMany widgets show content that hardly ever changes, for example categories on the site, or pages on the site. It’s only really things like latest comments that tend to change often, and even then it’s doubtful that would be more than once per minute. So even with the more active widgets there is space for caching, let alone the sedentary ones. If you have a busy site for some reason, that could be thousands of hits inside that minute that take up unnecessary processing time to check again and again all of the widgets and their output. Caching widgets is pretty easy, here is an example widget for displaying a table of events from a custom post type called ‘ak_events’. class show_ak_events_Widget extends WP_Widget { function show_ak_events_Widget() { /* Widget settings. */ $widget_ops = array( 'classname' => 'ak-events', 'description' => 'Shows events in a table' ); /* Widget control settings. */ $control_ops = array( 'width' => 300, 'height' => 350, 'id_base' => 'ak-events' ); /* Create the widget. */ $this->WP_Widget( 'ak-events', 'Show Events', $widget_ops, $control_ops ); } function widget( $args, $instance ) { extract( $args ); // get cache if it exists // $widget_id comes from the widget $args->widget_id and is the widgets unique ID $output = get_transient('events'.$widget_id); // if no $output do stuff inside this if statement if ( $output === false ) { // set the title variable $title = apply_filters('widget_title', $instance['title'] ); // standard opening of widget $output = $before_widget; // if a title exists add it to the top of the widget $output .= ( !empty( $title ) )? $before_title . $title . $after_title : "" ; // Create query arguments for WP_Query to use $widgetargs = array( 'posts_per_page'=>'-1', 'post_type'=>'ak_events', 'post_status'=>'publish' ); // WP_Query sets up a loop query $query = new WP_Query( $widgetargs ); // create the opening table and top row $output .= "<table><tr><th>Event Name</th><th>Information</th></tr>"; // If the WP_Query has results send them through the loop if ($query->have_posts()) : while ($query->have_posts()) : $query->the_post(); $output .= "<tr><td>" . get_the_title() . "</td><td> " . get_the_excerpt() . " </td></tr>"; endwhile;endif; // close the table $output .= "</table>"; // close widget properly $output .= $after_widget; // save $output as a transient and set it to be 60 seconds * 5 = 5 minutes. // set_transient( 'events'.$widget_id, $output, 60*5 ); } echo $output; } function update( $new_instance, $old_instance ) { // save form data $instance = $old_instance; $instance['title'] = $new_instance['title']; // delete the transient so the new title setting is used delete_transient('events'.$this->id); return $instance; } function form( $instance ) { $defaults = array( 'title'=>'' ); $instance = wp_parse_args( (array) $instance, $defaults ); ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"> <?php _e('Title:','proving-ground'); ?> </label> <input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" value="<?php echo $instance['title']; ?>" style="width:95%" /> </p> <?php } } As you can see, all of the loop and processing activity takes place inside the if statement that runs when the widget is called. The other thing to notice is that there is also The bit that makes this work is really the ‘events’.$widget_id bit, as that assigns a unique transient name to each instance of the widget that is loaded. So the widget used in one sidebar is cached separately from the same instance of the same widget . Using caching for theme optionsTheme options are more and more common inside WordPress themes. They hold things like the settings for the theme which are applied every time the site it loaded. This is fine, but it is requested from the database each time, as that is the way that If you use Here is a simple little function I made to manage this for me, whenever I need to <?php function my_get_cache_option($option_name = 'ThemeAdminOptions' ){ // get wanted transient $value = get_transient( $option_name ); // check if it has any content if(false === $value){ // if no content in the transient get new copy of wanted option $value = get_option( $option_name ); // set new transient with a refresh of 1 day set_transient( $option_name, $value, 60*60*24 ); } // return the transient $value or newly created $value return $value; } ?> Personally I setup default of ‘ThemeAdminOptions’ and use that for my theme options, but if I want to get a different transient or option I use it like this: <?php // get cached option $options = my_get_cache_option('WantedOptionName'); // do code with options array/object/string // example code echo $options['google_analytics_id']; ?> If you are going to use this solution, don’t forget to use <?php // update the option as normal update_option('WantedOptionName', $value ); // delete the transient that may be set delete_transient('WantedOptionName'); ?> Using transients for timed eventsOne of the great benefits of the Transients API is the ability to setup very simple timed events that perform functions for you. Say you wanted to get an RSS feed every minute from a stock trading company, it could be easily achieved by using the following function and call: <?php function setup_timed_event(){ // get value if set; $value = get_transient('run_batch'); // if not set run if statement; if($value === false){ // run every time the timer runs out do_minute_batch(); // set any value to $value; $value = 'done'; // setup transient to last for 60 seconds; set_transient('run_batch', $value, 60); } } function do_minute_batch(){ // // code to run in batch here that calls the RSS feed. // } // // use an action to call the timed event every time the init is called. add_action('init','setup_timed_event'); ?> It is not strictly true that it will run every 1 minute, as if the server performs no activity of any description for more than a minute, it will not run, but will run on the next activity whatever that is. Other activities that can be cached with transientsThe list is endless of things that you can make quicker on a website by using the transients API. Anywhere you have to process data, you can use it. If you write a twitter plugin, you could use it to save the Twitter feed and only update when it is deleted. Or maybe you have an Amazon store that you want to setup with caching for small periods, this is perfect and simple to use. In fact anywhere you need to get data from an external source, cache it with the transients API. There is nothing to stop you taking caching to the nth degree with multi loops with differing cache timers based on importance of content, or make sure that every single widget you have caches for the right content lifetime. In fact there is nothing to stop you giving the caching timeout control to the widget administrator by having a field that is the number of seconds or the calculation that sets the time period of the What about things that echo to the screen?In WordPress there are quite a few things that automatically echo to the screen, such as Or maybe a plugin developer has added an action or filter to the command, for example Both of these examples call for a slightly different way of saving the data in to the transient. We now have to use an <?php $loop_output = get_transient( 'loopOutput' ); if ( false === $loop_output ) { // Show the last 100 published posts. $query = array('post_per_page' => 100, 'post_status' => 'publish' ) ; // run the query $loop = new WP_Query($query); // start the output buffer to save contents of loop ob_start(); // do normal loop stuff if ($loop->have_posts()) : while ($loop->have_posts()) : $loop->the_post(); // show content or whatever you like ?><h1><?php the_title() ?></h1><?php the_content(); endwhile;endif; // save the output buffer contents in a variable $loop_output = ob_get_contents(); // clean the buffer as we will be using the variable from now on ob_end_clean(); // transient set to last for 1 hour set_transient('loopOutput', $loop_output, 60*60); } // output the new created loop if loop content does not exist. echo $loop_output; ?> Rounding upUsing the transients API can significantly increase the speed of your WordPress plugin, theme or widget. It really is worth considering making this standard practice when creating for WordPress in future. More from the Website Speed series…Website Speed Part 1: Write More Efficient CSS → |
| JustProto Competition Winners Announced Posted: 13 Dec 2011 08:39 AM PST Last week we published the competition to two win 3 year-long licenses to JustProto, worth $588 each. The competition has ended today and it is now time to announce the winners. Thanks to everybody for all the comments. The WinnersAll winners should have by now received their winner emails, if not please get in touch with us here. Winner Name: Bilal Shaheen Winner Name: Martin Winner Name: Alberto Attia Message from the JustProto teamLike many products, JustProto was born out of frustration with existing solutions on the market. DeSmart (our company) has been around for eight years and we've worked on a ton of projects, large and medium sized. Like most of you, we usually just make do with whatever works best in a situation – after all, nothing's perfect. Still, when we looked around the wireframing world, we realized we weren't the only team that needed a fully collaborative, affordable online solution to our prototyping needs. We also realized that there really wasn't one that ticked all the boxes for us. So, in 2008 our CEO, Piotr Duszynski, and Wojciech Skowronek, our lead programmer, knuckled down and started building JustProto. Around nine months later at the TriCity BarCamp on January 28, 2009, DeSmart gave birth to a happy, healthy baby app that we named JustProto. We brought JustProto into the world to help ourselves and others like us – and we hope it can help you too! |
| You are subscribed to email updates from Speckyboy Design Magazine To stop receiving these emails, you may unsubscribe now. | Email delivery powered by Google |
| Google Inc., 20 West Kinzie, Chicago IL USA 60610 | |
Keine Kommentare:
Kommentar veröffentlichen