使用PHP实现缓存类,适配多种缓存类型

前言

这篇博客主要讲实现一个缓存类的思路。需要提前知道这些知识点:类的自动加载、php对文件的操作、php中的几种设计模式、redis基础知识、memcache和memcached基础知识。后面我会补充上传所有文件的源代码。

知识储备

设计模式
主要用到以下几个设计模式:

  • 工厂模式
  • 单例模式
  • 注册器模式
  • 适配器模式

文件操作

  • 读文件
  • 写文件

类的自动加载

  • 魔术方法:__autoload
  • spl_autoload_register

缓存nosql

  • redis
  • memcache
  • memcached

实现思路

  1. 首先实现一个自动加载类,用来实现类的自动加载。
  2. 然后定义一个抽象类,定义缓存类通用功能的统一规范。
  3. 再然后,分别实现不同的缓存类,如memcache、redis等,继承上边的抽象类,实现规定好的抽象方法。
  4. 最后实现一个缓存调用的入口类。需要实现将传入的缓存类型(如redis)初始化。

整体结构

  • Cache文件夹里面存储所有具体实现缓存类以及缓存抽象类。
  • Cache.php是缓存调用入口类。
  • Register.php里面实现了类的自动加载。
  • index.php是业务调用缓存的代码。
    在这里插入图片描述

具体实现

这里主要讲一下自动加载的实现,抽象类的实现,以及缓存调用入口类文件的实现。具体实现的缓存类,我们就用redis来做一个例子。

自动加载

自动加载主要用到spl_autoload_register函数,spl_autoload_register() 满足了多条 autoload 的需求。 它实际上创建了 autoload 函数的队列。

主要实现代码(忽略了部分代码):

Register.php:

	public function __construct() {
        spl_autoload_register(array($this, 'autoload'));
    }
    
	public static function autoload($name)
    {
        if (trim($name) == '') {
            new \Exception('No class or interface named for loading');
        }
        
        // 规范类名称 
        $name = self::formatClassName($name);

        if (isset(self::$_loadedClass[$name])) {
            self::$_loadedClass[$name]++;
            return;
        }
        
        $file = __DIR__ . DIRECTORY_SEPARATOR . strtr($name, ['_' => DIRECTORY_SEPARATOR, '\\' => DIRECTORY_SEPARATOR]) . '.php';
        
        if (!$file || !is_file($file)) {
            return;
        }
		// 导入文件
        include $file;
        
        if (!self::classExists($name, false) && !self::interfaceExists($name, false)) {
            throw new \Exception('Class or interface does not exist in loaded file');
        }
        
        if (empty(self::$_loadedClass[$name])) {
            self::$_loadedClass[$name] = 1;
        }
    }

抽象类

因为是缓存类,所以必须定义缓存初始化、缓存值的设置、缓存值的获取、缓存值的删除、清空所有缓存等功能。

实现主要代码(删除了部分代码):

AbstractCache.php:

<?php
/**
 * 缓存抽象类
 * Class AbstractCache
 */
abstract class AbstractCache
{
    /**
     * 主服务器
     */
    protected $_masterObj;
	
	/**
     * 从服务器
     */
    protected $_slaveObj;

    /**
     * 初始化
     *
     * @param $conf
     * @return $this
     */
    abstract public function init($conf);

    /**
     * 获取数据
     *
     * @param array|string $key
     * @param bool         $checkClear 检查是否被清除
     * @param int          $time       以引用的方式返回数据添加时间
     * @param int          $expire     以引用的方式返回当前值的过期时间
     * @return mixed
     */
    abstract public function get($key, $checkClear = true, &$time = null, &$expire = null);

    /**
     * 写入数据
     *
     * @param      $key
     * @param null $value
     * @param int  $expire
     * @return mixed
     */
    abstract public function set($key, $value = null, $expire = 3600);

    /**
     * 删除数据
     *
     * @param $key
     * @return mixed
     */
    abstract public function delete($key);

    /**
     * 删除所有缓存
     * @return bool
     */
    abstract public function flush();
    
    /**
     * 生成KEY值
     *
     * @param array $param
     * @return string
     */
    public function makeKey($param = null)
    {
        if (empty($param)) {
            return '';
        }

        if (is_array($param)) {
            ksort($param);
            $param = http_build_query($param);
        }

        return md5($this->getModuleName() . $param);
    }

    /**
     * 包装VALUE
     *
     * @param mixed $val
     * @param int   $expire
     * @return array
     */
    final protected function _makeValue($val, $expire = 3600)
    {
        $val = array(
            'value'  => $val,
            'expire' => $expire,
            'time'   => $_SERVER['REQUEST_TIME'] ?? time(),
        );
        return $val;
    }

    /**
     * 获取原始的缓存对象
     *
     * @param bool $master
     * @return mixed
     */
    final public function getOriginObj($master = true)
    {
        return $master ? $this->_masterObj : $this->_slaveObj;
    }
}

redis的缓存

这里就只用redis做一个具体实现。主要实现抽象类中的抽象方法。

具体实现类(删除了部分代码)

Redis.php:

<?php
class RedisCache extends AbstractCache 
{
    const CONNECT_TYPE_MASTER = 'master';
    const CONNECT_TYPE_SLAVE  = 'slave';

    /**
     * @var Redis
     */
    protected $_masterObj;

    /**
     * @var Redis
     */
    protected $_slaveObj;


    /**
     * 初始化
     *
     * @param $conf
     * @return $this
     * @throws \Exception
     */
    public function init($conf)
    {
        if (empty($conf['master'])) {
            throw new \Exception('config is empty!');
        }

        if (!$this->_masterObj || !$this->_slaveObj) {
            $_master = $conf['master'];

            $_masterObj = self::_createConn($_master);


            if (!empty($conf['slaves'])) {
                $_slave    = count($conf['slaves']) > 1 ? array_rand($conf['slaves']) : reset($conf['slaves']);
                $_slaveObj = self::_createConn($_slave);
            } else {
                $_slaveObj = $_masterObj;
            }

            $this->_masterObj = $_masterObj;
            $this->_slaveObj  = $_slaveObj;
        }

        if (!empty($conf['options'])) {
            $this->_setOptions($conf['options']);
        }

        return $this;
    }

    /**
     * 清除所有数据
     *
     * @return bool
     */
    public function flush()
    {
        return $this->_masterObj->flushDB();
    }

    /**
     * 获取数据
     *
     * @param array|string $key
     * @param bool         $checkClear 检查是否被清除
     * @param int          $time       以引用的方式返回数据添加时间
     * @param int          $expire     以引用的方式返回当前值的过期时间
     * @return mixed
     */
    public function get($key, $checkClear = true, &$time = null, &$expire = null)
    {
        $_key = $this->makeKey($key);
        $res  = $this->_slaveObj->get($_key);

        $res = unserialize($res);
        if ($res && isset($res['value'])) {
            $time   = $res['time'];
            $expire = $res['expire'];
            if ($checkClear) {
                unset($res);
                return null;
            }
            return $res['value'];
        }

        return null;
    }

    /**
     * 写入数据
     *
     * @param      $key
     * @param null $value
     * @param int  $expire
     * @return mixed
     */
    public function set($key, $value = null, $expire = 3600)
    {
        return $this->_masterObj->set($this->makeKey($key), serialize($this->_makeValue($value, $expire)), $expire);
    }

    /**
     * 删除数据
     *
     * @param $key
     * @return void
     */
    public function delete($key)
    {
        $this->_masterObj->delete($this->makeKey($key));
    }

    /**
     * 创建redis对象
     *
     * @param  array $conf 配置 {host => string, port => int, timeout => float}
     * @return Redis
     */
    protected static function _createConn(array $conf)
    {
        $_obj = new Redis();

        $_connectFunc = empty($conf['pconnect']) ? 'connect' : 'pconnect';
        $_obj->$_connectFunc($conf['host'], $conf['port'], $conf['timeout']);
        return $_obj;
    }

    protected function _setOptions(array $options)
    {
        foreach ($options as $_key => $_val) {
            $this->_masterObj->setOption($_key, $_val);
            $this->_slaveObj->setOption($_key, $_val);
        }
    }
}

缓存入口类

这里是所有缓存类的入口。我们调用缓存类,统一实例化这个类。

具体实现代码(删除了部分代码):

Cache.php:

    /**
     * 单例
     *
     * @param null $_type 
     * @return 
     * @throws \\Exception
     */
    public static function instance($_type = null)
    {
        if (empty($_type)) {
            self::getConfig('DEFAULT');
        }

        if (!$_type) {
            throw new \Exception("The type can not be set to empty!");
        }

        if (!isset(self::$_instance[$_type])) {

            $conf = self::getConfig($_type);

            if (empty($conf)) {
                throw new \Exception("The '{$_type}' type cache config does not exists!");
            }
			
			// 获取类名称
            $class = self::getClassName($_type);
            $obj   = new $class();

            if (!$obj instanceof AbstractCache) {
                throw new \Exception("The '{$class}' not instanceof AbstractCache!");
            }

            $obj->init($conf);
            self::$_instance[$type] = $obj;

        } else {
            $obj = self::$_instance[$type];
        }
        return $obj;
    }

所有的实现部分到这里就结束了。
我们来使用一下:

$cacheObj = Cache::instance('memcache');
$cacheObj->set(array('id' => 2), array('name' => '浪子燕青'), 3600);//添加数据
$cacheObj->get(array('id' => 2));//获取数据

我们可以用这种方式实现更多的功能,比如:

  • 数据库的操作,可以实现多种数据库的CRUD
  • 消息队列的实现,可以实现各种工具消息队列的入队、出队。
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值