「PSR 规范」PSR-6 缓存接口规范
介绍
缓存是提升应用性能的常用手段,为框架中最通用的功能,每个框架也都推出专属的、功能多 样的缓存库。这些差别使得开发人员不得不学习多种系统,而很多可能是他们并不需要的功能。 此外,缓存库的开发者同样面临着一个窘境,是只支持有限数量的几个框架还是创建一堆庞 大的适配器类。
一个通用的缓存系统接口可以解决掉这些问题。库和框架的开发人员能够知道缓存系统会按照他们所 预期的方式工作,缓存系统的开发人员只需要实现单一的接口,而不用去开发各种各样的适配器。
[目标](http://www.jianshu.com/p/101207899122)
本 PSR 的目标是:创建一套通用的接口规范,能够让开发人员整合到现有框架和系统,而不需要去 开发框架专属的适配器类。
[关于「能愿动词」的使用](http://www.jianshu.com/p/101207899122)
为了避免歧义,文档大量使用了「能愿动词」,对应的解释如下:
必须 (MUST):绝对,严格遵循,请照做,无条件遵守;
一定不可 (MUST NOT):禁令,严令禁止;
应该 (SHOULD) :强烈建议这样做,但是不强求;
不该 (SHOULD NOT):强烈不建议这样做,但是不强求;
可以 (MAY) 和 可选 (OPTIONAL) :选择性高一点,在这个文档内,此词语使用较少;
参见:RFC 2119
定义
[库](http://www.jianshu.com/p/101207899122)
调用类库 (Calling Library) - 调用者,使用缓存服务的类库,这个类库调用缓存服务,调用的 是此缓存接口规范的具体「实现类库」,调用者不需要知道任何「缓存服务」的具体实现。
实现类库 (Implementing Library) - 此类库是对「缓存接口规范」的具体实现,封装起来的缓存服务,供「调用类库」使用。实现类库 必须 提供 PHP 类来实现 Cache\CacheItemPoolInterface 和 Cache\CacheItemInterface 接口。 实现类库 必须 支持最小的如下描述的 TTL 功能,秒级别的精准度。
生存时间值 (TTL - Time To Live) - 定义了缓存可以存活的时间,以秒为单位的整数值。
过期时间 (Expiration) - 定义准确的过期时间点,一般为缓存存储发生的时间点加上 TTL 时 间值,也可以指定一个 DateTime 对象。
假如一个缓存项的 TTL 设置为 300 秒,保存于 1
00 ,那么缓存项的过期时间为 1
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](http://www.jianshu.com/p/101207899122)
缓存池包含缓存系统里所有缓存数据的集合。缓存池逻辑上是所有缓存项存储的仓库,所有存储进去的数据, 都能从缓存池里取出来,所有的对缓存的操作,都发生在缓存池子里。
[缓存项 Items](http://www.jianshu.com/p/101207899122)
一条缓存项在缓存池里代表了一对「键/值」对应的数据,「键」被视为每一个缓存项主键,是缓存项的 唯一标识符,必须 是不可变更的,当然,「值」可以 任意变更。
[错误处理](http://www.jianshu.com/p/101207899122)
缓存对应用性能起着至关重要的作用,但是,无论在任何情况下,缓存 一定不可 作为应用程序不 可或缺的核心功能。
缓存系统里的错误 一定不可 导致应用程序故障,所以,实现类库 一定不可 抛出任何除了 此接口规范定义的以外的异常,并且 必须 捕捉包括底层存储驱动抛出的异常,不让其冒泡至超 出缓存系统内。
实现类库 应该 对此类错误进行记录,或者以任何形式通知管理员。
调用类库发起删除缓存项的请求,或者清空整个缓冲池子的请求,「键」不存在的话 必须 不能 当成是有错误发生。后置条件是一样的,如果取数据时,「键」不存在的话 必须 不能当成是有错误发生
[接口](http://www.jianshu.com/p/101207899122)
[CacheItemInterface](http://www.jianshu.com/p/101207899122)
CacheItemInterface 定义了缓存系统里的一个缓存项。每一个缓存项 必须 有一个「键」与之相 关联,此「键」通常是通过 Cache\CacheItemPoolInterface 来设置。
Cache\CacheItemInterface 对象把缓存项的存储进行了封装,每一个 Cache\CacheItemInterface 由一个 Cache\CacheItemPoolInterface 对象生成,CacheItemPoolInterface 负责一些必须的设置,并且给对象设置具有 唯一性 的「键」。
Cache\CacheItemInterface 对象 必须 能够存储和取出任何类型的,在「数据」章节定义的 PHP 数值。
调用类库 一定不可 擅自初始化「CacheItemInterface」对象,「缓存项」只能使用「CacheItemPoolInterface」对象的 getItem() 方法来获取。调用类库 一定不可 假设 由一个实现类库创建的「缓存项」能被另一个实现类库完全兼容。
namespacePsr\Cache;
/**
* CacheItemInterface 定了缓存系统里对缓存项操作的接口
*/
interfaceCacheItemInterface
{
/**
* 返回当前缓存项的「键」
*
* 「键」由实现类库来加载,并且高层的调用者(如:CacheItemPoolInterface)
* **应该** 能使用此方法来获取到「键」的信息。
*
* @return string
* 当前缓存项的「键」
*/
publicfunctiongetKey();
/**
* 凭借此缓存项的「键」从缓存系统里面取出缓存项。
*
* 取出的数据 **必须** 跟使用 `set()` 存进去的数据是一模一样的。
*
* 如果 `isHit()` 返回 false 的话,此方法必须返回 `null`,需要注意的是 `null`
* 本来就是一个合法的缓存数据,所以你 **应该** 使用 `isHit()` 方法来辨别到底是
* "返回 null 数据" 还是 "缓存里没有此数据"。
*
* @return mixed
* 此缓存项的「键」对应的「值」,如果找不到的话,返回 `null`
*/
publicfunctionget();
/**
* 确认缓存项的检查是否命中。
*
* 注意: 调用此方法和调用 `get()` 时 **一定不可** 有先后顺序之分。
*
* @return bool
* 如果缓冲池里有命中的话,返回 `true`,反之返回 `false`
*/
publicfunctionisHit();
/**
* 为此缓存项设置「值」。
*
* 参数 $value 可以是所有能被 PHP 序列化的数据,序列化的逻辑
* 需要在实现类库里书写。
*
* @param mixed $value
* 将被存储的可序列化的数据。
*
* @return static
* 返回当前对象。
*/
publicfunctionset($value);
/**
* 设置缓存项的准确过期时间点。
*
* @param \DateTimeInterface $expiration
*
* 过期的准确时间点,过了这个时间点后,缓存项就 **必须** 被认为是过期了的。
* 如果明确的传参 `null` 的话,**可以** 使用一个默认的时间。
* 如果没有设置的话,缓存 **应该** 存储到底层实现的最大允许时间。
*
* @return static
* 返回当前对象。
*/
publicfunctionexpiresAt($expiration);
/**
* 设置缓存项的过期时间。
*
* @param int|\DateInterval $time
* 以秒为单位的过期时长,过了这段时间后,缓存项就 **必须** 被认为是过期了的。
* 如果明确的传参 `null` 的话,**可以** 使用一个默认的时间。
* 如果没有设置的话,缓存 **应该** 存储到底层实现的最大允许时间。
*
* @return static
* 返回当前对象
*/
publicfunctionexpiresAfter($time);
}
[CacheItemPoolInterface](http://www.jianshu.com/p/101207899122)
Cache\CacheItemPoolInterface 的主要目的是从调用类库接收「键」,然后返回对应的 Cache\CacheItemInterface 对象。
此接口也是作为主要的,与整个缓存集合交互的方式。所有的配置和初始化由实现类库自行实现。
namespacePsr\Cache;
/**
* CacheItemPoolInterface 生成 CacheItemInterface 对象
*/
interfaceCacheItemPoolInterface
{
/**
* 返回「键」对应的一个缓存项。
*
* 此方法 **必须** 返回一个 CacheItemInterface 对象,即使是找不到对应的缓存项
* 也 **一定不可** 返回 `null`。
*
* @param string $key
* 用来搜索缓存项的「键」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值,\Psr\Cache\InvalidArgumentException 异常会被抛出。
*
* @return CacheItemInterface
* 对应的缓存项。
*/
publicfunctiongetItem($key);
/**
* 返回一个可供遍历的缓存项集合。
*
* @param array $keys
* 由一个或者多个「键」组成的数组。
*
* @throws InvalidArgumentException
* 如果 $keys 里面有哪个「键」不是合法,\Psr\Cache\InvalidArgumentException 异常
* 会被抛出。
*
* @return array|\Traversable
* 返回一个可供遍历的缓存项集合,集合里每个元素的标识符由「键」组成,即使即使是找不到对
* 的缓存项,也要返回一个「CacheItemInterface」对象到对应的「键」中。
* 如果传参的数组为空,也需要返回一个空的可遍历的集合。
*/
publicfunctiongetItems(array $keys=array());
/**
* 检查缓存系统中是否有「键」对应的缓存项。
*
* 注意: 此方法应该调用 `CacheItemInterface::isHit()` 来做检查操作,而不是
* `CacheItemInterface::get()`
*
* @param string $key
* 用来搜索缓存项的「键」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值,\Psr\Cache\InvalidArgumentException 异常会被抛出。
*
* @return bool
* 如果存在「键」对应的缓存项即返回 true,否则 false
*/
publicfunctionhasItem($key);
/**
* 清空缓冲池
*
* @return bool
* 成功返回 true,有错误发生返回 false
*/
publicfunctionclear();
/**
* 从缓冲池里移除某个缓存项
*
* @param string $key
* 用来搜索缓存项的「键」。
*
* @throws InvalidArgumentException
* 如果 $key 不是合法的值,\Psr\Cache\InvalidArgumentException 异常会被抛出。
*
* @return bool
* 成功返回 true,有错误发生返回 false
*/
publicfunctiondeleteItem($key);
/**
* 从缓冲池里移除多个缓存项
*
* @param array $keys
* 由一个或者多个「键」组成的数组。
*
* @throws InvalidArgumentException
* 如果 $keys 里面有哪个「键」不是合法,\Psr\Cache\InvalidArgumentException 异常
* 会被抛出。
*
* @return bool
* 成功返回 true,有错误发生返回 false
*/
publicfunctiondeleteItems(array $keys);
/**
* 立刻为「CacheItemInterface」对象做数据持久化。
*
* @param CacheItemInterface $item
* 将要被存储的缓存项
*
* @return bool
* 成功返回 true,有错误发生返回 false
*/
publicfunctionsave(CacheItemInterface$item);
/**
* 稍后为「CacheItemInterface」对象做数据持久化。
*
* @param CacheItemInterface $item
* 将要被存储的缓存项
*
* @return bool
* 成功返回 true,有错误发生返回 false
*/
publicfunctionsaveDeferred(CacheItemInterface$item);
/**
* 提交所有的正在队列里等待的请求到数据持久层,配合 `saveDeferred()` 使用
*
* @return bool
* 成功返回 true,有错误发生返回 false
*/
publicfunctioncommit();
}
[CacheException](http://www.jianshu.com/p/101207899122)
此异常用于缓存系统发生的所有严重错误,包括但不限制于 缓存系统配置,如连接到缓存服务器出错、错 误的用户身份认证等。
所有的实现类库抛出的异常都 必须 实现此接口。
namespacePsr\Cache;
/**
* 被所有的实现类库抛出的异常继承的「异常接口」
*/
interfaceCacheException
{
}
[InvalidArgumentException](http://www.jianshu.com/p/101207899122)
namespacePsr\Cache;
/**
* 传参错误抛出的异常接口
*
* 当一个错误或者非法的传参发生时,**必须** 抛出一个继承了
* Psr\Cache\InvalidArgumentException 的异常
*/
interfaceInvalidArgumentExceptionextendsCacheException
{
}
如果你觉得眼前需要更好的提高你可以来千锋学PHP,国内最好的PHP机构。