While developing the NextGEN Facebook OG plugin for WordPress, which adds social buttons from Facebook, Google+, LinkedIn, etc. to content and pages (along with several other features), I found the response time from these websites to be disappointing at times. When speed testing the pages of my websites, the JavaScript and images from these social elements would sometimes be a significant part of the total page load time. You can’t really save a copy of these files and serve them yourself, because they are frequently updated. You could create a cronjob to update them on a regular basis, but the maintenance of this can be cumbersome (as you add or remove files, etc.). It’s much easier to use a PHP method that caches and refreshes the remote files, and translate the URL at the same time. For example, something like:
|
1 |
$url = $cache->get( $url ); |
A complete example, including the setup of variables, etc., might look like this:
|
1 2 3 4 5 6 7 8 9 10 11 |
require_once ( dirname ( __FILE__ ) . '/cache.php' ); $cache = new ngfbCache(); $cache->base_dir = '/var/www/htdocs/cache/'; $cache->base_url = '/cache/'; $cache->pem_file = dirname ( __FILE__ ) . 'curl/cacert.pem'; $cache->verify_cert = true; $cache->expire_time = 3 * 60 * 60; // cache the file for 3 hours $url = $cache->get( $url ); // return a modified url (default) $raw = $cache->get( $url, 'raw' ); // return the file's content instead |
And here’s another example from the NextGEN Facebook OG plugin that creates the cache object in the __construct() method, defines the variable values in the setup_cache_vars() method, and uses get_cache_url() method to wrap the whole thing. In practice, this means I can use the following code to add a JavaScript URL:
|
1 2 |
<script type="text/javascript" src="<?php echo $this->get_cache_url( 'http://platform.tumblr.com/v1/share.js' ); ?>"></script> |
The __construct() method includes the cache.php code and defines the cache object.
|
1 2 3 4 5 6 |
var $cache = ''; function __construct() { require_once ( dirname ( __FILE__ ) . '/cache.php' ); $this->cache = new ngfbCache(); } |
The setup_cache_vars() method is called to adjust the cache variables (cache directory path, local URL base, expiration time, etc.).
|
1 2 3 4 5 6 7 8 9 |
function setup_cache_vars() { global $ngfb; $this->cache->base_dir = trailingslashit( NGFB_CACHEDIR ); $this->cache->base_url = trailingslashit( NGFB_CACHEURL ); $this->cache->pem_file = NGFB_PLUGINDIR . "lib/curl/cacert.pem"; $this->cache->verify_cert = $ngfb->options['ngfb_verify_certs']; $this->cache->expire_time = $ngfb->options['ngfb_cache_hours'] * 60 * 60; $this->cache->user_agent = NGFB_USER_AGENT; } |
And finally the get_cache_url() method does a few checks and returns the modified URL.
|
1 2 3 4 5 6 7 8 9 10 11 |
function get_cache_url( $url ) { global $ngfb; // facebook javascript sdk doesn't work when hosted locally if ( preg_match( '/connect.facebook.net/', $url ) ) return $url; // make sure the cache expiration is greater than 0 hours if ( empty( $ngfb->options['ngfb_cache_hours'] ) ) return $url; return ( $ngfb->cdn_linker_rewrite( $this->cache->get( $url ) ) ); } |
The setup_cache_vars() method is called once from another method (not shown here).
I’ve only found two issues (so far) when hosting remote content; some source files, like the Google+ plusone.js javascript file, may change depending on the user agent. In this case, you may want to define the $cache->user_agent variable (using a constant, as shown in the setup_cache_vars() method above). The default user agent in the ngfbCache() class is the one provided by the browser. Consider that crawlers may refresh the cache files, so
hard-coding a user agent may be desirable.
The $cache->base_url variable can be relative or include the protocol (http / https), and could be an alias to the
real cache folder location (which may be outside of the document root). And finally, note that the $cache->base_dir and $cache->base_url variables should end with a slash.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
<?php /* Copyright 2013 - Jean-Sebastien Morisset - http://surniaulula.com/ This script is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This script is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details at http://www.gnu.org/licenses/. The following ngfbCache() class was written for the NextGEN Facebook OG plugin for WordPress, available at http://wordpress.org/extend/plugins/nextgen-facebook/. Example usage: require_once ( dirname ( __FILE__ ) . '/cache.php' ); $cache = new ngfbCache(); $cache->base_dir = '/var/www/htdocs/cache/'; $cache->base_url = '/cache/'; $cache->pem_file = dirname ( __FILE__ ) . 'curl/cacert.pem'; $cache->verify_cert = true; $cache->expire_time = 3 * 60 * 60; // cache the file for 3 hours $url = $cache->get( $url ); // return a modified url (default) $raw = $cache->get( $url, 'raw' ); // return the file's content instead Some source files, like the Google+ plusone.js javascript file, may change depending on the user agent. In this case, you may want to define the $cache->user_agent variable as well. The default user agent is the one provided by the browser (consider that crawlers may refresh the cache files, so hard-coding a user agent may be desirable). The $cache->base_url can be relative or include the protocol (http / https), and could be an alias to the real folder location (which may be outside of the document root). The $cache->base_dir and $cache->base_url variables should end with a slash. */ if ( ! class_exists( 'ngfbCache' ) ) { class ngfbCache { var $base_dir = ''; var $base_url = '/cache/'; var $pem_file = ''; var $verify_cert = false; var $expire_time = 0; var $user_agent = ''; function __construct() { $this->base_dir = dirname ( __FILE__ ) . '/cache/'; $this->user_agent = $_SERVER['HTTP_USER_AGENT']; } // $ret = url : return the cached url if successful, else return the original url // $ret = raw : return the cached raw_data if successful, else return an empty string function get( $url, $ret = 'url' ) { if ( ! function_exists('curl_init') ) return $url; // if we're not using https on the current page, then no need to make our requests using https $get_url = empty( $_SERVER['HTTPS'] ) ? preg_replace( '/^https:/', 'http:', $url ) : $url; $get_url = preg_replace( '/#.*$/', '', $get_url ); $url_key = md5( $get_url ); $url_path = parse_url( $get_url, PHP_URL_PATH ); $url_ext = pathinfo( $url_path, PATHINFO_EXTENSION ); $url_frag = parse_url( $url, PHP_URL_FRAGMENT ); if ( ! empty( $url_frag ) ) $url_frag = '#' . $url_frag; $cache_file = $this->base_dir . $url_key . '.' . $url_ext; $cache_url = $this->base_url . $url_key . '.' . $url_ext . $url_frag; $cache_time = time() - $this->expire_time; $raw_data = ''; if ( file_exists( $cache_file ) && filemtime( $cache_file ) > $cache_time ) { $url = $cache_url; if ( $ret == 'raw' ) { $fh = fopen( $cache_file, 'rb' ); $raw_data = fread( $fh, filesize( $cache_file ) ); fclose( $fh ); } } else { $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, $get_url ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE ); curl_setopt( $ch, CURLOPT_USERAGENT, $this->user_agent ); if ( empty( $this->verify_cert) ) { curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 ); curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, FALSE ); } else { curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 ); curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, TRUE ); curl_setopt( $ch, CURLOPT_CAINFO, $this->pem_file ); } $raw_data = curl_exec( $ch ); curl_close( $ch ); if ( ! empty( $raw_data ) ) { if ( ! is_dir( $this->base_dir ) ) mkdir( $this->base_dir ); $fh = fopen( $cache_file, 'wb' ); if ( ! empty( $fh ) ) { if ( fwrite( $fh, $raw_data ) ) $url = $cache_url; fclose( $fh ); } } } if ( $ret == 'raw' ) return $raw_data; else return $url; } } } |
You can download the cache.php script here.
Did you find this post useful? Share it with your circles / friends, or leave a quick note bellow.
Thank you,
js.
