介紹
緩存是提升應(yīng)用性能的常用手段,為框架中最通用的功能凫佛,每個(gè)框架也都推出專屬的浑塞、功能多
樣的緩存庫借跪。這些差別使得開發(fā)人員不得不學(xué)習(xí)多種系統(tǒng),而很多可能是他們并不需要的功能酌壕。
此外掏愁,緩存庫的開發(fā)者同樣面臨著一個(gè)窘境歇由,是只支持有限數(shù)量的幾個(gè)框架還是創(chuàng)建一堆龐
大的適配器類。
一個(gè)通用的緩存系統(tǒng)接口可以解決掉這些問題果港。庫和框架的開發(fā)人員能夠知道緩存系統(tǒng)會(huì)按照他們所
預(yù)期的方式工作沦泌,緩存系統(tǒng)的開發(fā)人員只需要實(shí)現(xiàn)單一的接口,而不用去開發(fā)各種各樣的適配器辛掠。
目標(biāo)
本 PSR 的目標(biāo)是:創(chuàng)建一套通用的接口規(guī)范谢谦,能夠讓開發(fā)人員整合到現(xiàn)有框架和系統(tǒng),而不需要去
開發(fā)框架專屬的適配器類萝衩。
關(guān)于「能愿動(dòng)詞」的使用
為了避免歧義回挽,文檔大量使用了「能愿動(dòng)詞」,對應(yīng)的解釋如下:
-
必須 (MUST)
:絕對猩谊,嚴(yán)格遵循厅各,請照做,無條件遵守预柒; -
一定不可 (MUST NOT)
:禁令队塘,嚴(yán)令禁止; -
應(yīng)該 (SHOULD)
:強(qiáng)烈建議這樣做宜鸯,但是不強(qiáng)求憔古; -
不該 (SHOULD NOT)
:強(qiáng)烈不建議這樣做,但是不強(qiáng)求淋袖; -
可以 (MAY)
和可選 (OPTIONAL)
:選擇性高一點(diǎn)鸿市,在這個(gè)文檔內(nèi),此詞語使用較少即碗;
參見:RFC 2119
定義
調(diào)用類庫 (Calling Library) - 調(diào)用者焰情,使用緩存服務(wù)的類庫,這個(gè)類庫調(diào)用緩存服務(wù)剥懒,調(diào)用的
是此緩存接口規(guī)范的具體「實(shí)現(xiàn)類庫」内舟,調(diào)用者不需要知道任何「緩存服務(wù)」的具體實(shí)現(xiàn)。實(shí)現(xiàn)類庫 (Implementing Library) - 此類庫是對「緩存接口規(guī)范」的具體實(shí)現(xiàn)初橘,封裝起來的緩存服務(wù)验游,供「調(diào)用類庫」使用。實(shí)現(xiàn)類庫 必須 提供 PHP 類來實(shí)現(xiàn)
Cache\CacheItemPoolInterface
和Cache\CacheItemInterface
接口保檐。
實(shí)現(xiàn)類庫 必須 支持最小的如下描述的 TTL 功能耕蝉,秒級(jí)別的精準(zhǔn)度。生存時(shí)間值 (TTL - Time To Live) - 定義了緩存可以存活的時(shí)間夜只,以秒為單位的整數(shù)值垒在。
-
過期時(shí)間 (Expiration) - 定義準(zhǔn)確的過期時(shí)間點(diǎn),一般為緩存存儲(chǔ)發(fā)生的時(shí)間點(diǎn)加上 TTL 時(shí)
間值扔亥,也可以指定一個(gè) DateTime 對象场躯。假如一個(gè)緩存項(xiàng)的 TTL 設(shè)置為 300 秒谈为,保存于 1:30:00 ,那么緩存項(xiàng)的過期時(shí)間為 1:35:00推盛。
實(shí)現(xiàn)類庫 可以 讓緩存項(xiàng)提前過期,但是 必須 在到達(dá)過期時(shí)間時(shí)立即把緩存項(xiàng)標(biāo)示為
過期谦铃。如果調(diào)用類庫在保存一個(gè)緩存項(xiàng)的時(shí)候未設(shè)置「過期時(shí)間」耘成、或者設(shè)置了null
作為過期
時(shí)間(或者 TTL 設(shè)置為null
),實(shí)現(xiàn)類庫 可以 使用默認(rèn)自行配置的一個(gè)時(shí)間驹闰。如果沒
有默認(rèn)時(shí)間瘪菌,實(shí)現(xiàn)類庫 必須把存儲(chǔ)時(shí)間當(dāng)做永久性
存儲(chǔ),或者按照底層驅(qū)動(dòng)能支持的
最長時(shí)間作為保持時(shí)間嘹朗。 鍵 (KEY) - 長度大于 1 的字串师妙,用作緩存項(xiàng)在緩存系統(tǒng)里的唯一標(biāo)識(shí)符。實(shí)現(xiàn)類庫
必須 支持「鍵」規(guī)則A-Z
,a-z
,0-9
,_
, 和.
任何順序的 UTF-8 編碼屹培,長度
小于 64 位默穴。實(shí)現(xiàn)類庫 可以 支持更多的編碼或者更長的長度,不過 必須 支持至少以上指定
的編碼和長度褪秀。實(shí)現(xiàn)類庫可自行實(shí)現(xiàn)對「鍵」的轉(zhuǎn)義蓄诽,但是 必須 保證能夠無損的返回「鍵」字串。以下
的字串作為系統(tǒng)保留:{}()/\@:
媒吗,一定不可 作為「鍵」的命名支持仑氛。命中 (Hit) - 一個(gè)緩存的命中,指的是當(dāng)調(diào)用類庫使用「鍵」在請求一個(gè)緩存項(xiàng)的時(shí)候闸英,在緩存
池里能找到對應(yīng)的緩存項(xiàng)锯岖,并且此緩存項(xiàng)還未過期,并且此數(shù)據(jù)不會(huì)因?yàn)槿魏卧虺霈F(xiàn)錯(cuò)誤甫何。調(diào)用類
庫 應(yīng)該 確保先驗(yàn)證下isHit()
有命中后才調(diào)用get()
獲取數(shù)據(jù)出吹。未命中 (Miss) - 一個(gè)緩存未命中,是完全的上面描述的「命中」的相反辙喂。指的是當(dāng)調(diào)用類庫使用「鍵」在請求一個(gè)緩存項(xiàng)的時(shí)候趋箩,在緩存池里未能找到對應(yīng)的緩存項(xiàng),或者此緩存項(xiàng)已經(jīng)過期加派,或者此數(shù)據(jù)因?yàn)槿魏卧虺霈F(xiàn)錯(cuò)誤叫确。一個(gè)過期的緩存項(xiàng),必須 被當(dāng)做
未命中
來對待芍锦。延遲 (Deferred) - 一個(gè)延遲的緩存竹勉,指的是這個(gè)緩存項(xiàng)可能不會(huì)立刻被存儲(chǔ)到物理緩存池里。一個(gè)
緩存池對象 可以 對一個(gè)指定延遲的緩存項(xiàng)進(jìn)行延遲存儲(chǔ)娄琉,這樣做的好處是可以利用一些緩存服務(wù)器提供
的批量插入功能次乓。緩存池 必須 能對所有延遲緩存最終能持久化吓歇,并且不會(huì)丟失。可以 在調(diào)用類庫還未發(fā)起保存請求之前就做持久化票腰。當(dāng)調(diào)用類庫調(diào)用commit()
方法時(shí)城看,所有的延遲緩存都 必須
做持久化。實(shí)現(xiàn)類庫 可以 自行決定使用什么邏輯來觸發(fā)數(shù)據(jù)持久化杏慰,如對象的析構(gòu)方法 (destructor)
內(nèi)测柠、調(diào)用save()
時(shí)持久化、倒計(jì)時(shí)保存或者觸及最大數(shù)量時(shí)保存等缘滥。當(dāng)請求一個(gè)延遲
緩存項(xiàng)時(shí)轰胁,必須 返回一個(gè)延遲,未持久化的緩存項(xiàng)對象朝扼。
數(shù)據(jù)
實(shí)現(xiàn)類庫 必須 支持所有的可序列化的 PHP 數(shù)據(jù)類型赃阀,包含:
- 字符串 - 任何大小的 PHP 兼容字符串
- 整數(shù) - PHP 支持的低于 64 位的有符號(hào)整數(shù)值
- 浮點(diǎn)數(shù) - 所有的有符號(hào)浮點(diǎn)數(shù)
- 布爾 - true 和 false.
-
Null -
null
值 - 數(shù)組 - 各種形式的 PHP 數(shù)組
-
對象(Object) - 所有的支持無損序列化和反序列化的對象,如:
$o == unserialize(serialize($o))
擎颖。對象 可以
使用 PHP 的Serializable
接口榛斯,__sleep()
或者__wakeup()
魔術(shù)方法,或者在合適的情況下搂捧,使用其他類似的語言特性肖抱。
所有存進(jìn)實(shí)現(xiàn)類庫的數(shù)據(jù),都 必須
能做到原封不動(dòng)的取出异旧。連類型也 必須
是完全一致意述,如果
存進(jìn)緩存的是字符串 5,取出來的卻是整數(shù)值 5 的話吮蛹,可以算作嚴(yán)重的錯(cuò)誤荤崇。實(shí)現(xiàn)類庫 可以 使用 PHP 的「serialize()/unserialize() 方法」作為底層實(shí)現(xiàn),不過不強(qiáng)迫這樣做潮针。對于他們的兼容性术荤,以能支持所有數(shù)據(jù)類型作為基準(zhǔn)線。
實(shí)在無法「完整取出」存入的數(shù)據(jù)的話每篷,實(shí)現(xiàn)類庫 必須 把「緩存丟失」標(biāo)示作為返回瓣戚,而不是損壞了的數(shù)據(jù)。
主要概念
緩存池 Pool
緩存池包含緩存系統(tǒng)里所有緩存數(shù)據(jù)的集合焦读。緩存池邏輯上是所有緩存項(xiàng)存儲(chǔ)的倉庫子库,所有存儲(chǔ)進(jìn)去的數(shù)據(jù),
都能從緩存池里取出來矗晃,所有的對緩存的操作仑嗅,都發(fā)生在緩存池子里。
緩存項(xiàng) Items
一條緩存項(xiàng)在緩存池里代表了一對「鍵/值」對應(yīng)的數(shù)據(jù),「鍵」被視為每一個(gè)緩存項(xiàng)主鍵仓技,是緩存項(xiàng)的
唯一標(biāo)識(shí)符鸵贬,必須 是不可變更的,當(dāng)然脖捻,「值」可以 任意變更阔逼。
錯(cuò)誤處理
緩存對應(yīng)用性能起著至關(guān)重要的作用,但是地沮,無論在任何情況下嗜浮,緩存 一定不可 作為應(yīng)用程序不
可或缺的核心功能。
緩存系統(tǒng)里的錯(cuò)誤 一定不可 導(dǎo)致應(yīng)用程序故障诉濒,所以周伦,實(shí)現(xiàn)類庫 一定不可 拋出任何除了
此接口規(guī)范定義的以外的異常夕春,并且 必須 捕捉包括底層存儲(chǔ)驅(qū)動(dòng)拋出的異常未荒,不讓其冒泡至超
出緩存系統(tǒng)內(nèi)。
實(shí)現(xiàn)類庫 應(yīng)該 對此類錯(cuò)誤進(jìn)行記錄及志,或者以任何形式通知管理員片排。
調(diào)用類庫發(fā)起刪除緩存項(xiàng)的請求,或者清空整個(gè)緩沖池子的請求速侈,「鍵」不存在的話 必須 不能
當(dāng)成是有錯(cuò)誤發(fā)生率寡。后置條件是一樣的,如果取數(shù)據(jù)時(shí)倚搬,「鍵」不存在的話 必須 不能當(dāng)成是有錯(cuò)誤發(fā)生
接口
CacheItemInterface
CacheItemInterface
定義了緩存系統(tǒng)里的一個(gè)緩存項(xiàng)冶共。每一個(gè)緩存項(xiàng) 必須 有一個(gè)「鍵」與之相
關(guān)聯(lián),此「鍵」通常是通過 Cache\CacheItemPoolInterface 來設(shè)置每界。
Cache\CacheItemInterface 對象把緩存項(xiàng)的存儲(chǔ)進(jìn)行了封裝捅僵,每一個(gè) Cache\CacheItemInterface 由一個(gè) Cache\CacheItemPoolInterface 對象生成,CacheItemPoolInterface 負(fù)責(zé)一些必須的設(shè)置眨层,并且給對象設(shè)置具有 唯一性
的「鍵」庙楚。
Cache\CacheItemInterface 對象 必須 能夠存儲(chǔ)和取出任何類型的,在「數(shù)據(jù)」章節(jié)定義的 PHP 數(shù)值趴樱。
調(diào)用類庫 一定不可 擅自初始化「CacheItemInterface」對象馒闷,「緩存項(xiàng)」只能使用「CacheItemPoolInterface」對象的 getItem()
方法來獲取。調(diào)用類庫 一定不可 假設(shè)
由一個(gè)實(shí)現(xiàn)類庫創(chuàng)建的「緩存項(xiàng)」能被另一個(gè)實(shí)現(xiàn)類庫完全兼容叁征。
namespace Psr\Cache;
/**
* CacheItemInterface 定了緩存系統(tǒng)里對緩存項(xiàng)操作的接口
*/
interface CacheItemInterface
{
/**
* 返回當(dāng)前緩存項(xiàng)的「鍵」
*
* 「鍵」由實(shí)現(xiàn)類庫來加載纳账,并且高層的調(diào)用者(如:CacheItemPoolInterface)
* **應(yīng)該** 能使用此方法來獲取到「鍵」的信息。
*
* @return string
* 當(dāng)前緩存項(xiàng)的「鍵」
*/
public function getKey();
/**
* 憑借此緩存項(xiàng)的「鍵」從緩存系統(tǒng)里面取出緩存項(xiàng)捺疼。
*
* 取出的數(shù)據(jù) **必須** 跟使用 `set()` 存進(jìn)去的數(shù)據(jù)是一模一樣的塞祈。
*
* 如果 `isHit()` 返回 false 的話,此方法必須返回 `null`,需要注意的是 `null`
* 本來就是一個(gè)合法的緩存數(shù)據(jù)议薪,所以你 **應(yīng)該** 使用 `isHit()` 方法來辨別到底是
* "返回 null 數(shù)據(jù)" 還是 "緩存里沒有此數(shù)據(jù)"尤蛮。
*
* @return mixed
* 此緩存項(xiàng)的「鍵」對應(yīng)的「值」,如果找不到的話斯议,返回 `null`
*/
public function get();
/**
* 確認(rèn)緩存項(xiàng)的檢查是否命中产捞。
*
* 注意: 調(diào)用此方法和調(diào)用 `get()` 時(shí) **一定不可** 有先后順序之分。
*
* @return bool
* 如果緩沖池里有命中的話哼御,返回 `true`坯临,反之返回 `false`
*/
public function isHit();
/**
* 為此緩存項(xiàng)設(shè)置「值」。
*
* 參數(shù) $value 可以是所有能被 PHP 序列化的數(shù)據(jù)恋昼,序列化的邏輯
* 需要在實(shí)現(xiàn)類庫里書寫看靠。
*
* @param mixed $value
* 將被存儲(chǔ)的可序列化的數(shù)據(jù)。
*
* @return static
* 返回當(dāng)前對象液肌。
*/
public function set($value);
/**
* 設(shè)置緩存項(xiàng)的準(zhǔn)確過期時(shí)間點(diǎn)挟炬。
*
* @param \DateTimeInterface $expiration
*
* 過期的準(zhǔn)確時(shí)間點(diǎn),過了這個(gè)時(shí)間點(diǎn)后嗦哆,緩存項(xiàng)就 **必須** 被認(rèn)為是過期了的谤祖。
* 如果明確的傳參 `null` 的話,**可以** 使用一個(gè)默認(rèn)的時(shí)間老速。
* 如果沒有設(shè)置的話粥喜,緩存 **應(yīng)該** 存儲(chǔ)到底層實(shí)現(xiàn)的最大允許時(shí)間。
*
* @return static
* 返回當(dāng)前對象橘券。
*/
public function expiresAt($expiration);
/**
* 設(shè)置緩存項(xiàng)的過期時(shí)間额湘。
*
* @param int|\DateInterval $time
* 以秒為單位的過期時(shí)長,過了這段時(shí)間后旁舰,緩存項(xiàng)就 **必須** 被認(rèn)為是過期了的锋华。
* 如果明確的傳參 `null` 的話,**可以** 使用一個(gè)默認(rèn)的時(shí)間鬓梅。
* 如果沒有設(shè)置的話供置,緩存 **應(yīng)該** 存儲(chǔ)到底層實(shí)現(xiàn)的最大允許時(shí)間。
*
* @return static
* 返回當(dāng)前對象
*/
public function expiresAfter($time);
}
CacheItemPoolInterface
Cache\CacheItemPoolInterface 的主要目的是從調(diào)用類庫接收「鍵」绽快,然后返回對應(yīng)的 Cache\CacheItemInterface 對象芥丧。
此接口也是作為主要的,與整個(gè)緩存集合交互的方式坊罢。所有的配置和初始化由實(shí)現(xiàn)類庫自行實(shí)現(xiàn)续担。
namespace Psr\Cache;
/**
* CacheItemPoolInterface 生成 CacheItemInterface 對象
*/
interface CacheItemPoolInterface
{
/**
* 返回「鍵」對應(yīng)的一個(gè)緩存項(xiàng)。
*
* 此方法 **必須** 返回一個(gè) CacheItemInterface 對象活孩,即使是找不到對應(yīng)的緩存項(xiàng)
* 也 **一定不可** 返回 `null`物遇。
*
* @param string $key
* 用來搜索緩存項(xiàng)的「鍵」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值,\Psr\Cache\InvalidArgumentException 異常會(huì)被拋出询兴。
*
* @return CacheItemInterface
* 對應(yīng)的緩存項(xiàng)乃沙。
*/
public function getItem($key);
/**
* 返回一個(gè)可供遍歷的緩存項(xiàng)集合。
*
* @param array $keys
* 由一個(gè)或者多個(gè)「鍵」組成的數(shù)組诗舰。
*
* @throws InvalidArgumentException
* 如果 $keys 里面有哪個(gè)「鍵」不是合法警儒,\Psr\Cache\InvalidArgumentException 異常
* 會(huì)被拋出。
*
* @return array|\Traversable
* 返回一個(gè)可供遍歷的緩存項(xiàng)集合眶根,集合里每個(gè)元素的標(biāo)識(shí)符由「鍵」組成蜀铲,即使即使是找不到對
* 的緩存項(xiàng),也要返回一個(gè)「CacheItemInterface」對象到對應(yīng)的「鍵」中属百。
* 如果傳參的數(shù)組為空记劝,也需要返回一個(gè)空的可遍歷的集合。
*/
public function getItems(array $keys = array());
/**
* 檢查緩存系統(tǒng)中是否有「鍵」對應(yīng)的緩存項(xiàng)族扰。
*
* 注意: 此方法應(yīng)該調(diào)用 `CacheItemInterface::isHit()` 來做檢查操作厌丑,而不是
* `CacheItemInterface::get()`
*
* @param string $key
* 用來搜索緩存項(xiàng)的「鍵」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值别伏,\Psr\Cache\InvalidArgumentException 異常會(huì)被拋出蹄衷。
*
* @return bool
* 如果存在「鍵」對應(yīng)的緩存項(xiàng)即返回 true忧额,否則 false
*/
public function hasItem($key);
/**
* 清空緩沖池
*
* @return bool
* 成功返回 true厘肮,有錯(cuò)誤發(fā)生返回 false
*/
public function clear();
/**
* 從緩沖池里移除某個(gè)緩存項(xiàng)
*
* @param string $key
* 用來搜索緩存項(xiàng)的「鍵」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值睦番,\Psr\Cache\InvalidArgumentException 異常會(huì)被拋出类茂。
*
* @return bool
* 成功返回 true,有錯(cuò)誤發(fā)生返回 false
*/
public function deleteItem($key);
/**
* 從緩沖池里移除多個(gè)緩存項(xiàng)
*
* @param array $keys
* 由一個(gè)或者多個(gè)「鍵」組成的數(shù)組托嚣。
*
* @throws InvalidArgumentException
* 如果 $keys 里面有哪個(gè)「鍵」不是合法巩检,\Psr\Cache\InvalidArgumentException 異常
* 會(huì)被拋出。
*
* @return bool
* 成功返回 true示启,有錯(cuò)誤發(fā)生返回 false
*/
public function deleteItems(array $keys);
/**
* 立刻為「CacheItemInterface」對象做數(shù)據(jù)持久化兢哭。
*
* @param CacheItemInterface $item
* 將要被存儲(chǔ)的緩存項(xiàng)
*
* @return bool
* 成功返回 true,有錯(cuò)誤發(fā)生返回 false
*/
public function save(CacheItemInterface $item);
/**
* 稍后為「CacheItemInterface」對象做數(shù)據(jù)持久化夫嗓。
*
* @param CacheItemInterface $item
* 將要被存儲(chǔ)的緩存項(xiàng)
*
* @return bool
* 成功返回 true迟螺,有錯(cuò)誤發(fā)生返回 false
*/
public function saveDeferred(CacheItemInterface $item);
/**
* 提交所有的正在隊(duì)列里等待的請求到數(shù)據(jù)持久層,配合 `saveDeferred()` 使用
*
* @return bool
* 成功返回 true舍咖,有錯(cuò)誤發(fā)生返回 false
*/
public function commit();
}
CacheException
此異常用于緩存系統(tǒng)發(fā)生的所有嚴(yán)重錯(cuò)誤矩父,包括但不限制于 緩存系統(tǒng)配置,如連接到緩存服務(wù)器出錯(cuò)排霉、錯(cuò)
誤的用戶身份認(rèn)證等窍株。
所有的實(shí)現(xiàn)類庫拋出的異常都 必須 實(shí)現(xiàn)此接口。
namespace Psr\Cache;
/**
* 被所有的實(shí)現(xiàn)類庫拋出的異常繼承的「異常接口」
*/
interface CacheException
{
}
InvalidArgumentException
namespace Psr\Cache;
/**
* 傳參錯(cuò)誤拋出的異常接口
*
* 當(dāng)一個(gè)錯(cuò)誤或者非法的傳參發(fā)生時(shí),**必須** 拋出一個(gè)繼承了
* Psr\Cache\InvalidArgumentException 的異常
*/
interface InvalidArgumentException extends CacheException
{
}