/*
* Plugin Name: APCu Object Cache
* Description: APCu backend for the WP Object Cache.
* Based on Plugin named APCu Object Cache Backend
* Plugin URI: https://wordpress.org/plugins/apcu/
* Author: Pierre Schmitz
* Author URI: https://pierre-schmitz.com/
* Plugin URI: https://wordpress.org/plugins/apcu/
*
*
* @Authors James Dugger, Jonathan Bardo
* @copyright 2017 GoDaddy Inc. 14455 N. Hayden Road Scottsdale, Arizona
*/
$oc_logged_in = false;
foreach ( $_COOKIE as $k => $v ) {
if ( preg_match( '/^comment_author|wordpress_logged_in_[a-f0-9]+|woocommerce_items_in_cart|PHPSESSID_|edd_wp_session|edd_items_in_cartcc_cart_key|ccm_token/', $k ) ) {
$oc_logged_in = true;
break;
}
}
$oc_blocked_page = ( defined( 'WP_ADMIN' ) || defined( 'DOING_AJAX' ) || defined( 'XMLRPC_REQUEST' ) || 'wp-login.php' === basename( $_SERVER['SCRIPT_FILENAME'] ) );
function wpaas_is_using_apcu() {
return version_compare( PHP_VERSION, '5.6.0', '>=' ) && function_exists( 'apcu_fetch' );
}
if ( 'cli' !== php_sapi_name() && ! $oc_logged_in && ! $oc_blocked_page && wpaas_is_using_apcu() ) :
/**
* Save the transients to the DB. The explanation is a bit too long
* for code. The tl;dr of it is that we don't have a single 'fast cache'
* source yet (like memcached) and so some long lived items like transients
* are still best cached in the db and then brought back into APC
*
* @param string $transient
* @param mixed $value
* @param int $expire
* @param boolean $site = false
*
* @return bool
*/
function wpaas_save_transient( $transient, $value, $expire, $site = false ) {
global $wp_object_cache, $wpdb;
// The 'special' transient option names
$transient_timeout = ( $site ? '_site' : '' ) . '_transient_timeout_' . $transient;
$transient = ( $site ? '_site' : '' ) . '_transient_' . $transient;
// Cap expiration at 24 hours to avoid littering the DB
if ( $expire == 0 ) {
$expire = 24 * 60 * 60;
}
// Save to object cache
$wp_object_cache->set( $transient, $value, 'options', $expire );
$wp_object_cache->set( $transient_timeout, time() + $expire, 'options', $expire );
// Update alloptions
$alloptions = $wp_object_cache->get( 'alloptions', 'options' );
$alloptions[ $transient ] = $value;
$alloptions[ $transient_timeout ] = time() + $expire;
$wp_object_cache->set( 'alloptions', $alloptions, 'options' );
// Use the normal update option logic
if ( ! empty( $wpdb ) && $wpdb instanceof wpdb ) {
$flag = $wpdb->suppress_errors;
$wpdb->suppress_errors( true );
if ( $site && is_multisite() ) {
$wpdb->query(
$wpdb->prepare(
"INSERT INTO `{$wpdb->sitemeta}` ( `option_name`, `option_value`, `autoload` ) VALUES ( %s, UNIX_TIMESTAMP( NOW() ) + %d, 'yes' ) ON DUPLICATE KEY UPDATE `option_name` = VALUES ( `option_name` ), `option_value` = VALUES ( `option_value` ), `autoload` = VALUES ( `autoload` );",
$transient_timeout,
$expire
)
);
$wpdb->query(
$wpdb->prepare(
"INSERT INTO `{$wpdb->sitemeta}` ( `option_name`, `option_value`, `autoload` ) VALUES ( %s, %s, 'no' ) ON DUPLICATE KEY UPDATE `option_name` = VALUES ( `option_name` ), `option_value` = VALUES ( `option_value` ), `autoload` = VALUES ( `autoload` );",
$transient,
maybe_serialize( $value )
)
);
} else {
$wpdb->query(
$wpdb->prepare(
"INSERT INTO `{$wpdb->options}` (`option_name`, `option_value`, `autoload`) VALUES ( %s, UNIX_TIMESTAMP( NOW() ) + %d, 'yes' ) ON DUPLICATE KEY UPDATE `option_name` = VALUES ( `option_name` ), `option_value` = VALUES ( `option_value` ), `autoload` = VALUES ( `autoload` );",
$transient_timeout,
$expire
)
);
$wpdb->query(
$wpdb->prepare(
"INSERT INTO `{$wpdb->options}` (`option_name`, `option_value`, `autoload`) VALUES ( %s, %s, 'no' ) ON DUPLICATE KEY UPDATE `option_name` = VALUES ( `option_name` ), `option_value` = VALUES ( `option_value` ), `autoload` = VALUES ( `autoload` );",
$transient,
maybe_serialize( $value )
)
);
}
$wpdb->suppress_errors( $flag );
}
return true;
}
function wpaas_prune_transients() {
global $wpdb;
if ( ! empty( $wpdb ) && $wpdb instanceof wpdb && function_exists( 'is_main_site' ) && function_exists( 'is_main_network' ) ) {
$flag = $wpdb->suppress_errors;
$wpdb->suppress_errors( true );
// Lifted straight from schema.php
// Deletes all expired transients.
// The multi-table delete syntax is used to delete the transient record from table a,
// and the corresponding transient_timeout record from table b.
$time = time();
$wpdb->query( "DELETE a, b FROM $wpdb->options a, $wpdb->options b WHERE
a.option_name LIKE '\_transient\_%' AND
a.option_name NOT LIKE '\_transient\_timeout\_%' AND
b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
AND b.option_value < $time" );
if ( is_main_site() && is_main_network() ) {
$wpdb->query( "DELETE a, b FROM $wpdb->options a, $wpdb->options b WHERE
a.option_name LIKE '\_site\_transient\_%' AND
a.option_name NOT LIKE '\_site\_transient\_timeout\_%' AND
b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) )
AND b.option_value < $time" );
}
$wpdb->suppress_errors( $flag );
}
}
/**
* If another cache was flushed or updated, sync across all servers / processes using
* the database as the authority. This uses the database as the authority for timestamps
* as well to avoid drift between servers.
* @return void
*/
function wpaas_init_sync_cache() {
global $wpdb;
if ( empty( $wpdb ) || ! ( $wpdb instanceof wpdb ) ) {
return;
}
$flag = $wpdb->suppress_errors;
$wpdb->suppress_errors( true );
$result = $wpdb->get_results(
"SELECT option_name, option_value FROM `{$wpdb->options}` WHERE option_name = 'gd_system_last_cache_flush' UNION SELECT 'current_time', UNIX_TIMESTAMP( NOW() ) AS option_value;",
ARRAY_A
);
$wpdb->suppress_errors( $flag );
if ( empty( $result ) ) {
return;
}
$master_flush = false;
foreach ( $result as $row ) {
switch ( $row['option_name'] ) {
case 'current_time' :
$current_time = $row['option_value'];
break;
case 'gd_system_last_cache_flush' :
$master_flush = $row['option_value'];
break;
}
}
$local_flush = wp_cache_get( 'gd_system_last_cache_flush' );
if ( false === $local_flush || $local_flush < $master_flush ) {
wp_cache_flush( true );
wp_cache_set( 'gd_system_last_cache_flush', $current_time );
}
}
/**
* Start default implementation of object cache
*/
if ( ! defined( 'WP_APC_KEY_SALT' ) ) {
define( 'WP_APC_KEY_SALT', '' );
}
function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
if ( 'transient' == $group ) {
wpaas_save_transient( $key, $data, $expire );
return $wp_object_cache->add( "_transient_$key", $data, 'options', $expire );
} elseif ( 'site-transient' == $group ) {
wpaas_save_transient( $key, $data, $expire, true );
return $wp_object_cache->add( "_site_transient_$key", $data, 'site-options', $expire );
} else {
return $wp_object_cache->add( $key, $data, $group, $expire );
}
}
function wp_cache_incr( $key, $n = 1, $group = '' ) {
global $wp_object_cache;
return $wp_object_cache->incr2( $key, $n, $group );
}
function wp_cache_decr( $key, $n = 1, $group = '' ) {
global $wp_object_cache;
return $wp_object_cache->decr( $key, $n, $group );
}
function wp_cache_close() {
return true;
}
function wp_cache_delete( $key, $group = '' ) {
global $wp_object_cache, $wpdb;
if ( 'transient' == $group ) {
if ( ! empty( $wpdb ) && $wpdb instanceof wpdb ) {
$flag = $wpdb->suppress_errors;
$wpdb->suppress_errors( true );
$wpdb->query(
$wpdb->prepare(
"DELETE FROM `{$wpdb->prefix}options` WHERE option_name IN ( %s, %s );",
"_transient_{$key}",
"_transient_timeout_{$key}"
)
);
$wpdb->suppress_errors( $flag );
}
$wp_object_cache->delete( "_transient_timeout_$key", 'options' );
// Update alloptions
$alloptions = $wp_object_cache->get( 'alloptions', 'options' );
unset( $alloptions["_transient_$key"] );
unset( $alloptions["_transient_timeout_$key"] );
$wp_object_cache->set( 'alloptions', $alloptions, 'options' );
return $wp_object_cache->delete( "_transient_$key", 'options' );
} elseif ( 'site-transient' == $group ) {
if ( ! empty( $wpdb ) && $wpdb instanceof wpdb ) {
$table = $wpdb->options;
if ( is_multisite() ) {
$table = $wpdb->sitemeta;
}
$flag = $wpdb->suppress_errors;
$wpdb->suppress_errors( true );
$wpdb->query(
$wpdb->prepare(
"DELETE FROM `{$table}` WHERE option_name IN ( %s, %s );",
"_transient_{$key}",
"_transient_timeout_{$key}"
)
);
$wpdb->suppress_errors( $flag );
}
$wp_object_cache->delete( "_transient_timeout_$key", 'site-options' );
// Update alloptions
$alloptions = $wp_object_cache->get( 'alloptions', 'options' );
unset( $alloptions["_site_transient_$key"] );
unset( $alloptions["_site_transient_timeout_$key"] );
$wp_object_cache->set( 'alloptions', $alloptions, 'options' );
return $wp_object_cache->delete( "_site_transient_$key", 'site-options' );
}
return $wp_object_cache->delete( $key, $group );
}
function wp_cache_flush( $local_flush = false ) {
global $wp_object_cache, $wpdb;
if ( ! $local_flush ) {
if ( ! empty( $wpdb ) && $wpdb instanceof wpdb ) {
$flag = $wpdb->suppress_errors;
$wpdb->suppress_errors( true );
$wpdb->query( "INSERT INTO `{$wpdb->options}` (`option_name`, `option_value`, `autoload`) VALUES ( 'gd_system_last_cache_flush', UNIX_TIMESTAMP( NOW() ), 'no' ) ON DUPLICATE KEY UPDATE `option_name` = VALUES ( `option_name` ), `option_value` = VALUES ( `option_value` ), `autoload` = VALUES ( `autoload` );" );
$wpdb->suppress_errors( $flag );
}
}
return $wp_object_cache->flush();
}
function wp_cache_get( $key, $group = '', $force = false ) {
global $wp_object_cache, $wpdb;
if ( 'transient' == $group ) {
$alloptions = $wp_object_cache->get( 'alloptions', 'options' );
if ( isset( $alloptions["_transient_$key"] ) && isset( $alloptions["_transient_timeout_$key"] ) && $alloptions["_transient_timeout_$key"] > time() ) {
return maybe_unserialize( $alloptions["_transient_$key"] );
}
$transient = $wp_object_cache->get( "_transient_$key", 'options', $force );
$timeout = $wp_object_cache->get( "_transient_timeout_$key", 'options', $force );
if ( false !== $transient && ! empty( $timeout ) && $timeout > time() ) {
return maybe_unserialize( $transient );
}
if ( ! empty( $wpdb ) && $wpdb instanceof wpdb ) {
$flag = $wpdb->suppress_errors;
$wpdb->suppress_errors( true );
$result = $wpdb->get_results(
$wpdb->prepare(
"SELECT option_name, option_value FROM `{$wpdb->options}` WHERE option_name IN ( %s, %s ) UNION SELECT 'current_time', UNIX_TIMESTAMP( NOW() ) AS option_value;",
"_transient_{$key}",
"_transient_timeout_{$key}"
),
ARRAY_A
);
$wpdb->suppress_errors( $flag );
if ( ! empty( $result ) ) {
$transient = false;
$timeout = false;
$current_time = time();
foreach ( $result as $row ) {
switch ( $row['option_name'] ) {
case "_transient_$key" :
$transient = $row['option_value'];
break;
case "_transient_timeout_$key" :
$timeout = $row['option_value'];
break;
case 'current_time' :
$current_time = $row['option_value'];
break;
}
}
if ( false !== $transient && ! empty( $timeout ) && $timeout > $current_time ) {
return maybe_unserialize( $transient );
}
}
}
return false;
} elseif ( 'site-transient' == $group ) {
$transient = $wp_object_cache->get( "_site_transient_$key", 'options', $force );
$timeout = $wp_object_cache->get( "_site_transient_timeout_$key", 'options', $force );
if ( false !== $transient && ! empty( $timeout ) && $timeout > time() ) {
return maybe_unserialize( $transient );
}
if ( ! empty( $wpdb ) && $wpdb instanceof wpdb ) {
$table = $wpdb->options;
if ( is_multisite() ) {
$table = $wpdb->sitemeta;
}
$flag = $wpdb->suppress_errors;
$wpdb->suppress_errors( true );
$result = $wpdb->get_results(
$wpdb->prepare(
"SELECT option_name, option_value FROM `{$table}` WHERE option_name IN ( %s, %s ) UNION SELECT 'current_time', UNIX_TIMESTAMP( NOW() ) AS option_value;",
"_site_transient_{$key}",
"_site_transient_timeout_{$key}"
),
ARRAY_A
);
$wpdb->suppress_errors( $flag );
if ( ! empty( $result ) ) {
$transient = false;
$timeout = false;
$current_time = time();
foreach ( $result as $row ) {
switch ( $row['option_name'] ) {
case "_site_transient_$key" :
$transient = $row['option_value'];
break;
case "_site_transient_timeout_$key" :
$timeout = $row['option_value'];
break;
case 'current_time' :
$current_time = $row['option_value'];
break;
}
}
if ( false !== $transient && ! empty( $timeout ) && $timeout > $current_time ) {
return maybe_unserialize( $transient );
}
}
}
return false;
} else {
return $wp_object_cache->get( $key, $group, $force );
}
}
function wp_cache_init() {
global $wp_object_cache;
if ( mt_rand( 1, 100 ) == 42 ) {
wpaas_prune_transients();
}
add_action( 'muplugins_loaded', 'wpaas_init_sync_cache' );
$wp_object_cache = new APCu_Object_Cache();
}
function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
return $wp_object_cache->replace( $key, $data, $group, $expire );
}
function wp_cache_set( $key, $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
if ( defined( 'WP_INSTALLING' ) == false ) {
if ( 'transient' == $group ) {
return wpaas_save_transient( $key, $data, $expire );
} elseif ( 'site-transient' == $group ) {
return wpaas_save_transient( $key, $data, $expire, true );
} else {
return $wp_object_cache->set( $key, $data, $group, $expire );
}
} else {
return $wp_object_cache->delete( $key, $group );
}
}
function wp_cache_switch_to_blog( $blog_id ) {
global $wp_object_cache;
return $wp_object_cache->switch_to_blog( $blog_id );
}
function wp_cache_add_global_groups( $groups ) {
global $wp_object_cache;
$wp_object_cache->add_global_groups( $groups );
}
function wp_cache_add_non_persistent_groups( $groups ) {
global $wp_object_cache;
$wp_object_cache->add_non_persistent_groups( $groups );
}
class GD_APCu_Object_Cache {
private $prefix = '';
private $local_cache = array();
private $global_groups = array();
private $non_persistent_groups = array();
private $multisite = false;
private $blog_prefix = '';
public function __construct() {
global $table_prefix;
$this->multisite = is_multisite();
$this->blog_prefix = $this->multisite ? get_current_blog_id() . ':' : '';
$this->prefix = DB_HOST . '.' . DB_NAME . '.' . $table_prefix;
}
private function get_group( $group ) {
return empty( $group ) ? 'default' : $group;
}
private function get_key( $group, $key ) {
if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) {
return $this->prefix . '.' . $group . '.' . $this->blog_prefix . ':' . $key;
} else {
return $this->prefix . '.' . $group . '.' . $key;
}
}
public function add( $key, $data, $group = 'default', $expire = 0 ) {
$group = $this->get_group( $group );
$key = $this->get_key( $group, $key );
if ( function_exists( 'wp_suspend_cache_addition' ) && wp_suspend_cache_addition() ) {
return false;
}
if ( isset( $this->local_cache[ $group ][ $key ] ) ) {
return false;
}
// FIXME: Somehow apcu_add does not return false if key already exists
if ( ! isset( $this->non_persistent_groups[ $group ] ) && apcu_exists( $key ) ) {
return false;
}
if ( is_object( $data ) ) {
$this->local_cache[ $group ][ $key ] = clone $data;
} else {
$this->local_cache[ $group ][ $key ] = $data;
}
if ( ! isset( $this->non_persistent_groups[ $group ] ) ) {
return apcu_add( $key, $data, (int) $expire );
}
return true;
}
public function add_global_groups( $groups ) {
if ( is_array( $groups ) ) {
foreach ( $groups as $group ) {
$this->global_groups[ $group ] = true;
}
} else {
$this->global_groups[ $groups ] = true;
}
}
public function add_non_persistent_groups( $groups ) {
if ( is_array( $groups ) ) {
foreach ( $groups as $group ) {
$this->non_persistent_groups[ $group ] = true;
}
} else {
$this->non_persistent_groups[ $groups ] = true;
}
}
public function decr( $key, $offset = 1, $group = 'default' ) {
if ( $offset < 0 ) {
return $this->incr( $key, abs( $offset ), $group );
}
$group = $this->get_group( $group );
$key = $this->get_key( $group, $key );
if ( isset( $this->local_cache[ $group ][ $key ] ) && $this->local_cache[ $group ][ $key ] - $offset >= 0 ) {
$this->local_cache[ $group ][ $key ] -= $offset;
} else {
$this->local_cache[ $group ][ $key ] = 0;
}
if ( isset( $this->non_persistent_groups[ $group ] ) ) {
return $this->local_cache[ $group ][ $key ];
} else {
$value = apcu_dec( $key, $offset );
if ( $value < 0 ) {
apcu_store( $key, 0 );
return 0;
}
return $value;
}
}
public function delete( $key, $group = 'default', $force = false ) {
$group = $this->get_group( $group );
$key = $this->get_key( $group, $key );
unset( $this->local_cache[ $group ][ $key ] );
if ( ! isset( $this->non_persistent_groups[ $group ] ) ) {
return apcu_delete( $key );
}
return true;
}
public function flush() {
$this->local_cache = array();
// TODO: only clear our own entries
apcu_clear_cache();
return true;
}
public function get( $key, $group = 'default', $force = false, &$found = null ) {
$group = $this->get_group( $group );
$key = $this->get_key( $group, $key );
if ( ! $force && isset( $this->local_cache[ $group ][ $key ] ) ) {
$found = true;
if ( is_object( $this->local_cache[ $group ][ $key ] ) ) {
return clone $this->local_cache[ $group ][ $key ];
} else {
return $this->local_cache[ $group ][ $key ];
}
} elseif ( isset( $this->non_persistent_groups[ $group ] ) ) {
$found = false;
return false;
} else {
$value = @apcu_fetch( $key, $found );
if ( $found ) {
if ( $force ) {
$this->local_cache[ $group ][ $key ] = $value;
}
return $value;
} else {
return false;
}
}
}
public function incr2( $key, $offset = 1, $group = 'default' ) {
if ( $offset < 0 ) {
return $this->decr( $key, abs( $offset ), $group );
}
$group = $this->get_group( $group );
$key = $this->get_key( $group, $key );
if ( isset( $this->local_cache[ $group ][ $key ] ) && $this->local_cache[ $group ][ $key ] + $offset >= 0 ) {
$this->local_cache[ $group ][ $key ] += $offset;
} else {
$this->local_cache[ $group ][ $key ] = 0;
}
if ( isset( $this->non_persistent_groups[ $group ] ) ) {
return $this->local_cache[ $group ][ $key ];
} else if ( function_exists( 'apcu_inc' ) ) {
$value = apcu_inc( $key, $offset );
if ( $value < 0 ) {
apcu_store( $key, 0 );
return 0;
}
return $value;
}
return false;
}
public function replace( $key, $data, $group = 'default', $expire = 0 ) {
$group = $this->get_group( $group );
$key = $this->get_key( $group, $key );
if ( isset( $this->non_persistent_groups[ $group ] ) ) {
if ( ! isset( $this->local_cache[ $group ][ $key ] ) ) {
return false;
}
} else {
if ( ! isset( $this->local_cache[ $group ][ $key ] ) && ! apcu_exists( $key ) ) {
return false;
}
apcu_store( $key, $data, (int) $expire );
}
if ( is_object( $data ) ) {
$this->local_cache[ $group ][ $key ] = clone $data;
} else {
$this->local_cache[ $group ][ $key ] = $data;
}
return true;
}
public function reset() {
// This function is deprecated as of WordPress 3.5
// Be safe and flush the cache if this function is still used
$this->flush();
}
public function set( $key, $data, $group = 'default', $expire = 0 ) {
$group = $this->get_group( $group );
$key = $this->get_key( $group, $key );
if ( is_object( $data ) ) {
$this->local_cache[ $group ][ $key ] = clone $data;
} else {
$this->local_cache[ $group ][ $key ] = $data;
}
if ( ! isset( $this->non_persistent_groups[ $group ] ) ) {
return apcu_store( $key, $data, (int) $expire );
}
return true;
}
public function stats() {
// Only implemented because the default cache class provides this.
// This method is never called.
echo '';
}
public function switch_to_blog( $blog_id ) {
$this->blog_prefix = $this->multisite ? $blog_id . ':' : '';
}
}
if ( function_exists( 'apcu_inc' ) ) {
class APCu_Object_Cache extends GD_APCu_Object_Cache {
function incr( $key, $offset = 1, $group = 'default' ) {
return parent::incr2( $key, $offset, $group );
}
}
} else {
class APCu_Object_Cache extends GD_APCu_Object_Cache {
// Blank
}
}
endif;
The story behind the ‘Tokyo Stabbing’ picture, 1960 - Old Pictures
wpDiscuz
Would love your thoughts, please comment.
x
Insert