翻译进度
8
分块数量
6
参与人数

PSR-6 缓存接口规范 - 说明文档

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。

PSR-6 说明文档

1. 概述

使用缓存是一种常用的提高性能的方法,并适用于任何项目,这使得缓存库成为许多框架和库最常见的特性之一。最后导致了许多库都有自己的缓存库,并且具有不同级别的功能。这些差异导致开发人员不得不学习多个缓存系统,而他们所需要的功能可能在有的系统里并没有提供。此外,缓存库本身的开发人员只能选择要么支持少量框架,要么创建大量的适配器类。

li-luo-ao 翻译于 6个月前

2. 为什么有必要?

通用的缓存接口会解决这些问题。库和框架开发者可以期望缓存系统能正常工作,与此同时,缓存系统的开发者只用实现一部分接口而不是做一大堆适配工作。

而且,这里的实现也是为了方便未来扩展,它提供了许多本质上不同但是却又兼容 API 的实现,而且也为后面 PSRs 规范或者特定的实现提供了清晰的路径规划。

正方:

  • 一个标准的缓存接口能提供独立的库来让我们轻松缓存中间数据;他们可以简单的依赖和使用这些标准接口而不用关心具体的实现细节。
  • 由多个项目共享的常见开发的缓存系统,即使他们扩展了这个接口,也比单独开发的实现要健壮

反方:

  • 任何接口标准化会被认为扼杀了未来创新,被认为不应该这样实现。但是我们相信缓存是一个足够商业化的问题场景,缓存接口在这里提供的扩展能力降低了任何潜在的停滞风险。
yuesir 翻译于 3个月前

3. 范围

3.1 目标

  • 一种通用的底层和中间级缓存需求接口。
  • 一种清晰的机制,用于扩展规范以支持高级功能,包括将来的 PSRs 或单个实现。 此机制必须允许多个独立扩展而不会发生冲突。

3.2 非目标

  • 与所有现有缓存的实现体系结构兼容。
  • 像命名空间或标记这样由少数用户使用的高级缓存特性。
imajinyun 翻译于 3个月前

4. 方法

4.1 选择的方法

该规范采用『存储模型』或『数据映射』模型进行缓存,而不是传统可『可过期键-值』模型。主要原因是灵活性。简单的键/值模型更加难以扩展。

这里的模型要求用CacheItem对象和Pool对象,CacheItem对象表示缓存条目,Pool对象是缓存数据给定缓存。从池中检索项目,交互并返回到项目。有时候有些冗长,但是它提供了一个良好、稳健、灵活的缓存方法,尤其是在缓存比简单的保存在字符串更复杂的情况下。

大多数方法名称是根据成员项目和其他流行的非成员系统调查中的通用实践和方法名来选择的。

优点:

  • 灵活并且可扩展
  • 允许在不违反接口的情况下实现大量的变化
  • 不会将对象构造函数的隐式暴露为伪接口

缺点:

  • 比简单的方法更冗长

示例:

下面是一些常用的使用模式。这些是非规范的,但是可以说明一些设计决策的应用。

/**
 * 获取可用控件列表。
 *
 * 在这种情况下,我们假设小部件列表很少改动 
 * 列表一直缓存到显式清除为止。
 */
function get_widget_list()
{
    $pool = get_cache_pool('widgets');
    $item = $pool->getItem('widget_list');
    if (!$item->isHit()) {
        $value = compute_expensive_widget_list();
        $item->set($value);
        $pool->save($item);
    }
    return $item->get();
}
/**
 * 可用控件缓存列表。.
 *
 * 在这种情况下,我们假设已经计算了一个小部件列表,
 * 缓存它,无论缓存的是什么。
 */
function save_widget_list($list)
{
    $pool = get_cache_pool('widgets');
    $item = $pool->getItem('widget_list');
    $item->set($list);
    $pool->save($item);
}
/**
 * 清除缓存小部件列表。
 *
 * 在这种情况下,我们只想从缓存中删除小部件。
 * 我们不在意他是否已被设置;POST的条件是『不再设置』 。
 */
function clear_widget_list()
{
    $pool = get_cache_pool('widgets');
    $pool->deleteItems(['widget_list']);
}
/**
 * 清除所有的小部件。
 *
 * 在这种情况下,我们只想清空池中所有的小部件。 
 * 应用中其他的池可能不会受到影响。
 */
function clear_widget_cache()
{
    $pool = get_cache_pool('widgets');
    $pool->clear();
}
cici180324 翻译于 3个月前
/**
 * 加载小部件.
 *
 * 我们想要获取一个小部件的列表,其中一些是缓存 一些
 * 不是.这里假设从缓存中加载比在非缓存加载机制中更快 
 *  比在非缓存加载机制中更快。
 *
 * 在这种情况下, 假设窗口小部件需要经常更改因此我们仅
 * 设置缓存的时间为一小时 (3600 秒). 我们也将新缓存的
 * 对象
 * 返回到池中。
 *
 * 还需要注意在实际实现中还需要对小部件窗口进行多次
 * 加载操作,但是这与本次演示无关。
 */
