构建自己的PHP框架--构建缓存组件(1)

作为一个框架,我们还没有相应的缓存组件,下面我们就来构建我们的缓存组件。

先来定义一下接口,在 src 文件夹下创建 cache 文件夹,在cache文件夹下创建 CacheInterface.php 文件,其中定义 Cache 相应的接口,其内容如下:

<?php
namespace sf\cache;

/**
 * CacheInterface
 * @author Harry Sun <sunguangjun@126.com>
 */
interface CacheInterface
{
    /**
     * Builds a normalized cache key from a given key.
     */
    public function buildKey($key);

    /**
     * Retrieves a value from cache with a specified key.
     */
    public function get($key);

    /**
     * Checks whether a specified key exists in the cache.
     */
    public function exists($key);

    /**
     * Retrieves multiple values from cache with the specified keys.
     */
    public function mget($keys);

    /**
     * Stores a value identified by a key into cache.
     */
    public function set($key, $value, $duration = 0);

    /**
     * Stores multiple items in cache. Each item contains a value identified by a key.
     */
    public function mset($items, $duration = 0);

    /**
     * Stores a value identified by a key into cache if the cache does not contain this key.
     * Nothing will be done if the cache already contains the key.
     */
    public function add($key, $value, $duration = 0);

    /**
     * Stores multiple items in cache. Each item contains a value identified by a key.
     * If the cache already contains such a key, the existing value and expiration time will be preserved.
     */
    public function madd($items, $duration = 0);

    /**
     * Deletes a value with the specified key from cache
     */
    public function delete($key);

    /**
     * Deletes all values from cache.
     */
    public function flush();
}

定义了 buildKey/get/mget/set/mset/exists/add/madd/delete/flush接口,对应功能如下:

  • buildKey:构建真正的 key,避免特殊字符影响实现
  • get:根据 key 获取缓存的值
  • mget:根据 keys 数组获取多个缓存值
  • set:根据 key 设置缓存的值
  • mset:根据数组设置多个缓存值
  • exists:判断 key 是否存在
  • add:如果 key 不存在就设置缓存值,否则返回false
  • madd:根据数组,判断相应的 key 不存在就设置缓存值
  • delete:根据 key 删除一个缓存
  • flush:删除所有的缓存

实现缓存,可以使用很多方式,比如使用文件、数据库、memcache 以及 Redis 等。

我们今天先使用文件缓存来实现相应的接口。

其主要思想就是,每一个 key 都对应一个文件,缓存的内容序列化一下,存入到文件中,取出时再反序列化一下。剩下的基本都是相应的文件操作了。

在 src/cache 文件夹下创建 FileCache.php 文件,其内容如下:

<?php
namespace sf\cache;

/**
 * CacheInterface
 * @author Harry Sun <sunguangjun@126.com>
 */
class FileCache implements CacheInterface
{
    /**
     * @var string the directory to store cache files.
     * 缓存文件的地址,例如/Users/jun/projects/www/simple-framework/runtime/cache/
     */
    public $cachePath;
    /**
     * Builds a normalized cache key from a given key.
     */
    public function buildKey($key)
    {
        if (!is_string($key)) {
            // 不是字符串就json_encode一把,转成字符串,也可以用其他方法
            $key = json_encode($key);
        }
        return md5($key);
    }

    /**
     * Retrieves a value from cache with a specified key.
     */
    public function get($key)
    {
        $key = $this->buildKey($key);
        $cacheFile = $this->cachePath . $key;
        // filemtime用来获取文件的修改时间
        if (@filemtime($cacheFile) > time()) {
            // file_get_contents用来获取文件内容,unserialize用来反序列化文件内容
            return unserialize(@file_get_contents($cacheFile));
        } else {
            return false;
        }
    }

    /**
     * Checks whether a specified key exists in the cache.
     */
    public function exists($key)
    {
        $key = $this->buildKey($key);
        $cacheFile = $this->cachePath . $key;
        // 用修改时间标记过期时间,存入时会做相应的处理
        return @filemtime($cacheFile) > time();
    }

