Date post: | 22-May-2015 |
Category: |
Documents |
Upload: | maor-chasen |
View: | 1,174 times |
Download: | 1 times |
Advanced CachingAn Introduction to
methods in WordPress
Maor Chasen
WP Developer @ illumineaCore contributor for 3.5Have plugins on WordPress.orgOpen Source FanaticDesign EnthusiastFlying kites
It's not scary.
It's fun.
1. It's easy2. Your site = faster3. Can save you $$4. It will knock
your socks off5. You'll sleep better
at night.
Why caching is good for you?A question you shouldn't ask your mom
What we will talk about● What is fragment / object caching
● Why caching is important
● How to cache
● What to cache
● When to cache
What we won't talk about● Server setups
● Caching plugins
Transients APIfirst things first
Transients are● temporary bits of data (usually raw data)
● like options, but w/ expiration time
● always persistent out of the box
● stored in wp_options by default
● stored in memory if a caching backend (APC,
Memcache, XCache, etc.) is available
● available since 2.8
● fetching data from a remote source
● performing an expensive* query or request
○ meta_query
○ tax_query
● data persistence is absolutely required**
Use Transients when
* operations that require high CPU usage or high latency (Identify slow
queries with Debug Bar)
** ex. - when polling data from external sources
Transients functionsat your disposal
read core?wp-includes/option.php
get_transient( $transient );
Get it.
unique string (key) representing a piece of data
set_transient( $transient,$value,$expiration = 0
);
Set it.the data you wish to store
for how long?
delete_transient( $transient );
Scrap it.
unique string (key) representing a piece of data
get_site_transient();
set_site_transient();
delete_site_transient();
Multisite?You're in luck! All Transients functions have their network-wide counterparts.
Use case:Using Transients to store
fragmented data
wp_nav_menu( array( 'theme_location' => 'primary',
) );
<ul id="menu-primary" class="menu">
<li id="menu-item-10" class="menu-item">
<a href="...">...</a>
</li>
...
</ul>
Without usingwp_nav_menu()
After usingwp_nav_menu()
That is ~5 extra queries(for each menu)
Instead, we can cache the menu in a transientand save on DB queries
// Get an existing copy of our transient data
$menu_html = get_transient( 'wcj_2013_menu' );
if ( false === $menu_html ) {
// data is not available, let's generate it
$menu_html = wp_nav_menu( array(
'theme_location' => 'primary',
'echo' => false,
) );
set_transient( 'wcj_2013_menu', $menu_html, DAY_IN_SECONDS );
}
// do something with $menu_html
echo $menu_html; 60 * 60 * 24
returns the menu instead of printing
// Get an existing copy of our transient data
$menu_html = get_transient( 'wcj_2013_menu' );
if ( false === $menu_html ) {
// data is not available, let's generate it
$menu_html = wp_nav_menu( array(
'theme_location' => 'primary',
'echo' => false,
) );
set_transient( 'wcj_2013_menu', $menu_html );
}
// do something with $menu_html
echo $menu_html; doesn't expire, but it might anyway
function wcj_2013_invldt_menu( $menu_id, $menu_data ) {
delete_transient( 'wcj_2013_menu' );
}
add_action( 'wp_update_nav_menu', 'wcj_2013_invldt_menu', 10, 2 );
Now, let's invalidate
this action runs whenever a menu is being saved
Use Transients when fetching data from a remote source
http://...https://...
Twitter APIFacebook API
Google+ API
Pick your poison
last.fm API
Example (Twitter API):Retrieve follower count and cache the results
function wcj_2013_get_twitter_followers_for( $handle ) {
$key = "wcj_2013_followers_$handle";
if ( false === ( $followers_count = get_transient( $key ) ) ) {
// transient expired, regenerate!
$followers_count = wp_remote_retrieve_body(
wp_remote_get( "http://api.twitter.com/1/users/show.json?
screen_name=$handle" )
);
// request failed?
if ( empty( $followers_count ) )
return false;
// extract the number of followers
$json = (object) json_decode( $followers_count );
$followers_count = absint( $json->followers_count );
// request was complete, store data in a transient for 6 hours
set_transient( $key, $followers_count, 6 * HOUR_IN_SECONDS );
}
return $followers_count;
}
function wcj_2013_get_twitter_followers_for( $handle ) {
$key = "wcj_2013_followers_$handle";
if ( false === ( $followers_count = get_transient( $key ) ) ) {
// transient expired, regenerate!
$followers_count = wp_remote_retrieve_body(
wp_remote_get( "http://api.twitter.com/1/users/show.json?
screen_name=$handle" )
);
// request failed?
if ( empty( $followers_count ) )
return false;
// extract the number of followers
$json = (object) json_decode( $followers_count );
$followers_count = absint( $json->followers_count );
// request was complete, store data in a transient for 6 hours
set_transient( $key, $followers_count, 6 * HOUR_IN_SECONDS );
}
return $followers_count;
}
function wcj_2013_get_twitter_followers_for( $handle ) {
$key = "wcj_2013_followers_$handle";
if ( false === ( $followers_count = get_transient( $key ) ) ) {
// transient expired, regenerate!
$followers_count = wp_remote_retrieve_body(
wp_remote_get( "http://api.twitter.com/1/users/show.json?
screen_name=$handle" )
);
// request failed?
if ( empty( $followers_count ) )
return false;
// extract the number of followers
$json = (object) json_decode( $followers_count );
$followers_count = absint( $json->followers_count );
// request was complete, store data in a transient for 6 hours
set_transient( $key, $followers_count, 6 * HOUR_IN_SECONDS );
}
return $followers_count;
}
function wcj_2013_get_twitter_followers_for( $handle ) {
$key = "wcj_2013_followers_$handle";
if ( false === ( $followers_count = get_transient( $key ) ) ) {
// transient expired, regenerate!
$followers_count = wp_remote_retrieve_body(
wp_remote_get( "http://api.twitter.com/1/users/show.json?
screen_name=$handle" )
);
// request failed?
if ( empty( $followers_count ) )
return false;
// extract the number of followers
$json = (object) json_decode( $followers_count );
$followers_count = absint( $json->followers_count );
// request was complete, store data in a transient for 6 hours
set_transient( $key, $followers_count, 6 * HOUR_IN_SECONDS );
}
return $followers_count;
}
printf( 'I have %d followers!', wcj_2013_get_twitter_followers_for( 'maorh' ) );
Let's spice it upGot some metrics!
Without Transients0.36014295 seconds
WITH Transients0.00010109 seconds
That is
3,562 X FASTER
Object Cache APIlast but not least
Object Cache is● non-persistent out of the box
● stored in PHP memory by default
● ideally* persistent when a caching backend is
available
● similar to Transients
● available since 2.0
* Success depends on server environment
Is post_id = 13
cached?
Do something with that post
Store results in Object Cache
DBSELECT * FROM
wp_posts WHERE ID =
13
Yes
No
Common use of the Object Cache API
Object Cache functionsat your disposal
read core?wp-includes/cache.php
● wp_cache_add( $k, $d, $g, $ex )
● wp_cache_get( $k )
● wp_cache_set( $k, $d, $g, $ex )
● wp_cache_delete( $k, $g )
● wp_cache_incr( $k, $offset, $g )
Object Cache functionsat your disposal
$key, $data, $group, $expiration
$song_obj = wp_cache_get( $id, 'songs' );
if ( false === $song_obj ) {
$song_obj = $wpdb->get_row(
$wpdb->prepare( "SELECT * FROM
$wpdb->songs WHERE ID = %d", $id )
);
wp_cache_set( $id, $song_obj, 'songs' );
}
// do something with $song_obj
Example
Best Practicesfor the best of us
● prefer refreshing the cache ONLY when data is
added/changed
● Avoid, if possible, caching on front end page requests
(instead, generate the data on an admin event*)
● Design to fail gracefully (never assume that data is in the
cache)
* useful events: publish_post, transition_post_status, created_{$taxonomy}, edited_{$taxonomy}
Out of the boxMemory Cache
Backend
Transients API persistent - database Persistent
Object Cache APInon-persistent -
memoryIdeally Persistent
http://wordpress.stackexchange.com/a/45137
Side-by-side comparison
Questions?Thanks!
@maorhkeep in touch