Mittwoch, 14. Dezember 2011

“Website Speed Part 3 – Caching WordPress” plus 1 more - Speckyboy Design Magazine Feed

“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

Advertise here with BSA


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 :

  • • Rotating background image provided by php
  • • Alternating advertising scripting for comparison of suppliers
  • • A loop that updates every 5 minutes, along with others that updated hourly

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 API

The 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 set_transient() with the name of the transient, in this case ‘value’, with it’s $value being the data you want to save, and the last part being the calculation that would created the number of seconds that the transient should last for.

Using transients on a single loop inside WordPress

Unless 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 Information

Using 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 transients

Many 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 delete_transient() used inside the update function to delete the transient every time you update the widget on the admin screen.

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 options

Theme 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 get_option() works.

If you use get_transient() instead you might not be going to the database to request it. It’s possible it’s coming from memory cache or disk cache, which are quicker to access.

Here is a simple little function I made to manage this for me, whenever I need to get_option() I use my_get_cache_option() to get the WordPress option I want. The following code goes in your functions.php

 <?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 delete_transient() within the process of saving the updates on your admin pages. Normally you would just update the options, but as well as that you need to delete the transient that exists as your other code will pull the old data from the transient for up to 24 hours.

 <?php // update the option as normal update_option('WantedOptionName', $value );  // delete the transient that may be set delete_transient('WantedOptionName');  ?> 

Using transients for timed events

One 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 transients

The 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 widgets set_transient(). It really is endless where you can go with this.

What about things that echo to the screen?

In WordPress there are quite a few things that automatically echo to the screen, such as the_content(). Sometimes you want to use these items rather than the get equivalent get command, i.e. get_the_content(), as the formatting is already good without the need for extra manipulation.

Or maybe a plugin developer has added an action or filter to the command, for example the_content() could also have an action or filter added to it that appends share links and icons to the end of the content.

Both of these examples call for a slightly different way of saving the data in to the transient. We now have to use an ob_start(),ob_get_contents(), ob_end_clean() combination to save the data we are echoing to the screen and then save that as a variable as the transient

 <?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 up

Using 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 →
Website Speed Part 2: Working With and Optimizing Images for the Web →


Advertise here with BSA


JustProto Competition Winners Announced

Posted: 13 Dec 2011 08:39 AM PST

Advertise here with BSA


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 Winners

All winners should have by now received their winner emails, if not please get in touch with us here.

Winner Name: Bilal Shaheen
Comment: Comment URL

Winner Name: Martin
Comment: Comment URL

Winner Name: Alberto Attia
Comment: Comment URL

Message from the JustProto team

Like 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.

JustProto Homepage

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!


Advertise here with BSA


Keine Kommentare:

Kommentar veröffentlichen