<?php
|
namespace JVBase\managers;
|
|
if (!defined('ABSPATH')) {
|
exit; // Exit if accessed directly
|
}
|
|
class CacheManagerOld
|
{
|
private string $prefix = 'jvb_';
|
private string $group;
|
private int $cache_ttl;
|
private static ?bool $use_object_cache = null;
|
|
/**
|
* @param string|null $group The group name for this cache instance
|
* @param int|null $ttl The default ttl for this instance
|
*/
|
public function __construct(?string $group = null, ?int $ttl = null)
|
{
|
$this->group = $group ?: 'jvb_default';
|
$this->cache_ttl = $ttl ?: 3600;
|
|
// Check if Redis/Memcached is available
|
if (is_null(static::$use_object_cache)) {
|
static::$use_object_cache = !is_null(wp_using_ext_object_cache());
|
// error_log((static::$use_object_cache) ? 'Using Object Cache' : 'Not using Object Cache');
|
}
|
}
|
|
/**
|
* Get a value from the cache
|
* @param string|array $key The key to look up (auto-generates key from array of key=>values)
|
* @param string|null $group The group to get from. Defaults to current group
|
* @return mixed
|
*/
|
public function get(string|array $key, ?string $group = null): mixed
|
{
|
$group = $group ?: $this->group;
|
|
$key = $this->normalizeKey($key);
|
|
$cache_key = $this->buildKey($key);
|
|
// Use appropriate cache method
|
if (static::$use_object_cache) {
|
$value = wp_cache_get($cache_key, $group);
|
} else {
|
// Fallback to transients for local development
|
$value = get_transient($this->getTransientKey($cache_key, $group));
|
}
|
|
return (is_array($value) && array_key_exists('data', $value)) ? $value['data'] : $value;
|
}
|
|
public function getTimestamp(string|array $key, ?string $group = null): mixed
|
{
|
$group = $group ?: $this->group;
|
|
$key = $this->normalizeKey($key);
|
|
$cache_key = $this->buildKey($key);
|
|
// Use appropriate cache method
|
if (static::$use_object_cache) {
|
$value = wp_cache_get($cache_key, $group);
|
} else {
|
// Fallback to transients for local development
|
$value = get_transient($this->getTransientKey($cache_key, $group));
|
}
|
|
return (is_array($value) && array_key_exists('last_modified', $value)) ? $value['last_modified'] : false;
|
}
|
|
/**
|
* Store a value in cache
|
* @param string|array $key The key to look up (auto-generates key from array of key=>values)
|
* @param mixed $value The Value to set
|
* @param int|null $ttl The ttl (defaults to current set ttl)
|
* @param string|null $group The group to add cache to (defaults to current group))
|
* @return bool
|
*/
|
public function set(string|array $key, mixed $value, ?int $ttl = null, ?string $group = null): bool
|
{
|
$ttl = $ttl ?: $this->cache_ttl;
|
$group = $group ?: $this->group;
|
|
$key = $this->normalizeKey($key);
|
|
$cache_key = $this->buildKey($key);
|
$temp = [
|
'data' => $value,
|
'last_modified' => time(),
|
];
|
$value = $temp;
|
|
// Use appropriate cache method
|
if (static::$use_object_cache) {
|
return wp_cache_set($cache_key, $value, $group, $ttl);
|
} else {
|
// Fallback to transients
|
return set_transient($this->getTransientKey($cache_key, $group), $value, $ttl);
|
}
|
}
|
|
/**
|
* Delete a cached value
|
* @param string|array $key The key to look up (auto-generates key from array of key=>values)
|
* @param string|null $group The group to delete from (defaults to current group)
|
* @return bool
|
*/
|
public function delete(string|array $key, ?string $group = null): bool
|
{
|
$group = $group ?: $this->group;
|
|
$key = $this->normalizeKey($key);
|
|
$cache_key = $this->buildKey($key);
|
|
// Use appropriate cache method
|
if (static::$use_object_cache) {
|
return wp_cache_delete($cache_key, $group);
|
} else {
|
return delete_transient($this->getTransientKey($cache_key, $group));
|
}
|
}
|
|
public function clear():bool
|
{
|
try {
|
if (static::$use_object_cache) {
|
// With Redis, this could be implemented with SCAN command
|
// but wp_cache_* doesn't expose this, so we'd need direct Redis access
|
// For now, just flush the group as a nuclear option
|
if (function_exists('wp_cache_flush_group')) {
|
wp_cache_flush_group($this->group);
|
return true;
|
}
|
return false;
|
} else {
|
// For transients, search and delete
|
global $wpdb;
|
|
$prefix = self::getTransientPrefix($this->group);
|
$sql = "SELECT option_name FROM {$wpdb->options}
|
WHERE option_name LIKE %s
|
AND option_name LIKE %s";
|
|
$keys = $wpdb->get_col($wpdb->prepare(
|
$sql,
|
'_transient_' . $prefix . '%'
|
));
|
|
foreach ($keys as $key) {
|
$transient_key = str_replace('_transient_', '', $key);
|
delete_transient($transient_key);
|
}
|
return true;
|
}
|
} catch (\Exception $e) {
|
|
} finally {
|
return false;
|
}
|
}
|
|
/**
|
* Alias for delete() for backwards compatibility
|
* @param string $key The key to look up (auto-generates key from array of key=>values)
|
* @param string|null $group The group to delete from (defaults to current group))
|
* @return void
|
*/
|
public function invalidate(string $key, ?string $group = null): void
|
{
|
$this->delete($key, $group);
|
}
|
|
/**
|
* Clear all cache entries for a group
|
* @param string $group The group to clear
|
* @return bool
|
*/
|
public static function invalidateGroup(string $group): bool
|
{
|
$group = jvbNoBase($group);
|
|
if (wp_using_ext_object_cache()) {
|
// With Redis/Memcached, use native group flush
|
if (function_exists('wp_cache_flush_group')) {
|
return wp_cache_flush_group($group);
|
} else {
|
// Fallback for older WP versions - flush everything (not ideal)
|
return wp_cache_flush();
|
}
|
} else {
|
// For transients, we need to delete them from database
|
global $wpdb;
|
|
$prefix = self::getTransientPrefix($group);
|
|
// Delete transients and their timeouts
|
$sql = "DELETE FROM {$wpdb->options}
|
WHERE option_name LIKE %s
|
OR option_name LIKE %s";
|
|
$result = $wpdb->query($wpdb->prepare(
|
$sql,
|
'_transient_' . $prefix . '%',
|
'_transient_timeout_' . $prefix . '%'
|
));
|
|
return $result !== false;
|
}
|
}
|
|
/**
|
* Clear cache entries by pattern (only works efficiently with Redis)
|
* @param string $pattern
|
* @return int
|
*/
|
public function clearPattern(string $pattern): int
|
{
|
$count = 0;
|
|
if (static::$use_object_cache) {
|
// With Redis, this could be implemented with SCAN command
|
// but wp_cache_* doesn't expose this, so we'd need direct Redis access
|
// For now, just flush the group as a nuclear option
|
if (function_exists('wp_cache_flush_group')) {
|
wp_cache_flush_group($this->group);
|
return $count;
|
}
|
} else {
|
// For transients, search and delete
|
global $wpdb;
|
|
$prefix = self::getTransientPrefix($this->group);
|
$sql = "SELECT option_name FROM {$wpdb->options}
|
WHERE option_name LIKE %s
|
AND option_name LIKE %s";
|
|
$keys = $wpdb->get_col($wpdb->prepare(
|
$sql,
|
'_transient_' . $prefix . '%',
|
'%' . $pattern . '%'
|
));
|
|
foreach ($keys as $key) {
|
$transient_key = str_replace('_transient_', '', $key);
|
delete_transient($transient_key);
|
$count++;
|
}
|
}
|
|
return $count;
|
}
|
|
/**
|
* Helper to generateKey from array if applicable
|
* @param string|array $key
|
* @return string
|
*/
|
private function normalizeKey(string|array $key): string
|
{
|
return is_array($key) ? $this->generateKey($key) : $key;
|
}
|
|
/**
|
* Generate a cache key from parameters
|
* @param array $params An array of key/values that differentiates this cache item from others
|
* @return string
|
*/
|
public function generateKey(array $params): string
|
{
|
// Sort params for consistent key generation
|
ksort($params);
|
return md5(serialize($params));
|
}
|
|
/**
|
* The workhorse shorthand of CacheManager. Tests the cache, and calls the callback if nothing is found.
|
* @param string|array $key The key to look up (auto-generates key from array of key=>values)
|
* @param callable $callback The callback to generate the value for this key
|
* @param int|null $ttl The time-to-live for the cache. Defaults to constructor
|
* @param string|null $group The group to save cache to. Defaults to constructor
|
* @return mixed
|
*/
|
public function remember(string|array $key, callable $callback, ?int $ttl = null, ?string $group = null): mixed
|
{
|
$group = $group ?: $this->group;
|
$ttl = $ttl ?: $this->cache_ttl;
|
|
$key = $this->normalizeKey($key);
|
|
$value = $this->get($key, $group);
|
if ($value === false) {
|
$value = $callback();
|
if ($value !== false) {
|
$value = [
|
'data' => $value,
|
'last_modified' => time(),
|
];
|
$this->set($key, $value, $ttl, $group);
|
}
|
}
|
|
return (is_array($value) && array_key_exists('data', $value)) ? $value['data']: $value;
|
}
|
|
/**
|
* Build the cache key
|
* @param string $key
|
* @return string
|
*/
|
private function buildKey(string $key): string
|
{
|
return $this->prefix . $key;
|
}
|
|
/**
|
* Get transient key for fallback mode
|
* @param string $key
|
* @param string $group
|
* @return string
|
*/
|
private function getTransientKey(string $key, string $group): string
|
{
|
// Transients have a 172 character limit
|
$full_key = $group . '_' . $key;
|
|
if (strlen($full_key) > 160) {
|
// Use hash for long keys, but keep group prefix for clearPattern()
|
return substr($group, 0, 20) . '_' . md5($full_key);
|
}
|
|
return $full_key;
|
}
|
|
/**
|
* Get transient prefix for a group
|
*/
|
private static function getTransientPrefix(string $group): string
|
{
|
return $group . '_jvb_';
|
}
|
|
/**
|
* Check if using object cache
|
*/
|
public function isUsingObjectCache(): bool
|
{
|
return static::$use_object_cache;
|
}
|
|
|
/**
|
* Cleanup expired transients (maintenance method for non-Redis environments)
|
*/
|
public static function cleanupExpiredTransients(): int
|
{
|
if (wp_using_ext_object_cache()) {
|
return 0; // Not needed with Redis
|
}
|
|
global $wpdb;
|
|
// Delete expired transients
|
$sql = "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 < %d";
|
|
return $wpdb->query($wpdb->prepare($sql, time()));
|
}
|
}
|