本文目录导读:

我来详细讲解PHP项目中的缓存接口与适配器设计模式。
缓存接口设计
基础缓存接口
<?php
namespace App\Cache;
interface CacheInterface
{
/**
* 获取缓存
* @param string $key 缓存键
* @param mixed $default 默认值
* @return mixed
*/
public function get(string $key, mixed $default = null): mixed;
/**
* 设置缓存
* @param string $key 缓存键
* @param mixed $value 缓存值
* @param int|null $ttl 过期时间(秒)
* @return bool
*/
public function set(string $key, mixed $value, ?int $ttl = null): bool;
/**
* 删除缓存
* @param string $key 缓存键
* @return bool
*/
public function delete(string $key): bool;
/**
* 清空所有缓存
* @return bool
*/
public function clear(): bool;
/**
* 判断缓存是否存在
* @param string $key 缓存键
* @return bool
*/
public function has(string $key): bool;
/**
* 批量获取缓存
* @param array<string> $keys 缓存键数组
* @return array
*/
public function getMultiple(array $keys): array;
/**
* 批量设置缓存
* @param array $values 键值对数组
* @param int|null $ttl 过期时间
* @return bool
*/
public function setMultiple(array $values, ?int $ttl = null): bool;
/**
* 批量删除缓存
* @param array<string> $keys 缓存键数组
* @return bool
*/
public function deleteMultiple(array $keys): bool;
}
高级缓存接口(可选)
<?php
namespace App\Cache;
interface AdvancedCacheInterface extends CacheInterface
{
/**
* 递增
* @param string $key 缓存键
* @param int $value 递增的值
* @return int|false
*/
public function increment(string $key, int $value = 1): int|false;
/**
* 递减
* @param string $key 缓存键
* @param int $value 递减的值
* @return int|false
*/
public function decrement(string $key, int $value = 1): int|false;
/**
* 获取并删除
* @param string $key 缓存键
* @return mixed
*/
public function pull(string $key): mixed;
/**
* 如果不存在则设置
* @param string $key 缓存键
* @param mixed $value 缓存值
* @param int|null $ttl 过期时间
* @return bool
*/
public function add(string $key, mixed $value, ?int $ttl = null): bool;
}
适配器实现
Redis适配器
<?php
namespace App\Cache\Adapter;
use App\Cache\AdvancedCacheInterface;
use Redis;
use RedisException;
class RedisAdapter implements AdvancedCacheInterface
{
private Redis $redis;
private string $prefix;
public function __construct(Redis $redis, string $prefix = 'cache:')
{
$this->redis = $redis;
$this->prefix = $prefix;
}
private function buildKey(string $key): string
{
return $this->prefix . $key;
}
public function get(string $key, mixed $default = null): mixed
{
try {
$value = $this->redis->get($this->buildKey($key));
return $value === false ? $default : unserialize($value);
} catch (RedisException $e) {
// 记录日志
return $default;
}
}
public function set(string $key, mixed $value, ?int $ttl = null): bool
{
try {
$key = $this->buildKey($key);
$value = serialize($value);
if ($ttl !== null) {
return $this->redis->setex($key, $ttl, $value);
}
return $this->redis->set($key, $value);
} catch (RedisException $e) {
return false;
}
}
public function delete(string $key): bool
{
try {
return $this->redis->del($this->buildKey($key)) > 0;
} catch (RedisException $e) {
return false;
}
}
public function clear(): bool
{
try {
// 只清除当前前缀的缓存
$keys = $this->redis->keys($this->prefix . '*');
if (!empty($keys)) {
return $this->redis->del($keys) > 0;
}
return true;
} catch (RedisException $e) {
return false;
}
}
public function has(string $key): bool
{
try {
return $this->redis->exists($this->buildKey($key));
} catch (RedisException $e) {
return false;
}
}
public function getMultiple(array $keys): array
{
try {
$prefixedKeys = array_map(fn($key) => $this->buildKey($key), $keys);
$values = $this->redis->mget($prefixedKeys);
$result = [];
foreach ($keys as $i => $key) {
$result[$key] = $values[$i] === false ? null : unserialize($values[$i]);
}
return $result;
} catch (RedisException $e) {
return [];
}
}
public function setMultiple(array $values, ?int $ttl = null): bool
{
try {
$this->redis->multi();
foreach ($values as $key => $value) {
$key = $this->buildKey($key);
$value = serialize($value);
if ($ttl !== null) {
$this->redis->setex($key, $ttl, $value);
} else {
$this->redis->set($key, $value);
}
}
return $this->redis->exec() !== false;
} catch (RedisException $e) {
return false;
}
}
public function deleteMultiple(array $keys): bool
{
try {
$prefixedKeys = array_map(fn($key) => $this->buildKey($key), $keys);
return $this->redis->del($prefixedKeys) > 0;
} catch (RedisException $e) {
return false;
}
}
public function increment(string $key, int $value = 1): int|false
{
try {
return $this->redis->incrBy($this->buildKey($key), $value);
} catch (RedisException $e) {
return false;
}
}
public function decrement(string $key, int $value = 1): int|false
{
try {
return $this->redis->decrBy($this->buildKey($key), $value);
} catch (RedisException $e) {
return false;
}
}
public function pull(string $key): mixed
{
try {
$value = $this->get($key);
if ($value !== null) {
$this->delete($key);
}
return $value;
} catch (RedisException $e) {
return null;
}
}
public function add(string $key, mixed $value, ?int $ttl = null): bool
{
try {
$key = $this->buildKey($key);
$value = serialize($value);
if ($ttl !== null) {
return $this->redis->set($key, $value, ['nx', 'ex' => $ttl]);
}
return $this->redis->setnx($key, $value);
} catch (RedisException $e) {
return false;
}
}
}
Memcached适配器
<?php
namespace App\Cache\Adapter;
use App\Cache\CacheInterface;
use Memcached;
class MemcachedAdapter implements CacheInterface
{
private Memcached $memcached;
private string $prefix;
public function __construct(Memcached $memcached, string $prefix = 'cache:')
{
$this->memcached = $memcached;
$this->prefix = $prefix;
}
private function buildKey(string $key): string
{
return $this->prefix . $key;
}
public function get(string $key, mixed $default = null): mixed
{
$value = $this->memcached->get($this->buildKey($key));
return $value === false ? $default : $value;
}
public function set(string $key, mixed $value, ?int $ttl = null): bool
{
$ttl = $ttl ?? 0;
return $this->memcached->set($this->buildKey($key), $value, $ttl);
}
public function delete(string $key): bool
{
return $this->memcached->delete($this->buildKey($key));
}
public function clear(): bool
{
return $this->memcached->flush();
}
public function has(string $key): bool
{
return $this->memcached->get($this->buildKey($key)) !== false;
}
public function getMultiple(array $keys): array
{
$prefixedKeys = array_map(fn($key) => $this->buildKey($key), $keys);
$values = $this->memcached->getMulti($prefixedKeys);
$result = [];
foreach ($keys as $key) {
$prefixedKey = $this->buildKey($key);
$result[$key] = isset($values[$prefixedKey]) ? $values[$prefixedKey] : null;
}
return $result;
}
public function setMultiple(array $values, ?int $ttl = null): bool
{
$prefixedValues = [];
foreach ($values as $key => $value) {
$prefixedValues[$this->buildKey($key)] = $value;
}
$ttl = $ttl ?? 0;
return $this->memcached->setMulti($prefixedValues, $ttl);
}
public function deleteMultiple(array $keys): bool
{
$prefixedKeys = array_map(fn($key) => $this->buildKey($key), $keys);
return $this->memcached->deleteMulti($prefixedKeys) !== false;
}
}
文件缓存适配器
<?php
namespace App\Cache\Adapter;
use App\Cache\CacheInterface;
class FileCacheAdapter implements CacheInterface
{
private string $cacheDir;
public function __construct(string $cacheDir = null)
{
$this->cacheDir = $cacheDir ?? sys_get_temp_dir() . '/cache';
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0777, true);
}
}
private function getFilePath(string $key): string
{
$hash = md5($key);
$subDir = substr($hash, 0, 2);
$dir = $this->cacheDir . '/' . $subDir;
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
return $dir . '/' . $hash . '.cache';
}
public function get(string $key, mixed $default = null): mixed
{
$file = $this->getFilePath($key);
if (!file_exists($file)) {
return $default;
}
$data = unserialize(file_get_contents($file));
// 检查是否过期
if ($data['ttl'] !== null && time() > $data['expires_at']) {
unlink($file);
return $default;
}
return $data['value'];
}
public function set(string $key, mixed $value, ?int $ttl = null): bool
{
$file = $this->getFilePath($key);
$data = [
'value' => $value,
'ttl' => $ttl,
'expires_at' => $ttl !== null ? time() + $ttl : null,
'created_at' => time()
];
return file_put_contents($file, serialize($data), LOCK_EX) !== false;
}
public function delete(string $key): bool
{
$file = $this->getFilePath($key);
if (file_exists($file)) {
return unlink($file);
}
return true;
}
public function clear(): bool
{
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->cacheDir, \RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($files as $file) {
if ($file->isFile() && $file->getExtension() === 'cache') {
unlink($file->getRealPath());
}
}
return true;
}
public function has(string $key): bool
{
$value = $this->get($key);
return $value !== null;
}
public function getMultiple(array $keys): array
{
$result = [];
foreach ($keys as $key) {
$result[$key] = $this->get($key);
}
return $result;
}
public function setMultiple(array $values, ?int $ttl = null): bool
{
foreach ($values as $key => $value) {
if (!$this->set($key, $value, $ttl)) {
return false;
}
}
return true;
}
public function deleteMultiple(array $keys): bool
{
foreach ($keys as $key) {
$this->delete($key);
}
return true;
}
}
装饰器实现
标签缓存装饰器
<?php
namespace App\Cache\Decorator;
use App\Cache\CacheInterface;
class TaggedCacheDecorator implements CacheInterface
{
private CacheInterface $cache;
private string $tagPrefix = 'tag:';
public function __construct(CacheInterface $cache)
{
$this->cache = $cache;
}
/**
* 设置带标签的缓存
*/
public function setWithTags(string $key, mixed $value, array $tags = [], ?int $ttl = null): bool
{
// 保存标签关系
foreach ($tags as $tag) {
$tagKey = $this->tagPrefix . $tag;
$taggedKeys = $this->cache->get($tagKey, []);
$taggedKeys[] = $key;
$this->cache->set($tagKey, array_unique($taggedKeys));
}
return $this->cache->set($key, $value, $ttl);
}
/**
* 清除指定标签的所有缓存
*/
public function clearByTag(string $tag): bool
{
$tagKey = $this->tagPrefix . $tag;
$taggedKeys = $this->cache->get($tagKey, []);
foreach ($taggedKeys as $key) {
$this->cache->delete($key);
}
$this->cache->delete($tagKey);
return true;
}
public function get(string $key, mixed $default = null): mixed
{
return $this->cache->get($key, $default);
}
public function set(string $key, mixed $value, ?int $ttl = null): bool
{
return $this->cache->set($key, $value, $ttl);
}
public function delete(string $key): bool
{
return $this->cache->delete($key);
}
public function clear(): bool
{
return $this->cache->clear();
}
public function has(string $key): bool
{
return $this->cache->has($key);
}
public function getMultiple(array $keys): array
{
return $this->cache->getMultiple($keys);
}
public function setMultiple(array $values, ?int $ttl = null): bool
{
return $this->cache->setMultiple($values, $ttl);
}
public function deleteMultiple(array $keys): bool
{
return $this->cache->deleteMultiple($keys);
}
}
日志装饰器
<?php
namespace App\Cache\Decorator;
use App\Cache\CacheInterface;
use Psr\Log\LoggerInterface;
class LoggingCacheDecorator implements CacheInterface
{
private CacheInterface $cache;
private LoggerInterface $logger;
public function __construct(CacheInterface $cache, LoggerInterface $logger)
{
$this->cache = $cache;
$this->logger = $logger;
}
public function get(string $key, mixed $default = null): mixed
{
$startTime = microtime(true);
$result = $this->cache->get($key, $default);
$elapsedTime = microtime(true) - $startTime;
$hit = $result !== $default;
$this->logger->info('Cache access', [
'key' => $key,
'hit' => $hit,
'time' => $elapsedTime
]);
return $result;
}
public function set(string $key, mixed $value, ?int $ttl = null): bool
{
$this->logger->info('Cache set', [
'key' => $key,
'ttl' => $ttl
]);
return $this->cache->set($key, $value, $ttl);
}
public function delete(string $key): bool
{
$this->logger->info('Cache delete', ['key' => $key]);
return $this->cache->delete($key);
}
public function clear(): bool
{
$this->logger->warning('Cache cleared');
return $this->cache->clear();
}
public function has(string $key): bool
{
return $this->cache->has($key);
}
public function getMultiple(array $keys): array
{
return $this->cache->getMultiple($keys);
}
public function setMultiple(array $values, ?int $ttl = null): bool
{
return $this->cache->setMultiple($values, $ttl);
}
public function deleteMultiple(array $keys): bool
{
return $this->cache->deleteMultiple($keys);
}
}
缓存管理器
<?php
namespace App\Cache;
use App\Cache\Adapter\RedisAdapter;
use App\Cache\Adapter\FileCacheAdapter;
use App\Cache\Decorator\TaggedCacheDecorator;
class CacheManager
{
private array $drivers = [];
private ?CacheInterface $defaultDriver = null;
public function __construct(array $config = [])
{
$this->initializeFromConfig($config);
}
private function initializeFromConfig(array $config): void
{
if (isset($config['redis'])) {
$this->addDriver('redis', new RedisAdapter(
new \Redis(),
$config['redis']['prefix'] ?? 'cache:'
));
}
if (isset($config['file'])) {
$this->addDriver('file', new FileCacheAdapter(
$config['file']['path'] ?? null
));
}
}
public function addDriver(string $name, CacheInterface $driver): void
{
$this->drivers[$name] = $driver;
if ($this->defaultDriver === null) {
$this->defaultDriver = $driver;
}
}
public function driver(?string $name = null): CacheInterface
{
if ($name === null) {
return $this->defaultDriver;
}
if (!isset($this->drivers[$name])) {
throw new \InvalidArgumentException("Cache driver '{$name}' not found");
}
return $this->drivers[$name];
}
public function tags(array $tags): TaggedCacheDecorator
{
return new TaggedCacheDecorator($this->defaultDriver);
}
public function __call(string $method, array $arguments): mixed
{
return $this->defaultDriver->$method(...$arguments);
}
}
使用示例
<?php
// 配置缓存
$config = [
'redis' => [
'prefix' => 'app:',
],
'file' => [
'path' => '/var/cache/'
]
];
// 创建缓存管理器
$cacheManager = new CacheManager($config);
// 使用默认驱动(Redis)
$cache = $cacheManager->driver();
// 基本使用
$cache->set('user:1', ['name' => 'John', 'age' => 30], 3600);
$user = $cache->get('user:1', ['name' => 'Guest']);
// 标签缓存
$taggedCache = $cacheManager->tags(['users', 'profiles']);
$taggedCache->setWithTags('user:1', $userData, ['users', 'active'], 3600);
// 批量操作
$cache->setMultiple([
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3'
], 1800);
$values = $cache->getMultiple(['key1', 'key2', 'key3']);
// 使用不同的驱动
$fileCache = $cacheManager->driver('file');
$fileCache->set('config', $config, 7200);
这种设计模式的优势:
- 接口统一: 所有缓存驱动实现相同的接口
- 切换灵活: 可以轻松切换缓存驱动
- 扩展性好: 容易添加新的缓存驱动
- 装饰器模式: 可以添加横切关注点(日志、标签等)
- 依赖注入友好: 可以通过构造函数注入
这种架构非常适合在需要使用不同缓存方案的PHP项目中使用。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播