    /**
     * Retrieves multiple values from cache with the specified keys.
     */
    public function mget($keys)
    {
        $results = [];
        foreach ($keys as $key) {
            $results[$key] = $this->get($key);
        }
        return $results;
    }

    /**
     * Stores a value identified by a key into cache.
     */
    public function set($key, $value, $duration = 0)
    {
        $key = $this->buildKey($key);
        $cacheFile = $this->cachePath . $key;
        // serialize用来序列化缓存内容
        $value = serialize($value);
        // file_put_contents用来将序列化之后的内容写入文件,LOCK_EX表示写入时会对文件加锁
        if (@file_put_contents($cacheFile, $value, LOCK_EX) !== false) {
            if ($duration <= 0) {
                // 不设置过期时间,设置为一年,这是因为用文件的修改时间来做过期时间造成的
                 // redis/memcache 等都不会有这个问题
                $duration = 31536000; // 1 year
            }
            // touch用来设置修改时间,过期时间为当前时间加上$duration
            return touch($cacheFile, $duration + time());
        } else {
            return false;
        }
    }

    /**
     * Stores multiple items in cache. Each item contains a value identified by a key.
     */
    public function mset($items, $duration = 0)
    {
        $failedKeys = [];
        foreach ($items as $key => $value) {
            if ($this->set($key, $value, $duration) === false) {
                $failedKeys[] = $key;
            }
        }

        return $failedKeys;
    }

    /**
     * Stores a value identified by a key into cache if the cache does not contain this key.
     */
    public function add($key, $value, $duration = 0)
    {
        //  key不存在,就设置缓存
        if (!$this->exists($key)) {
            return $this->set($key, $value, $duration);
        } else {
            return false;
        }
    }

    /**
     * Stores multiple items in cache. Each item contains a value identified by a key.
     */
    public function madd($items, $duration = 0)
    {
        $failedKeys = [];
        foreach ($items as $key => $value) {
            if ($this->add($key, $value, $duration) === false) {
                $failedKeys[] = $key;
            }
        }

        return $failedKeys;
    }

    /**
     * Deletes a value with the specified key from cache
     */
    public function delete($key)
    {
        $key = $this->buildKey($key);
        $cacheFile = $this->cachePath . $key;
        // unlink用来删除文件
        return unlink($cacheFile);
    }

    /**
     * Deletes all values from cache.
     * Be careful of performing this operation if the cache is shared among multiple applications.
     * @return boolean whether the flush operation was successful.
     */
    public function flush()
    {
        // 打开cache文件所在目录
        $dir = @dir($this->cachePath);

        // 列出目录中的所有文件
        while (($file = $dir->read()) !== false) {
            if ($file !== '.' && $file !== '..') {
                unlink($this->cachePath . $file);
            }
        }

        // 关闭目录
        $dir->close();
    }
}

相关实现的解释都直接写在code中的注释里了。

然后我们来测试一下我们的缓存组件,首先我们需要添加一下配置文件,在 config 文件夹下创建 cache.php 文件,配置如下内容:

<?php
return [
    'class' => '\sf\cache\FileCache',
    'cachePath' => SF_PATH . '/runtime/cache/'
];

然后在 SiteController.php 中简单使用如下:

    public function actionCache()
    {
        $cache = Sf::createObject('cache');
        $cache->set('test', '我就是测试一下缓存组件');
        $result = $cache->get('test');
        $cache->flush();
        echo $result;
    }

访问 http://localhost/simple-framework/public/index.php?r=site/cache 路径,得到结果如下:

我就是测试一下缓存组件

这样我们完成了使用文件的缓存组件。

好了,今天就先到这里。项目内容和博客内容也都会放到Github上,欢迎大家提建议。

code:https://github.com/CraryPrimitiveMan/simple-framework/tree/0.9

blog project:https://github.com/CraryPrimitiveMan/create-your-own-php-framework

posted @ 2016-04-24 21:06  疯狂的原始人  阅读(1692)  评论(2编辑  收藏  举报