作為一個框架,我們還沒有相應的緩存組件,下面我們就來構建我們的緩存組件。
先來定義一下接口,在 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
