PHP 适配器切换缓存驱动

wen PHP项目 5

本文目录导读:

PHP 适配器切换缓存驱动

  1. 基础架构设计
  2. 缓存管理器(工厂类)
  3. 配置示例
  4. 门面(Facade)模式
  5. 使用示例
  6. 生产环境的高级配置

我来为你详细讲解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); }
}

这个适配器模式的设计具有以下优势:

  1. 松耦合:业务代码不依赖具体缓存实现
  2. 易扩展:添加新驱动只需实现CacheInterface接口
  3. 灵活切换:通过配置或运行时动态切换缓存驱动
  4. 统计监控:可以方便地添加监控和统计功能
  5. 统一接口:所有驱动提供一致的API

通过这种设计,你可以轻松地在不同缓存系统之间切换,而无需修改业务代码。

抱歉,评论功能暂时关闭!