all repos — wp-ffpc @ 99913abed19b8161427d0aad72c5b71b93e005f2

bugfixes; taxonomy invalidate; taxonomy precache
Peter Molnar hello@petermolnar.eu
Thu, 28 Mar 2013 22:27:54 +0000
commit

99913abed19b8161427d0aad72c5b71b93e005f2

parent

97dca3d3fceea5704334893f57ebcc754667e71a

5 files changed, 193 insertions(+), 63 deletions(-)

jump to
M readme.txtreadme.txt

@@ -28,7 +28,7 @@ * (optional) talkative log for troubleshooting

* multiple memcached upstream support * possibility of precaching ( requires permalinks ) -Many thanks for supporters, testers & bug reporters: [Eric Gilette](http://www.ericgillette.com/ "Eric Gilette"); [doconeill](http://wordpress.org/support/profile/doconeill "doconeill"); [Mark Costlow](mailto:cheeks@swcp.com "Mark Costlow"). +Many thanks for supporters, testers & bug reporters: [Harold Kyle](https://github.com/haroldkyle "Harold Kyle"); [Eric Gilette](http://www.ericgillette.com/ "Eric Gilette"); [doconeill](http://wordpress.org/support/profile/doconeill "doconeill"); [Mark Costlow](mailto:cheeks@swcp.com "Mark Costlow"). Thanks for [Hyper Cache](http://wordpress.org/extend/plugins/hyper-cache "Hyper Cache") for beeing inspirational.

@@ -88,9 +88,19 @@

= 1.1 = *under development* +What's new: + * HTML comment option for displaying cache info before closing "body" tag ( a.k.a make sure it works "noob" method ) -* precache engine +* Pre-cache Engine +* new, additional invalidation method: clear post & all taxonomy cache + +What's fixed: + +* contributed fixes from [Harold Kyle](https://github.com/haroldkyle "Harold Kyle"): squelched various php and wp notices and warnings, enqueuing admin css and js better * bugfix for status check ( there were situations where the status was not updated correctly ) +* manual flush cache bug fixed ( was only flushing if the settings were on flush all ) +* bugfix on data & meta prefixes ( some places used hardcoded prefixes ) +* feed caching fixed ( due to a security check it turned out, feeds were excluded for a long time ) = 1.0 = *2013.03.22*
M wp-ffpc-abstract.phpwp-ffpc-abstract.php

@@ -132,15 +132,15 @@ register_uninstall_hook( $this->plugin_file , 'plugin_uninstall' );

/* register settings pages */ if ( $this->network ) - add_filter( "network_admin_plugin_action_links_" . $this->plugin_file, array( $this, 'plugin_settings_link' ) ); + add_filter( "network_admin_plugin_action_links_" . $this->plugin_file, array( &$this, 'plugin_settings_link' ) ); else - add_filter( "plugin_action_links_" . $this->plugin_file, array( $this, 'plugin_settings_link' ) ); + add_filter( "plugin_action_links_" . $this->plugin_file, array( &$this, 'plugin_settings_link' ) ); /* register admin init, catches $_POST and adds submenu to admin menu */ if ( $this->network ) - add_action('network_admin_menu', array( $this , 'plugin_admin_init') ); + add_action('network_admin_menu', array( &$this , 'plugin_admin_init') ); else - add_action('admin_menu', array( $this , 'plugin_admin_init') ); + add_action('admin_menu', array( &$this , 'plugin_admin_init') ); } /**

@@ -199,7 +199,7 @@ /* get broadcast message, if available */

$this->broadcast_message = @file_get_contents( $this->broadcast_message ); /* add submenu to settings pages */ - add_submenu_page( $this->settings_slug, $this->plugin_name . __( ' options' , $this->plugin_constant ), $this->plugin_name, $this->capability, $this->plugin_settings_page, array ( $this , 'plugin_admin_panel' ) ); + add_submenu_page( $this->settings_slug, $this->plugin_name . __( ' options' , $this->plugin_constant ), $this->plugin_name, $this->capability, $this->plugin_settings_page, array ( &$this , 'plugin_admin_panel' ) ); } /**
M wp-ffpc-acache.phpwp-ffpc-acache.php

@@ -64,24 +64,33 @@ }

} } +/* canonical redirect storage */ $wp_ffpc_redirect = null; + +/* fires up the backend storage array with current config */ $wp_ffpc_backend = new WP_FFPC_Backend( $wp_ffpc_config ); + +/* will store time of page generation */ $wp_ffpc_gentime = 0; +/* backend connection failed, no caching :( */ if ( $wp_ffpc_backend->status() === false ) return false; -$wp_ffpc_keys = array ( 'meta', 'data' ); +/* try to get data & meta keys for current page */ +$wp_ffpc_keys = array ( $wp_ffpc_config['prefix_meta'], $wp_ffpc_config['prefix_data'] ); $wp_ffpc_values = array(); foreach ( $wp_ffpc_keys as $key ) { $value = $wp_ffpc_backend->get ( $wp_ffpc_backend->key ( $key ) ); if ( ! $value ) { + /* does not matter which is missing, we need both, if one fails, no caching */ wp_ffpc_start(); return; } else { + /* store results */ $wp_ffpc_values[ $key ] = $value; } }

