laipower/wp-content/plugins/w3-total-cache/Extension_FragmentCache_WpObjectCache.php

617 lines
15 KiB
PHP

<?php
namespace W3TC;
/**
* W3 Fragment Cache object
*/
class Extension_FragmentCache_WpObjectCache {
/**
* Internal cache array
*
* @var array
*/
var $cache = array();
/**
* Array of global groups
*
* @var array
*/
var $global_groups = array( 'site-transient' );
/**
* List of non-persistent groups
*
* @var array
*/
var $nonpersistent_groups = array();
/**
* Total count of calls
*
* @var integer
*/
var $cache_total = 0;
/**
* Cache hits count
*
* @var integer
*/
var $cache_hits = 0;
/**
* Cache misses count
*
* @var integer
*/
var $cache_misses = 0;
/**
* Total time
*
* @var integer
*/
var $time_total = 0;
/**
* Store debug information of w3tc using
*
* @var array
*/
var $debug_info = array();
/**
* Blog id of cache
*
* @var integer
*/
private $_blog_id;
/**
* Key cache
*
* @var array
*/
var $_key_cache = array();
/**
* Config
*/
var $_config = null;
/**
* Caching flag
*
* @var boolean
*/
var $_caching = false;
/**
* Cache reject reason
*
* @var string
*/
var $cache_reject_reason = '';
/**
* Lifetime
*
* @var integer
*/
var $_lifetime = 0;
/**
* Debug flag
*
* @var boolean
*/
var $_debug = false;
private $_core;
/**
* PHP5 style constructor
*/
function __construct() {
$this->_config = Dispatcher::config();
$this->_lifetime = $this->_config->get_integer( array( 'fragmentcache', 'lifetime' ) );
$this->_debug = $this->_config->get_boolean( array( 'fragmentcache', 'debug' ) );
$this->_caching = $this->_can_cache();
$this->_blog_id = Util_Environment::blog_id();
$this->_core = Dispatcher::component( 'Extension_FragmentCache_Core' );
}
/**
* Get from the cache
*
* @param string $id
* @param string $group
* @return mixed
*/
function get( $id, $group = 'transient', $force = false, &$found = null ) {
if ( $this->_debug ) {
$time_start = Util_Debug::microtime();
}
$key = $this->_get_cache_key( $id );
list( $fragment_group, $fragment_group_expiration, $fragment_group_global ) =
$this->_fragment_group( $id, $group );
$internal = isset( $this->cache[$fragment_group][$key] );
if ( $internal ) {
$found = true;
$value = $this->cache[$fragment_group][$key];
} elseif ( $this->_caching &&
!in_array( $group, $this->nonpersistent_groups ) ) {
$cache = $this->_get_cache( $fragment_group_global );
$v = $cache->get( $key, $fragment_group );
if ( is_array( $v ) && $v['content'] != null ) {
$found = true;
$value = $v['content'];
} else
$value = false;
} else {
$value = false;
}
if ( $value === null ) {
$value = false;
}
if ( is_object( $value ) ) {
$value = clone $value;
}
$this->cache[$fragment_group][$key] = $value;
$this->cache_total++;
if ( $value !== false ) {
$cached = true;
$this->cache_hits++;
} else {
$cached = false;
$this->cache_misses++;
}
/**
* Add debug info
*/
if ( $this->_debug ) {
$time = Util_Debug::microtime() - $time_start;
$this->time_total += $time;
if ( !$group ) {
$group = 'transient';
}
$this->debug_info[] = array(
'id' => $id,
'group' => $group,
'cached' => $cached,
'internal' => $internal,
'data_size' => ( $value ? strlen( serialize( $value ) ) : '' ),
'time' => $time
);
}
return $value;
}
/**
* Set to the cache
*
* @param string $id
* @param mixed $data
* @param string $group
* @param integer $expire
* @return boolean
*/
function set( $id, $data, $group = 'transient', $expire = 0 ) {
$key = $this->_get_cache_key( $id );
if ( is_object( $data ) ) {
$data = clone $data;
}
list( $fragment_group, $fragment_group_expiration, $fragment_group_global ) =
$this->_fragment_group( $id, $group );
if ( is_int( $fragment_group_expiration ) )
$expire = $fragment_group_expiration;
$this->cache[$fragment_group][$key] = $data;
if ( $this->_caching &&
!in_array( $group, $this->nonpersistent_groups ) ) {
$cache = $this->_get_cache( $fragment_group_global );
$v = array( 'content' => $data );
return $cache->set( $key, $v,
( $expire ? $expire : $this->_lifetime ), $fragment_group );
}
return true;
}
/**
* Delete from the cache
*
* @param string $id
* @param string $group
* @param bool $force
* @return boolean
*/
function delete( $id, $group = 'transient', $force = false ) {
if ( !$force && $this->get( $id, $group ) === false ) {
return false;
}
list( $fragment_group, $fragment_group_expiration, $fragment_group_global ) =
$this->_fragment_group( $id, $group );
$key = $this->_get_cache_key( $id );
unset( $this->cache[$fragment_group][$key] );
if ( $this->_caching && !in_array( $group, $this->nonpersistent_groups ) ) {
$cache = $this->_get_cache( $fragment_group_global );
return $cache->delete( $key, $fragment_group );
}
return true;
}
/**
* Add to the cache
*
* @param string $id
* @param mixed $data
* @param string $group
* @param integer $expire
* @return boolean
*/
function add( $id, $data, $group = 'default', $expire = 0 ) {
if ( $this->get( $id, $group ) !== false ) {
return false;
}
return $this->set( $id, $data, $group, $expire );
}
/**
* Replace in the cache
*
* @param string $id
* @param mixed $data
* @param string $group
* @param integer $expire
* @return boolean
*/
function replace( $id, $data, $group = 'default', $expire = 0 ) {
if ( $this->get( $id, $group ) === false ) {
return false;
}
return $this->set( $id, $data, $group, $expire );
}
/**
* Reset keys
*
* @return boolean
*/
function reset() {
$this->cache = array();
return true;
}
/**
* Flush cache
*
* @return boolean
*/
function flush() {
$this->cache = array();
$cache = $this->_get_cache( false );
$cache->flush( 'nogroup' );
$cache = $this->_get_cache( true );
$cache->flush( 'global-nogroup' );
$groups = $this->_core->get_registered_fragment_groups();
foreach ( $groups as $group => $descriptor ) {
$cache = $this->_get_cache( $descriptor['global'] );
$cache->flush( $group );
}
return true;
}
/**
* Purges all transients that belong to a transient group
*
* @param string $fragment_group fragment grouping
* @param bool $global whether flush site group or network wide, default is site specific. Use true for network wide.
* @return bool
*/
function flush_group( $fragment_group ) {
unset( $this->cache[$fragment_group] );
if ( $this->_caching ) {
list( $f1, $f2, $fragment_group_global ) =
$this->_fragment_group( $fragment_group, '' );
$cache = $this->_get_cache( $fragment_group_global );
return $cache->flush( $fragment_group );
}
return true;
}
/**
* Add global groups
*
* @param array $groups
* @return void
*/
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 );
}
/**
* Add non-persistent groups
*
* @param array $groups
* @return void
*/
function add_nonpersistent_groups( $groups ) {
if ( !is_array( $groups ) ) {
$groups = (array) $groups;
}
$this->nonpersistent_groups = array_merge( $this->nonpersistent_groups, $groups );
$this->nonpersistent_groups = array_unique( $this->nonpersistent_groups );
}
/**
* Increment numeric cache item's value
*
* @param int|string $key The cache key to increment
* @param int $offset The amount by which to increment the item's value. Default is 1.
* @param string $group The group the key is in.
* @return bool|int False on failure, the item's new value on success.
*/
function incr( $key, $offset = 1, $group = 'default' ) {
$value = $this->get( $key, $group );
if ( $value === false )
return false;
if ( !is_numeric( $value ) )
$value = 0;
$offset = (int) $offset;
$value += $offset;
if ( $value < 0 )
$value = 0;
$this->replace( $key, $value, $group );
return $value;
}
/**
* Decrement numeric cache item's value
*
* @param int|string $key The cache key to increment
* @param int $offset The amount by which to decrement the item's value. Default is 1.
* @param string $group The group the key is in.
* @return bool|int False on failure, the item's new value on success.
*/
function decr( $key, $offset = 1, $group = 'default' ) {
$value = $this->get( $key, $group );
if ( $value === false )
return false;
if ( !is_numeric( $value ) )
$value = 0;
$offset = (int) $offset;
$value -= $offset;
if ( $value < 0 )
$value = 0;
$this->replace( $key, $value, $group );
return $value;
}
/**
* Switches context to another blog
*
* @param integer $blog_id
*/
function switch_blog( $blog_id ) {
$this->reset();
$this->_blog_id = $blog_id;
}
/**
* Returns cache key
*
* @param string $id
* @param string $group
* @return string
*/
function _get_cache_key( $id ) {
return md5( $id );
}
/**
* Returns cache object
*
* @param null $blog_id
* @param string $group
* @return W3_Cache_Base
*/
function _get_cache( $global = false ) {
static $cache = array();
if ( !$global )
$blog_id = $this->_blog_id;
else
$blog_id = 0;
if ( !isset( $cache[$blog_id] ) ) {
$engine = $this->_config->get_string( array( 'fragmentcache', 'engine' ) );
switch ( $engine ) {
case 'memcached':
$engineConfig = array(
'servers' => $this->_config->get_array( array( 'fragmentcache', 'memcached.servers' ) ),
'persistent' => $this->_config->get_boolean( array( 'fragmentcache', 'memcached.persistent' ) ),
'aws_autodiscovery' => $this->_config->get_boolean( array( 'fragmentcache', 'memcached.aws_autodiscovery' ) ),
'username' => $this->_config->get_string( array( 'fragmentcache', 'memcached.username' ) ),
'password' => $this->_config->get_string( array( 'fragmentcache', 'memcached.password' ) )
);
break;
case 'redis':
$engineConfig = array(
'servers' => $this->_config->get_array( array( 'fragmentcache', 'redis.servers' ) ),
'verify_tls_certificates' => $this->_config->get_boolean( array( 'fragmentcache', 'redis.verify_tls_certificates' ) ),
'persistent' => $this->_config->get_boolean( array( 'fragmentcache', 'redis.persistent' ) ),
'timeout' => $this->_config->get_integer( array( 'fragmentcache', 'redis.timeout' ) ),
'retry_interval' => $this->_config->get_integer( array( 'fragmentcache', 'redis.retry_interval' ) ),
'read_timeout' => $this->_config->get_integer( array( 'fragmentcache', 'redis.read_timeout' ) ),
'dbid' => $this->_config->get_integer( array( 'fragmentcache', 'redis.dbid' ) ),
'password' => $this->_config->get_string( array( 'fragmentcache', 'redis.password' ) )
);
break;
case 'file':
$engineConfig = array(
'section' => 'fragment',
'locking' => $this->_config->get_boolean( array( 'fragmentcache', 'file.locking' ) ),
'flush_timelimit' => $this->_config->get_integer( 'timelimit.cache_flush' )
);
break;
default:
$engineConfig = array();
}
$engineConfig['blog_id'] = $blog_id;
$engineConfig['module'] = 'fragmentcache';
$engineConfig['host'] = Util_Environment::host();
$engineConfig['instance_id'] = Util_Environment::instance_id();
$cache[$blog_id] = Cache::instance( $engine, $engineConfig );
}
return $cache[$blog_id];
}
/**
* Check if caching allowed on init
*
* @return boolean
*/
function _can_cache() {
return true;
}
/**
* Returns debug info
*
* @return string
*/
public function w3tc_footer_comment( $strings ) {
$append = ( $this->cache_reject_reason != '' ?
sprintf( ' (%s)', $this->cache_reject_reason ) :'' );
$strings[] = sprintf(
__( 'Fragment Caching %d/%d fragments using %s%s', 'w3-total-cache' ),
$this->cache_hits, $this->cache_total,
Cache::engine_name( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) ),
$append );
if ( $this->_config->get_boolean( array( 'fragmentcache', 'debug' ) ) ) {
$strings[] = '';
$strings[] = 'Fragment Cache debug info:';
$strings[] = sprintf( "%s%s", str_pad( 'Caching: ', 20 ), ( $this->_caching ? 'enabled' : 'disabled' ) );
$strings[] = sprintf( "%s%d", str_pad( 'Total calls: ', 20 ), $this->cache_total );
$strings[] = sprintf( "%s%d", str_pad( 'Cache hits: ', 20 ), $this->cache_hits );
$strings[] = sprintf( "%s%d", str_pad( 'Cache misses: ', 20 ), $this->cache_misses );
$strings[] = sprintf( "%s%.4f", str_pad( 'Total time: ', 20 ), $this->time_total );
$strings[] = "W3TC Fragment Cache info:";
$strings[] = sprintf( "%s | %s | %s | %s | %s | %s| %s| %s",
str_pad( '#', 5, ' ', STR_PAD_LEFT ),
str_pad( 'Status', 15, ' ', STR_PAD_BOTH ),
str_pad( 'Source', 15, ' ', STR_PAD_BOTH ),
str_pad( 'Data size (b)', 13, ' ', STR_PAD_LEFT ),
str_pad( 'Query time (s)', 14, ' ', STR_PAD_LEFT ),
str_pad( 'Group', 14, ' ', STR_PAD_LEFT ),
str_pad( 'Accessible', 10, ' ', STR_PAD_LEFT ),
'Transient ID' );
foreach ( $this->debug_info as $index => $debug ) {
list( $fragment_group, $fragment_group_expiration, $fragment_group_global ) =
$this->_fragment_group( $debug['id'], $debug['group'] );
$strings[] = sprintf( "%s | %s | %s | %s | %s | %s| %s| %s",
str_pad( $index + 1, 5, ' ', STR_PAD_LEFT ),
str_pad( ( $debug['cached'] ? 'cached' : 'not cached' ), 15, ' ', STR_PAD_BOTH ),
str_pad( ( $debug['internal'] ? 'internal' : 'persistent' ), 15, ' ', STR_PAD_BOTH ),
str_pad( $debug['data_size'], 13, ' ', STR_PAD_LEFT ),
str_pad( round( $debug['time'], 4 ), 14, ' ', STR_PAD_LEFT ),
str_pad( $fragment_group, 14, ' ', STR_PAD_LEFT ),
str_pad( ( $debug['group'] == 'transient' ? 'site' : 'network' ), 10, ' ', STR_PAD_LEFT ),
$debug['id'] );
}
$strings[] = '';
}
return $strings;
}
/**
* Returns the group part of a transient/site-transient id.
* Uses registered fragment groups to identify it.
*
* @param unknown $id
* @return string
*/
private function _fragment_group( $id, $group ) {
if ( empty( $id ) )
return array( 'nogroup', 0, false );
$groups = $this->_core->get_registered_fragment_groups();
$use_group = '';
$length = 0;
foreach ( $groups as $group => $descriptor ) {
if ( strpos( $id, $group ) !== false ) {
if ( strlen( $group ) > $length ) {
$length = strlen( $group );
$use_group = array( $group, $descriptor['expiration'],
$descriptor['global'] );
}
}
}
if ( $use_group )
return $use_group;
if ( $group == 'site-transient' )
return array( 'global-nogroup', 0, true );
return array( 'nogroup', 0, false );
}
public function w3tc_usage_statistics_of_request( $storage ) {
$storage->counter_add( 'fragmentcache_calls_total', $this->cache_total );
$storage->counter_add( 'fragmentcache_calls_hits', $this->cache_hits );
}
}