> 官網原文網址:http://www.php-fig.org/psr/psr-6/
## 介紹
緩存是提升應用性能的常用手段,為框架中最通用的功能,每個框架也都推出專屬的、功能多
樣的緩存庫。這些差別使得開發人員不得不學習多種系統,而很多可能是他們并不需要的功能。
此外,緩存庫的開發者同樣面臨著一個窘境,是只支持有限數量的幾個框架還是創建一堆龐
大的適配器類。
一個通用的緩存系統接口可以解決掉這些問題。庫和框架的開發人員能夠知道緩存系統會按照他們所
預期的方式工作,緩存系統的開發人員只需要實現單一的接口,而不用去開發各種各樣的適配器。
## 目標
本 PSR 的目標是:創建一套通用的接口規范,能夠讓開發人員整合到現有框架和系統,而不需要去
開發框架專屬的適配器類。
## 關于「能愿動詞」的使用
為了避免歧義,文檔大量使用了「能愿動詞」,對應的解釋如下:
* `必須 (MUST)`:絕對,嚴格遵循,請照做,無條件遵守;
* `一定不可 (MUST NOT)`:禁令,嚴令禁止;
* `應該 (SHOULD)` :強烈建議這樣做,但是不強求;
* `不該 (SHOULD NOT)`:強烈不建議這樣做,但是不強求;
* `可以 (MAY)` 和 `可選 (OPTIONAL)` :選擇性高一點,在這個文檔內,此詞語使用較少;
> 參見:[RFC 2119](http://www.ietf.org/rfc/rfc2119.txt)
## 定義
* **調用類庫 (Calling Library)** - 調用者,使用緩存服務的類庫,這個類庫調用緩存服務,調用的
是此緩存接口規范的具體「實現類庫」,調用者不需要知道任何「緩存服務」的具體實現。
* **實現類庫 (Implementing Library)** - 此類庫是對「緩存接口規范」的具體實現,封裝起來的緩存服務,供「調用類庫」使用。實現類庫 **必須** 提供 PHP 類來實現
`Cache\CacheItemPoolInterface` 和 `Cache\CacheItemInterface` 接口。
實現類庫 **必須** 支持最小的如下描述的 TTL 功能,秒級別的精準度。
* **生存時間值 (TTL - Time To Live)** - 定義了緩存可以存活的時間,以秒為單位的整數值。
* **過期時間 (Expiration)** - 定義準確的過期時間點,一般為緩存存儲發生的時間點加上 TTL 時
間值,也可以指定一個 DateTime 對象。
假如一個緩存項的 TTL 設置為 300 秒,保存于 1:30:00 ,那么緩存項的過期時間為 1:35:00。
實現類庫 **可以** 讓緩存項提前過期,但是 **必須** 在到達過期時間時立即把緩存項標示為
過期。如果調用類庫在保存一個緩存項的時候未設置「過期時間」、或者設置了 `null` 作為過期
時間(或者 TTL 設置為 `null`),實現類庫 **可以** 使用默認自行配置的一個時間。如果沒
有默認時間,實現類庫 **必須**把存儲時間當做 `永久性` 存儲,或者按照底層驅動能支持的
最長時間作為保持時間。
* **鍵 (KEY)** - 長度大于 1 的字串,用作緩存項在緩存系統里的唯一標識符。實現類庫
**必須** 支持「鍵」規則 `A-Z`, `a-z`, `0-9`, `_`, 和 `.` 任何順序的 UTF-8 編碼,長度
小于 64 位。實現類庫 **可以** 支持更多的編碼或者更長的長度,不過 **必須** 支持至少以上指定
的編碼和長度。實現類庫可自行實現對「鍵」的轉義,但是 **必須** 保證能夠無損的返回「鍵」字串。以下
的字串作為系統保留: `{}()/\@:`,**一定不可** 作為「鍵」的命名支持。
* **命中 (Hit)** - 一個緩存的命中,指的是當調用類庫使用「鍵」在請求一個緩存項的時候,在緩存
池里能找到對應的緩存項,并且此緩存項還未過期,并且此數據不會因為任何原因出現錯誤。調用類
庫 **應該** 確保先驗證下 `isHit()` 有命中后才調用 `get()` 獲取數據。
* **未命中 (Miss)** - 一個緩存未命中,是完全的上面描述的「命中」的相反。指的是當調用類庫使用「鍵」在請求一個緩存項的時候,在緩存池里未能找到對應的緩存項,或者此緩存項已經過期,或者此數據因為任何原因出現錯誤。一個過期的緩存項,**必須** 被當做 `未命中` 來對待。
* **延遲 (Deferred)** - 一個延遲的緩存,指的是這個緩存項可能不會立刻被存儲到物理緩存池里。一個
緩存池對象 **可以** 對一個指定延遲的緩存項進行延遲存儲,這樣做的好處是可以利用一些緩存服務器提供
的批量插入功能。緩存池 **必須** 能對所有延遲緩存最終能持久化,并且不會丟失。**可以** 在調用類庫還未發起保存請求之前就做持久化。當調用類庫調用 `commit()` 方法時,所有的延遲緩存都 **必須**
做持久化。實現類庫 **可以** 自行決定使用什么邏輯來觸發數據持久化,如對象的 `析構方法 (destructor)` 內、調用 `save()` 時持久化、倒計時保存或者觸及最大數量時保存等。當請求一個延遲
緩存項時,**必須** 返回一個延遲,未持久化的緩存項對象。
## 數據
實現類庫 **必須** 支持所有的可序列化的 PHP 數據類型,包含:
* **字符串** - 任何大小的 PHP 兼容字符串
* **整數** - PHP 支持的低于 64 位的有符號整數值
* **浮點數** - 所有的有符號浮點數
* **布爾** - true 和 false.
* **Null** - `null` 值
* **數組** - 各種形式的 PHP 數組
* **對象(Object)** - 所有的支持無損序列化和反序列化的對象,如:` $o == unserialize(serialize($o))` 。對象 **可以**
使用 PHP 的 `Serializable` 接口,`__sleep()` 或者 `__wakeup()` 魔術方法,或者在合適的情況下,使用其他類似的語言特性。
所有存進實現類庫的數據,都 `必須` 能做到原封不動的取出。連類型也 `必須` 是完全一致,如果
存進緩存的是字符串 5,取出來的卻是整數值 5 的話,可以算作嚴重的錯誤。實現類庫 **可以** 使用 PHP 的「serialize()/unserialize() 方法」作為底層實現,不過不強迫這樣做。對于他們的兼容性,以能支持所有數據類型作為基準線。
實在無法「完整取出」存入的數據的話,實現類庫 **必須** 把「緩存丟失」標示作為返回,而不是損壞了的數據。
## 主要概念
### 緩存池 Pool
緩存池包含緩存系統里所有緩存數據的集合。緩存池邏輯上是所有緩存項存儲的倉庫,所有存儲進去的數據,
都能從緩存池里取出來,所有的對緩存的操作,都發生在緩存池子里。
### 緩存項 Items
一條緩存項在緩存池里代表了一對「鍵/值」對應的數據,「鍵」被視為每一個緩存項主鍵,是緩存項的
唯一標識符,**必須** 是不可變更的,當然,「值」**可以** 任意變更。
## 錯誤處理
緩存對應用性能起著至關重要的作用,但是,無論在任何情況下,緩存 **一定不可** 作為應用程序不
可或缺的核心功能。
緩存系統里的錯誤 **一定不可** 導致應用程序故障,所以,實現類庫 **一定不可** 拋出任何除了
此接口規范定義的以外的異常,并且 **必須** 捕捉包括底層存儲驅動拋出的異常,不讓其冒泡至超
出緩存系統內。
實現類庫 **應該** 對此類錯誤進行記錄,或者以任何形式通知管理員。
調用類庫發起刪除緩存項的請求,或者清空整個緩沖池子的請求,「鍵」不存在的話 **必須** 不能
當成是有錯誤發生。后置條件是一樣的,如果取數據時,「鍵」不存在的話 **必須** 不能當成是有錯誤發生
## 接口
### CacheItemInterface
`CacheItemInterface` 定義了緩存系統里的一個緩存項。每一個緩存項 **必須** 有一個「鍵」與之相
關聯,此「鍵」通常是通過 Cache\CacheItemPoolInterface 來設置。
Cache\CacheItemInterface 對象把緩存項的存儲進行了封裝,每一個 Cache\CacheItemInterface 由一個 Cache\CacheItemPoolInterface 對象生成,CacheItemPoolInterface 負責一些必須的設置,并且給對象設置具有 `唯一性` 的「鍵」。
Cache\CacheItemInterface 對象 **必須** 能夠存儲和取出任何類型的,在「數據」章節定義的 PHP 數值。
調用類庫 **一定不可** 擅自初始化「CacheItemInterface」對象,「緩存項」只能使用「CacheItemPoolInterface」對象的 `getItem()` 方法來獲取。調用類庫 **一定不可** 假設
由一個實現類庫創建的「緩存項」能被另一個實現類庫完全兼容。
```php
namespace Psr\Cache;
/**
* CacheItemInterface 定了緩存系統里對緩存項操作的接口
*/
interface CacheItemInterface
{
/**
* 返回當前緩存項的「鍵」
*
* 「鍵」由實現類庫來加載,并且高層的調用者(如:CacheItemPoolInterface)
* **應該** 能使用此方法來獲取到「鍵」的信息。
*
* @return string
* 當前緩存項的「鍵」
*/
public function getKey();
/**
* 憑借此緩存項的「鍵」從緩存系統里面取出緩存項。
*
* 取出的數據 **必須** 跟使用 `set()` 存進去的數據是一模一樣的。
*
* 如果 `isHit()` 返回 false 的話,此方法必須返回 `null`,需要注意的是 `null`
* 本來就是一個合法的緩存數據,所以你 **應該** 使用 `isHit()` 方法來辨別到底是
* "返回 null 數據" 還是 "緩存里沒有此數據"。
*
* @return mixed
* 此緩存項的「鍵」對應的「值」,如果找不到的話,返回 `null`
*/
public function get();
/**
* 確認緩存項的檢查是否命中。
*
* 注意: 調用此方法和調用 `get()` 時 **一定不可** 有先后順序之分。
*
* @return bool
* 如果緩沖池里有命中的話,返回 `true`,反之返回 `false`
*/
public function isHit();
/**
* 為此緩存項設置「值」。
*
* 參數 $value 可以是所有能被 PHP 序列化的數據,序列化的邏輯
* 需要在實現類庫里書寫。
*
* @param mixed $value
* 將被存儲的可序列化的數據。
*
* @return static
* 返回當前對象。
*/
public function set($value);
/**
* 設置緩存項的準確過期時間點。
*
* @param \DateTimeInterface $expiration
*
* 過期的準確時間點,過了這個時間點后,緩存項就 **必須** 被認為是過期了的。
* 如果明確的傳參 `null` 的話,**可以** 使用一個默認的時間。
* 如果沒有設置的話,緩存 **應該** 存儲到底層實現的最大允許時間。
*
* @return static
* 返回當前對象。
*/
public function expiresAt($expiration);
/**
* 設置緩存項的過期時間。
*
* @param int|\DateInterval $time
* 以秒為單位的過期時長,過了這段時間后,緩存項就 **必須** 被認為是過期了的。
* 如果明確的傳參 `null` 的話,**可以** 使用一個默認的時間。
* 如果沒有設置的話,緩存 **應該** 存儲到底層實現的最大允許時間。
*
* @return static
* 返回當前對象
*/
public function expiresAfter($time);
}
```
### CacheItemPoolInterface
Cache\CacheItemPoolInterface 的主要目的是從調用類庫接收「鍵」,然后返回對應的 Cache\CacheItemInterface 對象。
此接口也是作為主要的,與整個緩存集合交互的方式。所有的配置和初始化由實現類庫自行實現。
```php
namespace Psr\Cache;
/**
* CacheItemPoolInterface 生成 CacheItemInterface 對象
*/
interface CacheItemPoolInterface
{
/**
* 返回「鍵」對應的一個緩存項。
*
* 此方法 **必須** 返回一個 CacheItemInterface 對象,即使是找不到對應的緩存項
* 也 **一定不可** 返回 `null`。
*
* @param string $key
* 用來搜索緩存項的「鍵」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值,\Psr\Cache\InvalidArgumentException 異常會被拋出。
*
* @return CacheItemInterface
* 對應的緩存項。
*/
public function getItem($key);
/**
* 返回一個可供遍歷的緩存項集合。
*
* @param array $keys
* 由一個或者多個「鍵」組成的數組。
*
* @throws InvalidArgumentException
* 如果 $keys 里面有哪個「鍵」不是合法,\Psr\Cache\InvalidArgumentException 異常
* 會被拋出。
*
* @return array|\Traversable
* 返回一個可供遍歷的緩存項集合,集合里每個元素的標識符由「鍵」組成,即使即使是找不到對
* 的緩存項,也要返回一個「CacheItemInterface」對象到對應的「鍵」中。
* 如果傳參的數組為空,也需要返回一個空的可遍歷的集合。
*/
public function getItems(array $keys = array());
/**
* 檢查緩存系統中是否有「鍵」對應的緩存項。
*
* 注意: 此方法應該調用 `CacheItemInterface::isHit()` 來做檢查操作,而不是
* `CacheItemInterface::get()`
*
* @param string $key
* 用來搜索緩存項的「鍵」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值,\Psr\Cache\InvalidArgumentException 異常會被拋出。
*
* @return bool
* 如果存在「鍵」對應的緩存項即返回 true,否則 false
*/
public function hasItem($key);
/**
* 清空緩沖池
*
* @return bool
* 成功返回 true,有錯誤發生返回 false
*/
public function clear();
/**
* 從緩沖池里移除某個緩存項
*
* @param string $key
* 用來搜索緩存項的「鍵」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值,\Psr\Cache\InvalidArgumentException 異常會被拋出。
*
* @return bool
* 成功返回 true,有錯誤發生返回 false
*/
public function deleteItem($key);
/**
* 從緩沖池里移除多個緩存項
*
* @param array $keys
* 由一個或者多個「鍵」組成的數組。
*
* @throws InvalidArgumentException
* 如果 $keys 里面有哪個「鍵」不是合法,\Psr\Cache\InvalidArgumentException 異常
* 會被拋出。
*
* @return bool
* 成功返回 true,有錯誤發生返回 false
*/
public function deleteItems(array $keys);
/**
* 立刻為「CacheItemInterface」對象做數據持久化。
*
* @param CacheItemInterface $item
* 將要被存儲的緩存項
*
* @return bool
* 成功返回 true,有錯誤發生返回 false
*/
public function save(CacheItemInterface $item);
/**
* 稍后為「CacheItemInterface」對象做數據持久化。
*
* @param CacheItemInterface $item
* 將要被存儲的緩存項
*
* @return bool
* 成功返回 true,有錯誤發生返回 false
*/
public function saveDeferred(CacheItemInterface $item);
/**
* 提交所有的正在隊列里等待的請求到數據持久層,配合 `saveDeferred()` 使用
*
* @return bool
* 成功返回 true,有錯誤發生返回 false
*/
public function commit();
}
```
### CacheException
此異常用于緩存系統發生的所有嚴重錯誤,包括但不限制于 *緩存系統配置*,如連接到緩存服務器出錯、錯
誤的用戶身份認證等。
所有的實現類庫拋出的異常都 **必須** 實現此接口。
```php
namespace Psr\Cache;
/**
* 被所有的實現類庫拋出的異常繼承的「異常接口」
*/
interface CacheException
{
}
```
### InvalidArgumentException
```php
namespace Psr\Cache;
/**
* 傳參錯誤拋出的異常接口
*
* 當一個錯誤或者非法的傳參發生時,**必須** 拋出一個繼承了
* Psr\Cache\InvalidArgumentException 的異常
*/
interface InvalidArgumentException extends CacheException
{
}
```