@@ -111,6 +120,8 @@ die();

} } +/*** SERVING CACHED PAGE ***/ + /* if we reach this point it means data was found & correct, serve it */ header('Content-Type: ' . $wp_ffpc_values['meta']['mime']);

@@ -142,11 +153,16 @@ echo $wp_ffpc_values['data'];

flush(); die(); +/*** END SERVING CACHED PAGE ***/ + + +/*** GENERATING CACHE ENTRY ***/ /** * starts caching function * */ function wp_ffpc_start( ) { + /* set start time */ global $wp_ffpc_gentime; $mtime = explode ( " ", microtime() ); $wp_ffpc_gentime = $mtime[1] + $mtime[0];

@@ -169,16 +185,19 @@ /**

* write cache function, called when page generation ended */ function wp_ffpc_callback( $buffer ) { + /* use global config */ global $wp_ffpc_config; + /* backend was already set up, try to use it */ global $wp_ffpc_backend; + /* check is it's a redirect */ global $wp_ffpc_redirect; - /* no is_home = error */ + /* no is_home = error, WordPress functions are not availabe */ if (!function_exists('is_home')) return $buffer; - /* no <body> close tag = not HTML, don't cache */ - if (stripos($buffer, '</body>') === false) + /* no <body> close tag = not HTML, also no <rss>, not feed, don't cache */ + if ( stripos($buffer, '</body>') === false && stripos($buffer, '</rss>') === false ) return $buffer; /* reset meta to solve conflicts */

@@ -266,7 +285,8 @@

$buffer = str_replace ( $sync_from, $sync_to, $buffer ); } - if ( $wp_ffpc_config['generate_time'] == '1' ) { + /* add generation info is option is set, but only to HTML */ + if ( $wp_ffpc_config['generate_time'] == '1' && stripos($buffer, '</body>') ) { global $wp_ffpc_gentime; $mtime = explode ( " ", microtime() ); $wp_ffpc_gentime = ( $mtime[1] + $mtime[0] )- $wp_ffpc_gentime;

@@ -277,8 +297,8 @@

$buffer = substr_replace( $buffer, $insertion, $index, 0); } - $wp_ffpc_backend->set ( $wp_ffpc_backend->key ( 'meta' ) , $meta ); - $wp_ffpc_backend->set ( $wp_ffpc_backend->key ( 'data' ) , $buffer ); + $wp_ffpc_backend->set ( $wp_ffpc_backend->key ( $wp_ffpc_config['prefix_meta'] ) , $meta ); + $wp_ffpc_backend->set ( $wp_ffpc_backend->key ( $wp_ffpc_config['prefix_data'] ) , $buffer ); /* vital for nginx, make no problem at other places */ header("HTTP/1.1 200 OK");

@@ -286,5 +306,6 @@

/* echoes HTML out */ return $buffer; } +/*** END GENERATING CACHE ENTRY ***/ ?>
M wp-ffpc-backend.phpwp-ffpc-backend.php

@@ -21,7 +21,6 @@

/** * - * @var array $key_prefixes Prefix keys for entries * @var mixed $connection Backend object storage variable * @var array $config Configuration settings array * @var boolean $alive Backend aliveness indicator

@@ -32,16 +31,14 @@ class WP_FFPC_Backend {

const plugin_constant = 'wp-ffpc'; const network_key = 'network'; - const id_prefix = 'wp-ffpc-id-'; - const prefix = 'prefix-'; const host_separator = ','; const port_separator = ':'; - private $key_prefixes = array ( 'meta', 'data' ); private $connection = NULL; - private $options; private $alive = false; - private $status; + private $network = false; + private $options = array(); + private $status = array(); /** * constructor

@@ -49,9 +46,10 @@ *

* @param mixed $config Configuration options * */ - public function __construct( $config ) { + public function __construct( $config, $network = false ) { $this->options = $config; + $this->network = $network; /* no config, nothing is going to work */ if ( empty ( $this->options ) ) {

@@ -73,10 +71,10 @@

/** * build key to make requests with * - * @param string $suffix suffix to add to prefix + * @param string $prefix prefix to add to prefix * */ - public function key ( $suffix = 'meta' ) { + public function key ( &$prefix ) { /* use the full accessed URL string as key, same will be generated by nginx as well we need a data and a meta key: data is string only with content, meta is not used in nginx */ if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' )

@@ -84,7 +82,7 @@ $_SERVER['HTTPS'] = 'on';

$protocol = ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) ) ? 'https://' : 'http://'; - return $suffix . '-' . $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + return $prefix . $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; }

@@ -147,20 +145,20 @@ * public get function, transparent proxy to internal function based on backend

* * @param string $key Cache key to invalidate, false mean full flush */ - public function clear ( $post_id = false ) { + public function clear ( $post_id = false, $force = false ) { /* look for backend aliveness, exit on inactive backend */ if ( ! $this->is_alive() ) return false; /* exit if no post_id is specified */ - if ( empty ( $post_id ) && $this->options['invalidation_method'] != 0) { + if ( empty ( $post_id ) && $force === false ) { $this->log ( __translate__('not clearing unidentified post ', self::plugin_constant ), LOG_WARNING ); return false; } /* if invalidation method is set to full, flush cache */ - if ( $this->options['invalidation_method'] === 0 || empty ( $post_id ) ) { + if ( $this->options['invalidation_method'] === 0 || $force === true ) { /* log action */ $this->log ( __translate__('flushing cache', self::plugin_constant ) );

@@ -174,36 +172,125 @@

return $result; } - /* need permalink functions */ - if ( !function_exists('get_permalink') ) - include_once ( ABSPATH . 'wp-includes/link-template.php' ); + /* storage for entries to clear */ + $to_clear = array(); + + /* clear taxonomies if settings requires it */ + if ( $this->options['invalidation_method'] == 2 ) { + /* this will only clear the current blog's entries */ + $this->taxonomy_links( $to_clear ); + } + + /* if there's a post id pushed, it needs to be invalidated in all cases */ + if ( !empty ( $post_id ) ) { + + /* need permalink functions */ + if ( !function_exists('get_permalink') ) + include_once ( ABSPATH . 'wp-includes/link-template.php' ); + + /* get path from permalink */ + $path = substr ( get_permalink( $post_id ) , 7 ); + + /* no path, don't do anything */ + if ( empty( $path ) ) { + $this->log ( __translate__('unable to determine path from Post Permalink, post ID: ', self::plugin_constant ) . $post_id , LOG_WARNING ); + return false; + } - /* get path from permalink */ - $path = substr ( get_permalink( $post_id ) , 7 ); + if ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) ) + $protocol = 'https://'; + else + $protocol = 'http://'; - /* no path, don't do anything */ - if ( empty( $path ) ) { - $this->log ( __translate__('unable to determine path from Post Permalink, post ID: ', self::plugin_constant ) . $post_id , LOG_WARNING ); - return false; + /* elements to clear + values are keys, faster to process and eliminates duplicates + */ + $to_clear[ $protocol . $path ] = true; } - if ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) ) - $protocol = 'https://'; - else - $protocol = 'http://'; + foreach ( $to_clear as $link => $dummy ) { + /* clear all feeds as well */ + $to_clear[ $link. 'feed' ] = true; + } - /* elements to clear */ - $to_clear = array ( - $this->options['prefix-meta'] . $protocol . $path, - $this->options['prefix-data'] . $protocol . $path, - ); + /* add data & meta prefixes */ + foreach ( $to_clear as $link => $dummy ) { + unset ( $to_clear [ $link ]); + $to_clear[ $this->options[ 'prefix_meta' ] . $link ] = true; + $to_clear[ $this->options[ 'prefix_data' ] . $link ] = true; + } - /* delete entries */ + /* run clear */ $internal = $this->proxy ( 'clear' ); $this->$internal ( $to_clear ); } /** + * to collect all permalinks of all taxonomy terms used in invalidation & precache + * + * @param array &$links Passed by reference array that has to be filled up with the links + * @param mixed $site Site ID or false; used in WordPress Network + * + */ + public function taxonomy_links ( &$links, $site = false ) { + + if ( $site !== false ) { + $current_blog = get_current_blog_id(); + switch_to_blog( $site ); + + $url = get_blog_option ( $site, 'siteurl' ); + if ( substr( $url, -1) !== '/' ) + $url = $url . '/'; + + $links[ $url ] = true; + } + + /* we're only interested in public taxonomies */ + $args = array( + 'public' => true, + ); + + /* get taxonomies as objects */ + $taxonomies = get_taxonomies( $args, 'objects' ); + + if ( !empty( $taxonomies ) ) { + foreach ( $taxonomies as $taxonomy ) { + /* reset array, just in case */ + $terms = array(); + + /* get all the terms for this taxonomy, only if not empty */ + $sargs = array( + 'hide_empty' => true, + 'fields' => 'all', + 'hierarchical' =>false, + ); + $terms = get_terms ( $taxonomy->name , $sargs ); + + if ( !empty ( $terms ) ) { + foreach ( $terms as $term ) { + /* get the permalink for the term */ + $link = get_term_link ( $term->slug, $taxonomy->name ); + /* add to container */ + $links[ $link ] = true; + /* remove the taxonomy name from the link, lots of plugins remove this for SEO, it's better to include them than leave them out + in worst case, we cache some 404 as well + */ + $link = str_replace ( '/'.$taxonomy->rewrite['slug'], '', $link ); + /* add to container */ + $links[ $link ] = true; + } + } + } + } + + /* switch back to original site if we navigated away */ + if ( $site !== false ) { + switch_to_blog( $current_blog ); + } + + } + + /** * get backend aliveness * * @return array Array of configured servers with aliveness value

@@ -382,12 +469,12 @@ * Removes entry from APC or flushes APC user entry storage

* * @param mixed $keys Keys to clear, string or array */ - private function apc_clear ( $keys ) { + private function apc_clear ( &$keys ) { /* make an array if only one string is present, easier processing */ if ( !is_array ( $keys ) ) - $keys = array ( $keys ); + $keys = array ( $keys => true ); - foreach ( $keys as $key ) { + foreach ( $keys as $key => $dummy ) { if ( ! apc_delete ( $key ) ) { $this->log ( __translate__('Failed to delete APC entry: ', self::plugin_constant ) . $key, LOG_ERR ); //throw new Exception ( __translate__('Deleting APC entry failed with key ', self::plugin_constant ) . $key );

@@ -432,7 +519,7 @@ }

/* check if initialization was success or not */ if ( $this->connection === NULL ) { - $this->log ( __translate__( 'error initializing Memcached PHP extension, exiting', self::prefix ) ); + $this->log ( __translate__( 'error initializing Memcached PHP extension, exiting', self::plugin_constant ) ); return false; }

@@ -532,13 +619,13 @@ * Removes entry from Memcached or flushes Memcached storage

* * @param mixed $keys String / array of string of keys to delete entries with */ - private function memcached_clear ( $keys ) { + private function memcached_clear ( &$keys ) { /* make an array if only one string is present, easier processing */ if ( !is_array ( $keys ) ) - $keys = array ( $keys ); + $keys = array ( $keys => true ); - foreach ( $keys as $key ) { + foreach ( $keys as $key => $dummy ) { $kresult = $this->connection->delete( $key ); if ( $kresult === false ) {

@@ -575,7 +662,7 @@ $this->connection = new Memcache();

/* check if initialization was success or not */ if ( $this->connection === NULL ) { - $this->log ( __translate__( 'error initializing Memcache PHP extension, exiting', self::prefix ) ); + $this->log ( __translate__( 'error initializing Memcache PHP extension, exiting', self::plugin_constant ) ); return false; }

@@ -648,12 +735,12 @@ * Removes entry from Memcached or flushes Memcached storage

* * @param mixed $keys String / array of string of keys to delete entries with */ - private function memcache_clear ( $keys ) { + private function memcache_clear ( &$keys ) { /* make an array if only one string is present, easier processing */ if ( !is_array ( $keys ) ) - $keys = array ( $keys ); + $keys = array ( $keys => true ); - foreach ( $keys as $key ) { + foreach ( $keys as $key => $dummy ) { $kresult = $this->connection->delete( $key ); if ( $kresult === false ) {
M wp-ffpc-class.phpwp-ffpc-class.php

@@ -117,6 +117,7 @@ /* invalidation method possible values array */

$this->select_invalidation_method = array ( 0 => __( 'flush cache' , $this->plugin_constant ), 1 => __( 'only modified post' , $this->plugin_constant ), + 2 => __( 'modified post and all taxonomies' , $this->plugin_constant ), ); }

@@ -128,7 +129,7 @@ */

public function plugin_setup () { /* initiate backend */ - $this->backend = new WP_FFPC_Backend ( $this->options ); + $this->backend = new WP_FFPC_Backend ( $this->options, $this->network ); /* get all available post types */ $post_types = get_post_types( );

@@ -187,7 +188,7 @@ */

public function plugin_hook_admin_init () { /* save parameter updates, if there are any */ if ( isset( $_POST[ $this->button_flush ] ) ) { - $this->backend->clear(); + $this->backend->clear( false, true ); $this->status = 3; header( "Location: ". $this->settings_link . self::slug_flush ); }

@@ -382,7 +383,7 @@ <dd>

<select name="invalidation_method" id="invalidation_method"> <?php $this->print_select_options ( $this->select_invalidation_method , $this->options['invalidation_method'] ) ?> </select> - <span class="description"><?php _e('Select cache invalidation method. <p><strong>Be careful! Selecting "flush cache" will flush the whole cache, including elements that might have been set and used by other applications. Also, invalidating only the post will _not_ clear categories, archive and taxonomy pages, therefore only use this if refreshing after publish can wait until the entries expire on their own.</strong></p>', $this->plugin_constant); ?></span> + <span class="description"><?php _e('Select cache invalidation method. <ol><li><em>flush cache</em> - clears everything in storage, <strong>including values set by other applications</strong></li><li><em>only modified post</em> - clear only the modified posts entry, everything else remains in cache</li><li><em>modified post and all taxonomies</em> - removes all taxonomy term cache ( categories, tags, home, etc ) and the modified post as well</li></ol>', $this->plugin_constant); ?></span> </dd> <dt>

@@ -588,7 +589,7 @@ <input class="button-secondary" type="submit" name="<?php echo $this->button_precache ?>" id="<?php echo $this->button_precache ?>" value="<?php _e('Pre-cache', $this->plugin_constant ) ?>" />

<?php endif; ?> </dt> <dd> - <span class="description"><?php _e('Start a background process that visits all permalinks of all blogs it can found thus forces WordPress to generate cached version of all the pages.', $this->plugin_constant); ?></span> + <span class="description"><?php _e('Start a background process that visits all permalinks of all blogs it can found thus forces WordPress to generate cached version of all the pages.<br />The plugin tries to visit links of taxonomy terms without the taxonomy name as well. This may generate 404 hits, please be prepared for these in your logfiles if you plan to pre-cache.', $this->plugin_constant); ?></span> </dd> </dl> </fieldset>

@@ -843,7 +844,7 @@ $out .= '<?php

$links = ' . var_export ( $links , true ) . '; echo "permalink\tgeneration time (s)\tsize ( kbyte )\n"; - foreach ( $links as $permalink ) { + foreach ( $links as $permalink => $dummy ) { $starttime = explode ( " ", microtime() ); $starttime = $starttime[1] + $starttime[0];

@@ -882,7 +883,14 @@ /* if a site id was provided, save current blog and change to the other site */

if ( $site !== false ) { $current_blog = get_current_blog_id(); switch_to_blog( $site ); + + $url = get_blog_option ( $site, 'siteurl' ); + if ( substr( $url, -1) !== '/' ) + $url = $url . '/'; + + $links[ $url ] = true; } + /* get all published posts */ $args = array (

@@ -916,9 +924,12 @@ break;

} /* collect permalinks */ - $links[] = $permalink; + $links[ $permalink ] = true; } + + //$this->taxonomy_links ( $links ); + $this->backend->taxonomy_links ( $links ); /* just in case, reset $post */ wp_reset_postdata();

@@ -928,6 +939,7 @@ if ( $site !== false ) {

switch_to_blog( $current_blog ); } } + } }