function load_widgets(array $ids)
{
    $pool = get_cache_pool('widgets');
    $keys = array_map(function($id) { return 'widget.' . $id; }, $ids);
    $items = $pool->getItems($keys);

    $widgets = array();
    foreach ($items as $key => $item) {
        if ($item->isHit()) {
            $value = $item->get();
        } else {
            $value = expensive_widget_load($id);
            $item->set($value);
            $item->expiresAfter(3600);
            $pool->saveDeferred($item, true);
        }
        $widget[$value->id()] = $value;
    }
    $pool->commit(); // 如果没有延期的项目这里无操作。

    return $widgets;
}
/**
 * 这个示例反应了此规范未包含的
 * 功能,但是显示为如何通过扩展来实现
 * 添加此类功能的示例。
 */

interface TaggablePoolInterface extends Psr\Cache\CachePoolInterface
{
    /**
     * 只清除池中指定标记的项目。
     */
    clearByTag($tag);
}

interface TaggableItemInterface extends Psr\Cache\CacheItemInterface
{
    public function setTags(array $tags);
}

/**
 * 标记缓存小部件。
 */
function set_widget(TaggablePoolInterface $pool, Widget $widget)
{
    $key = 'widget.' . $widget->id();
    $item = $pool->getItem($key);

    $item->setTags($widget->tags());
    $item->set($widget);
    $pool->save($item);
}
cici180324 翻译于 3个月前

4.2 Alternative: "Weak item" approach

A variety of earlier drafts took a simpler "key value with expiration" approach, also known as a "weak item" approach. In this model, the "Cache Item" object was really just a dumb array-with-methods object. Users would instantiate it directly, then pass it to a cache pool. While more familiar, that approach effectively prevented any meaningful extension of the Cache Item. It effectively made the Cache Item's constructor part of the implicit interface, and thus severely curtailed extensibility or the ability to have the cache item be where the intelligence lives.

In a poll conducted in June 2013, most participants showed a clear preference for the more robust if less conventional "Strong item" / repository approach, which was adopted as the way forward.

Pros:

  • More traditional approach.

Cons:

  • Less extensible or flexible.

4.3 Alternative: "Naked value" approach

Some of the earliest discussions of the Cache spec suggested skipping the Cache Item concept all together and just reading/writing raw values to be cached. While simpler, it was pointed out that made it impossible to tell the difference between a cache miss and whatever raw value was selected to represent a cache miss. That is, if a cache lookup returned NULL it's impossible to tell if there was no cached value or if NULL was the value that had been cached. (NULL is a legitimate value to cache in many cases.)

Most more robust caching implementations we reviewed -- in particular the Stash caching library and the home-grown cache system used by Drupal -- use some sort of structured object on get at least to avoid confusion between a miss and a sentinel value. Based on that prior experience FIG decided that a naked value on get was impossible.

4.4 Alternative: ArrayAccess Pool

There was a suggestion to make a Pool implement ArrayAccess, which would allow for cache get/set operations to use array syntax. That was rejected due to limited interest, limited flexibility of that approach (trivial get and set with default control information is all that's possible), and because it's trivial for a particular implementation to include as an add-on should it desire to do so.

5. People

5.1 Editor

  • Larry Garfield

5.2 Sponsors

  • Paul Dragoonis, PPI Framework (Coordinator)
  • Robert Hafner, Stash

6. Votes

Acceptance vote on the mailing list

7. Relevant Links

Note: Order descending chronologically.

8. Errata

8.1 Handling of incorrect DateTime values in expiresAt()

The CacheItemInterface::expiresAt() method's $expiration parameter is untyped in the interface, but in the docblock is specified as \DateTimeInterface. The intent is that either a \DateTime or \DateTimeImmutable object is allowed. However, \DateTimeInterface and \DateTimeImmutable were added in PHP 5.5, and the authors chose not to impose a hard syntactic requirement for PHP 5.5 on the specification.

Despite that, implementers MUST accept only \DateTimeInterface or compatible types (such as \DateTime and \DateTimeImmutable) as if the method was explicitly typed. (Note that the variance rules for a typed parameter may vary between language versions.)

Simulating a failed type check unfortunately varies between PHP versions and thus is not recommended. Instead, implementors SHOULD throw an instance of \Psr\Cache\InvalidArgumentException.\
The following sample code is recommended in order to enforce the type check on the expiresAt() method:


class ExpiresAtInvalidParameterException implements Psr\Cache\InvalidArgumentException {}

// ...

if (! (
        null === $expiration
        || $expiration instanceof \DateTime
        || $expiration instanceof \DateTimeInterface
)) {
    throw new ExpiresAtInvalidParameterException(sprintf(
        'Argument 1 passed to %s::expiresAt() must be an instance of DateTime or DateTimeImmutable; %s given',
        get_class($this),
        is_object($expiration) ? get_class($expiration) : gettype($expiration)
    ));
}

本文章首发在 Laravel China 社区
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

参与译者:6
讨论数量: 0
发起讨论


暂无话题~