本文目录导读:

我来为你详细讲解PHP中如何实现适配器模式切换缓存驱动,这是一个非常实用的设计模式应用场景。
基础架构设计
1 缓存接口定义
<?php
namespace App\Cache;
interface CacheInterface
{
public function get(string $key): mixed;
public function set(string $key, mixed $value, int $ttl = null): bool;
public function delete(string $key): bool;
public function clear(): bool;
public function has(string $key): bool;
}
2 Redis缓存适配器
<?php
namespace App\Cache\Adapter;
use App\Cache\CacheInterface;
use Redis;
class RedisCache implements CacheInterface
{
private Redis $client;
private string $prefix;
public function __construct(array $config)
{
$this->client = new Redis();
$this->client->connect($config['host'], $config['port']);
if (isset($config['password'])) {
$this->client->auth($config['password']);
}
$this->prefix = $config['prefix'] ?? 'app:';
}
private function generateKey(string $key): string
{
return $this->prefix . $key;
}
public function get(string $key): mixed
{
$value = $this->client->get($this->generateKey($key));
return $value === false ? null : unserialize($value);
}
public function set(string $key, mixed $value, int $ttl = null): bool
{
$value = serialize($value);
if ($ttl) {
return $this->client->setex($this->generateKey($key), $ttl, $value);
}
return $this->client->set($this->generateKey($key), $value);
}
public function delete(string $key): bool
{
return $this->client->del($this->generateKey($key)) > 0;
}
public function clear(): bool
{
$prefix = $this->prefix;
$keys = $this->client->keys($prefix . '*');
if (empty($keys)) return true;
return $this->client->del($keys) > 0;
}
public function has(string $key): bool
{
return $this->client->exists($this->generateKey($key));
}
}
3 Memcached缓存适配器
<?php
namespace App\Cache\Adapter;
use App\Cache\CacheInterface;
use Memcached;
class MemcachedCache implements CacheInterface
{
private Memcached $client;
private string $prefix;
public function __construct(array $config)
{
$this->client = new Memcached();
$this->client->addServer($config['host'], $config['port']);
$this->prefix = $config['prefix'] ?? 'app:';
}
private function generateKey(string $key): string
{
return $this->prefix . $key;
}
public function get(string $key): mixed
{
$value = $this->client->get($this->generateKey($key));
return $value === false ? null : unserialize($value);
}
public function set(string $key, mixed $value, int $ttl = null): bool
{
$value = serialize($value);
return $this->client->set($this->generateKey($key), $value, $ttl ?? 0);
}
public function delete(string $key): bool
{
return $this->client->delete($this->generateKey($key));
}
public function clear(): bool
{
return $this->client->flush();
}
public function has(string $key): bool
{
$value = $this->client->get($this->generateKey($key));
return $value !== false;
}
}
4 文件缓存适配器
<?php
namespace App\Cache\Adapter;
use App\Cache\CacheInterface;
class FileCache implements CacheInterface
{
private string $cacheDir;
private string $prefix;
public function __construct(array $config)
{
$this->cacheDir = $config['path'] ?? sys_get_temp_dir() . '/cache';
$this->prefix = $config['prefix'] ?? 'app_';
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0777, true);
}
}
private function getFilePath(string $key): string
{
return $this->cacheDir . '/' . $this->prefix . md5($key) . '.cache';
}
public function get(string $key): mixed
{
$file = $this->getFilePath($key);
if (!file_exists($file)) return null;
$data = unserialize(file_get_contents($file));
if ($data['expires'] !== 0 && $data['expires'] < time()) {
$this->delete($key);
return null;
}
return $data['value'];
}
public function set(string $key, mixed $value, int $ttl = null): bool
{
$file = $this->getFilePath($key);
$data = [
'value' => $value,
'expires' => $ttl ? time() + $ttl : 0
];
return file_put_contents($file, serialize($data)) !== 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 = glob($this->cacheDir . '/' . $this->prefix . '*.cache');
foreach ($files as $file) {
unlink($file);
}
return true;
}
public function has(string $key): bool
{
return $this->get($key) !== null;
}
}
5 数据库缓存适配器
<?php
namespace App\Cache\Adapter;
use App\Cache\CacheInterface;
use PDO;
class DatabaseCache implements CacheInterface
{
private PDO $pdo;
private string $table;
public function __construct(array $config)
{
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8',
$config['host'], $config['database']);
$this->pdo = new PDO($dsn, $config['username'], $config['password']);
$this->table = $config['table'] ?? 'cache';
$this->createTableIfNotExists();
}
private function createTableIfNotExists(): void
{
$sql = "CREATE TABLE IF NOT EXISTS {$this->table} (
cache_key VARCHAR(255) PRIMARY KEY,
cache_value LONGTEXT,
expires_at INT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)";
$this->pdo->exec($sql);
}
public function get(string $key): mixed
{
$stmt = $this->pdo->prepare("SELECT * FROM {$this->table} WHERE cache_key = ?");
$stmt->execute([$key]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) return null;
if ($row['expires_at'] !== null && $row['expires_at'] < time()) {
$this->delete($key);
return null;
}
return unserialize($row['cache_value']);
}
public function set(string $key, mixed $value, int $ttl = null): bool
{
$expires = $ttl ? time() + $ttl : null;
$value = serialize($value);
$sql = "INSERT INTO {$this->table} (cache_key, cache_value, expires_at)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE
cache_value = VALUES(cache_value),
expires_at = VALUES(expires_at)";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([$key, $value, $expires]);
}
public function delete(string $key): bool
{
$stmt = $this->pdo->prepare("DELETE FROM {$this->table} WHERE cache_key = ?");
return $stmt->execute([$key]);
}
public function clear(): bool
{
return $this->pdo->exec("TRUNCATE TABLE {$this->table}") !== false;
}
public function has(string $key): bool
{
return $this->get($key) !== null;
}
}
缓存管理器(工厂类)
<?php
namespace App\Cache;
use App\Cache\Adapter\RedisCache;
use App\Cache\Adapter\MemcachedCache;
use App\Cache\Adapter\FileCache;
use App\Cache\Adapter\DatabaseCache;
use InvalidArgumentException;
class CacheManager
{
private array $configs;
private array $instances = [];
public function __construct(array $configs)
{
$this->configs = $configs;
}
public function driver(string $name = null): CacheInterface
{
$name = $name ?: $this->configs['default'];
$config = $this->configs['drivers'][$name] ?? null;
if (!$config) {
throw new InvalidArgumentException("Cache driver [{$name}] not found");
}
if (!isset($this->instances[$name])) {
$this->instances[$name] = $this->createDriver($config);
}
return $this->instances[$name];
}
private function createDriver(array $config): CacheInterface
{
$type = $config['type'];
return match ($type) {
'redis' => new RedisCache($config),
'memcached' => new MemcachedCache($config),
'file' => new FileCache($config),
'database' => new DatabaseCache($config),
default => throw new InvalidArgumentException("Unsupported cache type: {$type}")
};
}
}
配置示例
// config/cache.php
return [
'default' => env('CACHE_DRIVER', 'redis'),
'drivers' => [
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
'password' => null,
'prefix' => 'myapp:',
],
'memcached' => [
'type' => 'memcached',
'host' => '127.0.0.1',
'port' => 11211,
'prefix' => 'myapp:',
],
'file' => [
'type' => 'file',
'path' => storage_path('/cache'),
'prefix' => 'myapp_',
],
'database' => [
'type' => 'database',
'host' => '127.0.0.1',
'database' => 'mydb',
'username' => 'root',
'password' => '',
'table' => 'cache',
],
],
];
门面(Facade)模式
<?php
namespace App\Facades;
use App\Cache\CacheManager;
class Cache
{
private static ?CacheManager $manager = null;
public static function init(array $configs): void
{
self::$manager = new CacheManager($configs);
}
public static function put(string $key, mixed $value, int $ttl = null): bool
{
return self::driver()->set($key, $value, $ttl);
}
public static function get(string $key, mixed $default = null): mixed
{
$value = self::driver()->get($key);
return $value ?? $default;
}
public static function remember(string $key, int $ttl, callable $callback): mixed
{
if ($value = self::get($key)) {
return $value;
}
$value = $callback();
self::put($key, $value, $ttl);
return $value;
}
public static function forget(string $key): bool
{
return self::driver()->delete($key);
}
public static function flush(): bool
{
return self::driver()->clear();
}
public static function has(string $key): bool
{
return self::driver()->has($key);
}
public static function driver(string $name = null): CacheInterface
{
return self::$manager->driver($name);
}
}
使用示例
<?php
// 初始化配置
$configs = [
'default' => 'redis',
'drivers' => [
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
],
'file' => [
'type' => 'file',
'path' => '/tmp/cache',
],
],
];
Cache::init($configs);
// 使用默认驱动(Redis)
Cache::put('user:1', ['name' => 'John', 'age' => 30], 3600);
$user = Cache::get('user:1');
// 切换驱动(临时使用文件缓存)
Cache::driver('file')->set('page:about', '<html>...</html>', 7200);
$page = Cache::driver('file')->get('page:about');
// 使用remember模式
$data = Cache::remember('trending:posts', 1800, function () {
// 从数据库查询热门文章
return DB::table('posts')->where('views', '>', 1000)->get()->toArray();
});
// 删除缓存
Cache::forget('user:1');
// 清空缓存
Cache::flush();
生产环境的高级配置
<?php
namespace App\Cache;
class AdvancedCacheManager extends CacheManager
{
private array $stats = [];
public function driver(string $name = null): CacheInterface
{
$driver = parent::driver($name);
// 添加统计装饰器
return new StatisticsCache($driver, $this->stats);
}
public function getStats(): array
{
return $this->stats;
}
}
// 统计装饰器
class StatisticsCache implements CacheInterface
{
private CacheInterface $driver;
private array &$stats;
public function __construct(CacheInterface $driver, array &$stats)
{
$this->driver = $driver;
$this->stats = &$stats;
}
public function get(string $key): mixed
{
$start = microtime(true);
$value = $this->driver->get($key);
$this->record('get', microtime(true) - $start);
return $value;
}
public function set(string $key, mixed $value, int $ttl = null): bool
{
$start = microtime(true);
$result = $this->driver->set($key, $value, $ttl);
$this->record('set', microtime(true) - $start);
return $result;
}
private function record(string $operation, float $duration): void
{
$this->stats[$operation] = $this->stats[$operation] ?? [
'count' => 0,
'total_time' => 0
];
$this->stats[$operation]['count']++;
$this->stats[$operation]['total_time'] += $duration;
}
// 其他方法委托给内部驱动
public function delete(string $key): bool { return $this->driver->delete($key); }
public function clear(): bool { return $this->driver->clear(); }
public function has(string $key): bool { return $this->driver->has($key); }
}
这个适配器模式的设计具有以下优势:
- 松耦合:业务代码不依赖具体缓存实现
- 易扩展:添加新驱动只需实现CacheInterface接口
- 灵活切换:通过配置或运行时动态切换缓存驱动
- 统计监控:可以方便地添加监控和统计功能
- 统一接口:所有驱动提供一致的API
通过这种设计,你可以轻松地在不同缓存系统之间切换,而无需修改业务代码。