構建自己的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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM