Created
February 12, 2013 20:33
-
-
Save staylor/4773131 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Plugin Name: Memcached Redux | |
Description: The real Memcached (not Memcache) backend for the WP Object Cache. | |
Version: 3.0 | |
Plugin URI: http://wordpress.org/extend/plugins/memcached/ | |
Author: Scott Taylor - uses code from Ryan Boren, Denis de Bernardy, Matt Martz | |
Install this file to wp-content/object-cache.php | |
*/ | |
// Users with setups where multiple installs share a common wp-config.php or $table_prefix | |
// can use this to guarantee uniqueness for the keys generated by this object cache | |
if ( ! defined( 'WP_CACHE_KEY_SALT' ) ) | |
define( 'WP_CACHE_KEY_SALT', '' ); | |
/** | |
* Wrap WP_Object_Cache | |
* | |
* @global WP_Object_Cache $wp_object_cache | |
*/ | |
function wp_cache_init() { | |
$GLOBALS['wp_object_cache'] = new WP_Object_Cache(); | |
} | |
function wp_cache_add_global_groups( $groups ) { | |
$GLOBALS['wp_object_cache']->add_global_groups( $groups ); | |
} | |
function wp_cache_add_non_persistent_groups( $groups ) { | |
$GLOBALS['wp_object_cache']->add_non_persistent_groups( $groups ); | |
} | |
function wp_cache_close() { | |
return $GLOBALS['wp_object_cache']->close(); | |
} | |
function wp_cache_flush() { | |
return $GLOBALS['wp_object_cache']->flush(); | |
} | |
function wp_cache_switch_to_blog( $blog_id ) { | |
return $GLOBALS['wp_object_cache']->switch_to_blog( $blog_id ); | |
} | |
/** | |
* Wrap WP_Cache_Bucket | |
* | |
*/ | |
/** | |
* | |
* @param string $key | |
* @param mixed $data | |
* @param string $group | |
* @param int $expire | |
* @return mixed | |
*/ | |
function wp_cache_add( $key, $data, $group = '', $expire = 0 ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
return $bucket->add( $key, $data, $expire ); | |
} | |
/** | |
* | |
* @param string $key | |
* @param int $n | |
* @param string $group | |
* @return int | |
*/ | |
function wp_cache_incr( $key, $n = 1, $group = '' ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
return $bucket->incr( $key, $n ); | |
} | |
/** | |
* | |
* @param string $key | |
* @param int $n | |
* @param string $group | |
* @return int | |
*/ | |
function wp_cache_decr( $key, $n = 1, $group = '' ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
return $bucket->decr( $key, $n ); | |
} | |
/** | |
* | |
* @param string $key | |
* @param mixed $data | |
* @param string $group | |
* @param int $expire | |
* @return | |
*/ | |
function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
return $bucket->replace( $key, $data, $expire ); | |
} | |
/** | |
* | |
* @param string $key | |
* @param string $group | |
* @return type | |
*/ | |
function wp_cache_delete( $key, $group = '' ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
return $bucket->delete( $key ); | |
} | |
/** | |
* | |
* @param string $key | |
* @param string $group | |
* @return mixed | |
*/ | |
function wp_cache_get( $key, $group = '' ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
return $bucket->get( $key ); | |
} | |
/** | |
* | |
* @param string $key | |
* @param mixed $data | |
* @param string $group | |
* @param int $expire | |
* @return mixed | |
*/ | |
function wp_cache_set( $key, $data, $group = '', $expire = 0 ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
if ( defined( 'WP_INSTALLING' ) == false ) | |
return $bucket->set( $key, $data, $expire ); | |
else | |
return $bucket->delete( $key ); | |
} | |
/** | |
* | |
* @param array $keys | |
* @param string $group | |
* @return array | |
*/ | |
function wp_cache_get_multi( $keys, $group = 'default' ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
return $bucket->get_multi( $keys ); | |
} | |
/** | |
* | |
* @param array $keys | |
* @param string $group | |
* @return array | |
*/ | |
function wp_cache_set_multi( $items, $expire = 0, $group = 'default' ) { | |
$bucket = WP_Cache_Bucket::get_instance( $group ); | |
return $bucket->set_multi( $items, $expire ); | |
} | |
class WP_Cache_Bucket { | |
static $instances = array(); | |
/** | |
* Cache group | |
* | |
* @var string | |
*/ | |
public $bucket; | |
/** | |
* Whether cache group is a global group | |
* | |
* @var boolean | |
*/ | |
public $is_global; | |
/** | |
* Decorator | |
* | |
* @var WP_Object_Cache | |
*/ | |
private $wp_cache; | |
/** | |
* Private bucket cache | |
* | |
* @var array | |
*/ | |
private $cache = array(); | |
/** | |
* Encrypted group token | |
* | |
* @var string | |
*/ | |
private $group; | |
/** | |
* Memcached bucket | |
* | |
* @var Memcached | |
*/ | |
private $mc; | |
private function __construct( $bucket = 'default' ) { | |
$this->bucket = empty( $bucket ) ? 'default' : $bucket; | |
$this->wp_cache =& $GLOBALS['wp_object_cache']; | |
$this->mc =& $this->wp_cache->get_mc( $bucket ); | |
$this->group = $this->mc->get( $this->encrypt( $this->bucket, 'bucket' ) ); | |
$this->is_global = in_array( $this->bucket, $this->wp_cache->global_groups ); | |
if ( empty( $this->group ) ) | |
$this->flush(); | |
} | |
public function is_falsey( $value ) { | |
return NULL === $value || false === $value || ( is_integer( $value ) && -1 == $value ); | |
} | |
public static function get_instance( $bucket ) { | |
if ( empty( $GLOBALS['wp_object_cache'] ) ) | |
return; | |
if ( ! isset( self::$instances[$bucket] ) ) | |
self::$instances[$bucket] = new WP_Cache_Bucket( $bucket ); | |
return self::$instances[$bucket]; | |
} | |
public function encrypt( $key, $group = false ) { | |
if ( $group ) | |
return $this->wp_cache->key( $key, $group ); | |
else | |
return $this->wp_cache->key( $key, $this->group ); | |
} | |
function get( $id ) { | |
$key = $this->encrypt( $id ); | |
if ( isset( $this->cache[$key] ) ) { | |
$value = is_object( $this->cache[$key] ) ? clone $this->cache[$key] : $this->cache[$key]; | |
} else if ( in_array( $this->bucket, $this->wp_cache->no_mc_groups ) ) { | |
$this->cache[$key] = $value = false; | |
} else { | |
$value = $this->mc->get( $key ); | |
if ( $this->is_falsey( $value ) ) | |
$value = false; | |
$this->cache[$key] = $value; | |
} | |
if ( 'checkthedatabaseplease' === $value ) { | |
unset( $this->cache[$key] ); | |
$value = false; | |
} | |
return $value; | |
} | |
function set( $id, $data, $expire = 0 ) { | |
$key = $this->encrypt( $id ); | |
if ( isset( $this->cache[$key] ) && ( 'checkthedatabaseplease' === $this->cache[$key] ) ) | |
return false; | |
if ( is_object( $data ) ) | |
$data = clone $data; | |
$this->cache[$key] = $data; | |
if ( in_array( $this->bucket, $this->wp_cache->no_mc_groups ) ) | |
return true; | |
$expire = empty( $expire ) ? $this->wp_cache->default_expiration : $expire; | |
$this->mc->set( $key, $data, $expire ); | |
$result = $data; | |
return $result; | |
} | |
function get_multi( $keys ) { | |
$return = array(); | |
$gets = array(); | |
foreach ( $keys as $id ) { | |
$key = $this->encrypt( $id ); | |
if ( isset( $this->cache[$key] ) ) { | |
if ( is_object( $this->cache[$key] ) ) | |
$return[$key] = clone $this->cache[$key]; | |
else | |
$return[$key] = $this->cache[$key]; | |
} else if ( in_array( $this->bucket, $this->wp_cache->no_mc_groups ) ) { | |
$return[$key] = false; | |
} else { | |
$gets[$key] = $key; | |
} | |
} | |
if ( ! empty( $gets ) ) { | |
$null = null; | |
$results = $this->mc->getMulti( $gets, $null, Memcached::GET_PRESERVE_ORDER ); | |
$joined = array_combine( array_keys( $gets ), array_values( $results ) ); | |
$return = array_merge( $return, $joined ); | |
} | |
$this->cache = array_merge( $this->cache, $return ); | |
return array_values( $return ); | |
} | |
function set_multi( $items, $expire = 0 ) { | |
$sets = array(); | |
$expire = empty( $expire ) ? $this->wp_cache->default_expiration : $expire; | |
foreach ( $items as $item ) { | |
list( $id, $data ) = $item; | |
$key = $this->encrypt( $id ); | |
if ( 'checkthedatabaseplease' === $this->cache[$key] ) | |
continue; | |
if ( is_object( $data ) ) | |
$data = clone $data; | |
$this->cache[$key] = $data; | |
if ( in_array( $this->bucket, $this->wp_cache->no_mc_groups ) ) | |
continue; | |
$sets[$key] = $data; | |
} | |
if ( ! empty( $sets ) ) | |
$this->mc->setMulti( $sets, $expire ); | |
} | |
function add( $id, $data, $expire = 0 ) { | |
$key = $this->encrypt( $id ); | |
if ( is_object( $data ) ) | |
$data = clone $data; | |
if ( in_array( $this->bucket, $this->wp_cache->no_mc_groups ) ) { | |
$this->cache[$key] = $data; | |
return true; | |
} elseif ( isset( $this->cache[$key] ) && false !== $this->cache[$key] ) { | |
return false; | |
} | |
$expire = empty( $expire ) ? $this->wp_cache->default_expiration : $expire; | |
$result = $this->mc->add( $key, $data, $expire ); | |
if ( false !== $result ) | |
$this->cache[$key] = $data; | |
return $result; | |
} | |
function replace( $id, $data, $expire = 0 ) { | |
$key = $this->encrypt( $id ); | |
$expire = empty( $expire ) ? $this->wp_cache->default_expiration : $expire; | |
if ( is_object( $data ) ) | |
$data = clone $data; | |
$result = $this->mc->replace( $key, $data, false, $expire ); | |
if ( false !== $result ) | |
$this->cache[$key] = $data; | |
return $result; | |
} | |
function delete( $id ) { | |
$key = $this->encrypt( $id ); | |
if ( in_array( $this->bucket, $this->wp_cache->no_mc_groups ) ) { | |
unset( $this->cache[$key] ); | |
return true; | |
} | |
$result = $this->mc->delete( $key ); | |
if ( false !== $result ) | |
unset( $this->cache[$key] ); | |
return $result; | |
} | |
function incr( $id, $n = 1 ) { | |
$key = $this->encrypt( $id ); | |
$this->cache[$key] = $this->mc->increment( $key, $n ); | |
return $this->cache[$key]; | |
} | |
function decr( $id, $n = 1 ) { | |
$key = $this->encrypt( $id ); | |
$this->cache[$key] = $this->mc->decrement( $key, $n ); | |
return $this->cache[$key]; | |
} | |
public function purge( $key ) { | |
unset( $this->cache[$key] ); | |
$this->delete( $key ); | |
} | |
public function flush() { | |
$key = uniqid( $this->is_global ? 'global_' : '' ); | |
$this->mc->set( $this->encrypt( $this->bucket, 'bucket' ), $key, 0 ); | |
$this->group = $key; | |
} | |
} | |
class WP_Object_Cache { | |
var $global_groups = array(); | |
var $no_mc_groups = array(); | |
var $cache = array(); | |
var $mc = array(); | |
var $cache_enabled = true; | |
var $default_expiration = 0; | |
function __construct() { | |
global $memcached_servers, $blog_id, $table_prefix; | |
$buckets = ! empty( $memcached_servers ) ? $memcached_servers : array( '127.0.0.1' ); | |
reset( $buckets ); | |
if ( is_int( key( $buckets ) ) ) | |
$buckets = array( 'default' => $buckets ); | |
foreach ( $buckets as $bucket => $servers ) { | |
$this->mc[$bucket] = new Memcached(); | |
$instances = array(); | |
foreach ( $servers as $server ) { | |
@list( $node, $port ) = explode( ':', $server ); | |
if ( empty( $port ) ) | |
$port = ini_get( 'memcache.default_port' ); | |
$port = intval( $port ); | |
if ( ! $port ) | |
$port = 11211; | |
$instances[] = array( $node, $port, 1 ); | |
} | |
$this->mc[$bucket]->addServers( $instances ); | |
} | |
$this->global_prefix = ''; | |
$this->blog_prefix = ''; | |
if ( function_exists( 'is_multisite' ) ) { | |
$this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix; | |
$this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ) . ':'; | |
} | |
} | |
function get_mc( $group ) { | |
if ( isset( $this->mc[$group] ) ) | |
return $this->mc[$group]; | |
return $this->mc['default']; | |
} | |
function add_global_groups( $groups ) { | |
if ( ! is_array( $groups ) ) | |
$groups = (array) $groups; | |
$this->global_groups = array_merge( $this->global_groups, $groups ); | |
$this->global_groups = array_unique( $this->global_groups ); | |
} | |
function add_non_persistent_groups( $groups ) { | |
if ( ! is_array( $groups ) ) | |
$groups = (array) $groups; | |
$this->no_mc_groups = array_merge( $this->no_mc_groups, $groups ); | |
$this->no_mc_groups = array_unique( $this->no_mc_groups ); | |
} | |
function switch_to_blog( $blog_id ) { | |
global $table_prefix; | |
$blog_id = (int) $blog_id; | |
$this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ) . ':'; | |
} | |
function key( $key, $group ) { | |
if ( empty( $group ) ) | |
$group = 'default'; | |
if ( false !== array_search( $group, $this->global_groups ) ) | |
$prefix = $this->global_prefix; | |
else | |
$prefix = $this->blog_prefix; | |
return preg_replace( '/\s+/', '', WP_CACHE_KEY_SALT . "$prefix$group:$key" ); | |
} | |
function close() { | |
// Silence is Golden. | |
} | |
function flush() { | |
// Don't flush if multi-blog. | |
if ( function_exists( 'is_site_admin' ) || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) | |
return true; | |
$ret = true; | |
foreach ( array_keys( $this->mc ) as $group ) | |
$ret &= $this->mc[$group]->flush(); | |
return $ret; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment