PSR-6 缓存接口规范

简介

缓存是提升应用性能的常用手段,是框架中最通用的功能,每个框架可能会有自己的缓存库。这些差别使得开发人员不得不学习多种系统,缓存库的开发者同样面临一个窘境,是只支持有限数量的几个框架还是创建一堆庞 大的适配器类。

一个通用的缓存系统接口可以解决这些问题。框架和库的开发人员知道缓存系统会按照他们预期的方式工作,缓存系统的开发人员不用去开发各种各样的适配器,只需要实现单一的接口即可。

PSR-6 的目标就是提供一套通用的缓存接口规范,让开发人员整合到现有的框架和系统,而不用去开发框架专属的适配器类。

术语

调用类库(Calling Library)

这些类库将利用实现了标准接口的缓存服务,而不需要去了解缓存服务具体是如何实现的。项目开发人员只需通过 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 的字符串。

实现类库 必须 支持 Key 的规则为 A-Z, a-z, 0-9, _, 和 . 任何顺序的 UTF-8 编码,长度 小于 64 位。

实现类库 可以 支持更多的编码或者更长的长度。实现类库可自行实现 Key 的转义,{}()/@: 是系统保留的字符,严禁用来给键命名。

命中缓存(Hit)

指的是调用类库使用 key 在获取一个缓存项的时候,在缓存池里能找到对应的缓存项,并且此缓存项还未过期,而且不会因为任何原因出现错误。调用类库 应该 先验证 isHit() 有命中之后,才调用 get() 获取数据。

未命中缓存(Miss)

和 Hit 相反。一个过期的缓存项,必须 被当做 未命中 来对待。

延迟缓存(Deferred)

指的是这个缓存项可能不会立刻存储到物理缓存池里。

缓存池对象 可以 对一个指定延迟的缓存项进行延迟存储,好处是可以利用一些缓存服务器提供 的批量插入功能。

缓存池 必须 能对所有延迟缓存进行持久化,并且不会丢失。可以 在调用类库还未发起保存之前就做持久化。

当调用类库调用 commit() 方法时,所有的延迟缓存都 必须 做持久化。实现类库 可以 自行决定使用什么逻辑来触发数据持久化,如对象的 析构方法 (destructor) ,调用 save() ,倒计时保存或者触及最大数量时保存等。当获取一个延迟缓存项时,必须 返回一个未持久化的缓存项对象。

数据类型

实现类库 必须 支持所有的可序列化的 PHP 数据类型。

  • 字符串: 任何大小的 PHP 兼容字符串。
  • 整数: PHP 支持的低于 64 位的有符号整数值。
  • 浮点数:所有的有符号浮点数。
  • 布尔: true 和 false。
  • Null: null 值。
  • 数组: 各种形式的 PHP 数组。
  • 对象: 所有的无损序列化和反序列化的对象,如:$o == unserialize(serialize($o)) 。对象 可以 使用 PHP 的 Serializable 接口,__sleep() 或者 __wakeup() 魔术方法,或者使用其他类似的语言特性。

所有存进实现类库的数据,都 必须 能做到原封不动的取出。连类型也 必须 是完全一致,如果 存进缓存的是字符串 5,取出来的却是整数值 5 的话,可以算作严重的错误。

实现类库 可以 使用 PHP 的「serialize()/unserialize() 方法」作为底层实现,不过不强迫这样做。对于他们的兼容性,以能支持所有数据类型作为基准线。

如果实在无法完整取出存入的数据,实现类库 必须 返回缓存丢失标示,而不是损坏了的数据。

主要概念

缓存池(Pool)

缓存池指的是缓存系统里所有缓存数据的集合。

缓存池逻辑上是所有缓存项存储的仓库,所有存储进去的数据, 都能从缓存池里取出来,所有的对缓存的操作,都发生在缓存池里。

缓存项(Items)

一条缓存项在缓存池里代表了一个键值对(key => value)。

键是缓存项的唯一标识符,必须是不可变更的;值可以任意变更。

错误处理(Error handling)

在任何情况下,缓存 严禁 作为应用程序不可或缺的核心功能。

缓存系统里的错误 一定不可 导致应用程序故障,所以,实现类库 一定不可 抛出任何此接口规范定义之外的异常,并且 必须 捕捉包括底层存储驱动抛出的异常,不让其冒泡超出缓存系统。

实现类库 应该 对此类错误进行记录,或者以任何形式通知管理员。

调用类库发起删除缓存项的请求,或者清空整个缓冲池子的请求, key 不存在的话禁止当做错误。如果取数据时,key 不存在的话,也禁止当做错误。

接口(Interfaces)

CacheItemInterface

CacheItemInterface 定义了缓存系统里的缓存项。

每一个缓存项 必须 有一个 key 与之关联。 key 一般是通过 Cache\CacheItemPoolInterface 来设置。

Cache\CacheItemInterface 把缓存项的存储进行了封装,每一个 Cache\CacheItemInterface 由一个 Cache\CacheItemPoolInterface 对象生成,CacheItemPoolInterface 负责一些必须的设置,并且给对象设置具有唯一性的 key 。

Cache\CacheItemInterface 必须 能够存储和取出任何类型的数据。

调用类库 一定不可 擅自初始化 CacheItemInterface ,缓存项只能使用 CacheItemPoolInterface 的 getItem() 方法来获取。

调用类库 一定不可 假设 由一个实现类库创建的缓存项能被另一个实现类库完全兼容。

namespace Psr\Cache;

/**
 * 缓存系统里对缓存项操作的接口
 */
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
     *   以秒为单位的过期时长,过了这段时间后,缓存项就 **必须** 被认为是过期的。
     * @return static
     *   返回当前对象
     */
    public function expiresAfter($time);

}

CacheItemPoolInterface

它的主要作用是从调用类库接收 key,然后返回对应的 Cache\CacheItemInterface 对象。

此接口也是作为主要的与整个缓存集合交互的方式。所有的配置和初始化由实现类库自行实现。

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

此异常用于缓存系统发生的所有严重错误,包括但不限于缓存系统配置,如连接到缓存服务器出错、用户身份认证出错等。

所有的实现类库抛出的异常都 必须 实现此接口。

namespace Psr\Cache;

/**
 * 被所有的实现类库抛出的异常继承的「异常接口」
 */
interface CacheException
{
}

InvalidArgumentException

namespace Psr\Cache;

/**
 * 传参错误抛出的异常接口
 *
 * 当传参错误时,**必须** 抛出一个继承了
 * Psr\Cache\InvalidArgumentException 的异常
 */
interface InvalidArgumentException extends CacheException
{
}

更多详情可参考官方:https://www.php-fig.org/psr/psr-6/

猜你喜欢

转载自blog.csdn.net/lamp_yang_3533/article/details/80213762