PSR-16 Simple Cache

[PSR一覧](http://qiita.com/rana_kualu/items/f41d8f657df7709bda0f)
[PSR-5](8b2d9d42b81e9f6f0fd1) / [PSR-6](b37c226363f3015951b6) / [PSR-11](a45829de16380af5acd0) / [PSR-12](28f169bb3eadc6f9e3f3) / [PSR-14](aa60378265d802051397) / [PSR-16](f25ab973b975a274b151)


[PSR-16](http://www.php-fig.org/psr/psr-16/)をゆるふわ訳していきます。

PSR-16はキャッシュ機能のインターフェイスで、2017/01/01に受理されました。
PSRのキャッシュについては既に[PSR-6](http://qiita.com/rana_kualu/items/b37c226363f3015951b6)というのがありますが、それよりもっと簡単に使えるインターフェイスとなっています。


# [Common Interface for Caching Libraries](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md)

このドキュメントは、より単純で拡張も可能なキャッシュインターフェイスを解説するよ。

"MUST"とか"MAY"とかの意味合いについては[RFC2119](http://tools.ietf.org/html/rfc2119)を参照。

最終的にできあがったライブラリはもっとたくさんの機能が実装されていてもいい(MAY)けど、少なくともここに書かれている機能だけは実装しないといけない(MUST)よ。

# 1. Specification

## 1.1 Introduction

キャッシングはアプリのパフォーマンスを向上させる一般的な方法で、フレームワークやライブラリで最もよくある機能のひとつだよ。
この機能の相互運用性とは、あるライブラリが自分の独自実装を捨てて簡単に他のキャッシュライブラリに乗り換えられるということを意味するよ。

PSR-6は既にこの問題を解決してるけど、もっと単純にキャッシュ機能だけ欲しいという要求に対しては冗長で堅苦しいよ。
PSR-16は、キャッシュ機能について単純で標準化合理化された方法を提供するよ。
PSR-6とは独立してるけど、簡単に互換もできるようにしてるよ。

## 1.2 Definitions

Library、TTL、有効期限などの定義はPSR-6と同じだからコピペだよ。

### Calling Library

呼び出し元ライブラリは、実際にキャッシュ機能を使う側のライブラリだよ。
このライブラリはキャッシュ機能を利用はするけど、キャッシュ機能の中身がどうなってるのかは全く知らないよ。

### Implementing Library

実装ライブラリは、キャッシュ機能を提供する側のライブラリで、呼び出し元ライブラリから呼び出されるよ。
実装ライブラリは`Psr\SimpleCache\CacheInterface`を実装しないといけない(MUST)よ。
また、次に説明するTTL機能を秒単位で実装しなければならない(MUST)よ。

### TTL

TTLは、そのアイテムを保存したときから失効するまでの時間だよ。
TTLは秒を表す整数、もしくはDateIntervalで表すよ。

### Expiration

有効期限はアイテムが失効する実際の時間だよ。
これはアイテムを保存した時刻+TTLで計算できるよ。

TTL300秒のアイテムを`01:30:00`に保存した場合、そのアイテムの有効期限は`01:35:00`になるよ。

実装ライブラリは、有効期限に達する前にアイテムを失効させてもいい(MAY)けど、有効期限が過ぎたら必ず失効させないといけない(MUST)よ。
呼び出し元ライブラリが有効期限もTTLも指定せずにアイテムを保存しようとした場合、実装ライブラリはTTLのデフォルト設定値を使っていい(MAY)よ。
TTLのデフォルト設定値が設定されていない場合、そのアイテムは無期限か、もしくは可能な限り長く保存しないといけない(MUST)よ。

0以下のTTLが指定された場合、そのアイテムがキャッシュに存在していたらキャッシュを削除しなければならない(MUST)よ。

### Key

キーは、キャッシュされたアイテムを一意に識別する文字列だよ。
キーとして使える文字は`A-Za-z0-9_.`をサポートしないといけない(MUST)よ。
エンコーディングはUTF-8で、長さは64文字以内だよ。
上記以外の文字種や、65文字以上のキーをサポートしてもいい(MAY)けど、最低限上記の要件だけはサポートしなければならない(MUST)よ。

実装ライブラリは、保存時に必要に応じてキー文字列をエスケープしてもいいけど、データを返すときは元々のエスケープしてない文字を返さないといけない(MUST)よ。
以下の文字`{}()/\@:`は将来の拡張用に予約されているから、ライブラリでサポートしてはいけない(MUST NOT)よ。

### Cache

キャッシュは`Psr\SimpleCache\CacheInterface`を実装したオブジェクトだよ。

### Cache Misses

キャッシュが存在しない場合はnullを返すよ。
なのでnull値を保存することはできないよ。
ここはPSR-6との相違点だよ。

## 1.3 Cache

実装ライブラリは、TTLを指定しない場合のデフォルトTTLを設定する機能を提供してもいい(MAY)よ。
デフォルト値が指定されていない場合、実装ライブラリは可能な限り長い値にTTLデフォルト値を設定しなければならない(MUST)よ。
実装ライブラリがTTLをサポートしていない場合、呼び出し元ライブラリからのTTL指定はエラーを出さずに無視しなければならない(MUST)よ。

## 1.4 Data

実装ライブラリは、保存する値として以下の全ての型をサポートしないといけない(MUST)よ。

### Strings

任意長の文字列型。

### Integers

PHPがサポートする任意長整数型、最大で符号付き64ビット。

### Floats

符号付き浮動小数点型。

### Boolean

trueとfalse。

### Null

null値。ただしキャッシュミスと区別することはできない。

### Arrays

普通にPHPの配列。

### Object

`$o == unserialize(serialize($o))`となる任意のオブジェクト。
中で実装せず、PHPのSerializable、もしくはマジックメソッド`__sleep()`と`__wakeup()`に任せてしまってもよい(MAY)。

実装ライブラリに渡された全てのデータは、そのままの値で返されなければならない(MUST)。
すなわち、5を保存したのに'5'を返したりしてはいけないよ。
実装にはPHPのserialize()/unserialize()を使ってもいい(MAY)し使わなくてもいいよ。
Compatibility with them is simply used as a baseline for acceptable object values.

何らかの理由で保存された値を返すことができない場合は、不正値を返すのではなくキャッシュミスを返さなければならない(MUST)。

## 2. Interfaces

### 2.1 CacheInterface

CacheInterfaceは、キャッシュ関連操作に関する必要最小限の機能を集めたものだよ。
個々のキャッシュの書き込み、読み出し、削除機能があるよ。

さらに、一度に複数のキャッシュを書き込み、読み出し削除する機能もあるよ。
実行したいキャッシュ操作が大量にある場合に、キャッシュサーバへのアクセスを一回で終わらせてくれるから、操作にかかる時間を短縮できるよ。

CacheInterfaceのインスタンスは、全てひとつの名前空間にあるアイテムのコレクションで、PSR-6で言うところのCacheItemPoolひとつぶんだよ。
CacheInterfaceのインスタンスを複数作成した場合、それぞれが同じデータを読み書きすることはあるかもしれない(MAY)けど、両者は論理的に独立していないといけない(MUST)よ。

```php
namespace Psr\SimpleCache;

interface CacheInterface
{
    /**
     * キャッシュから値を取得する。
     *
     * @param string $key     キャッシュの識別キー。
     * @param mixed  $default 値がなかったらデフォルト値を返す。
     *
     * @return mixed setした値。無ければ$default。
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *   $keyが正しくない場合には出さなければならない(MUST)。
     */
    public function get($key, $default = null);

    /**
     * キャッシュを書き込む。
     *
     * @param string                $key   キー。
     * @param mixed                 $value 保存する値。serializableでないといけない(MUST)。
     * @param null|int|DateInterval $ttl   有効期限。値がなければシステムデフォルト値を使う。
     *
     * @return bool 成功したらtrue。
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function set($key, $value, $ttl = null);

    /**
     * キャッシュを削除する。
     *
     * @param string $key キー。
     *
     * @return bool 削除したらtrue。なんかエラーがおきたらfalse。
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function delete($key);

    /**
     * 全キャッシュを削除する。
     *
     * @return bool 削除したらtrue。
     */
    public function clear();

    /**
     * 複数のキャッシュを一括取得する。
     *
     * @param iterable $keys    取得するキーの配列。
     * @param mixed    $default 値がなかったらデフォルト値を返す。
     *
     * @return iterable [$key=>$value]の配列。
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function getMultiple($keys, $default = null);

    /**
     * 複数のキャッシュを一括保存する。
     *
     * @param iterable              $values [キー=>保存する値]の配列。
     * @param null|int|DateInterval $ttl    有効期限。値がなければシステムデフォルト値を使う。
     *
     * @return bool 成功したらtrue。
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function setMultiple($values, $ttl = null);

    /**
     * 複数のキャッシュを一括削除する。
     *
     * @param iterable $keys A 削除するキーの配列。
     *
     * @return bool 成功したらtrue。
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function deleteMultiple($keys);

    /**
     * キャッシュが存在するかどうかを確認する。
     *
     * NOTE: これは[キャッシュウォーミング](http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/mysql_rds_innodb_buffer_pool_load_now.html)でだけ使うことを勧めるよ。
     * has()で確認した後、get()/set()する前に他のプロセスがその中身を変更することがあるよ。
     *
     * @param string $key キー
     *
     * @return bool 存在すればtrue
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function has($key);
}
```

### 2.2 CacheException

```php
namespace Psr\SimpleCache;

/**
 * キャッシュライブラリで出す例外は全てこれをimplementsする。
 */
interface CacheException
{
}
```

### 2.3 InvalidArgumentException

```php
namespace Psr\SimpleCache;

/**
 * 引数が正しくない場合に出す(MUST)。
 * 引数のキーがstringでない、使用可能文字列でないなど。
 * 
 */
interface InvalidArgumentException extends CacheException
{
}
```


# [PSR-16 Meta Document](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache-meta.md)

## 1. Summary

キャッシングは、プロジェクトのパフォーマンスを向上させる一般的な方法であり、多くのライブラリが対応しています。
このPSRは、ライブラリ独自のキャッシング実装をやめさせることで、他のフレームワークやライブラリのキャッシング実装に簡単に入れ替えられるようにするよ。

## 2. Why Bother?

PSR-6は既に上記の問題を解決してるけど、もっと単純にキャッシュ機能だけ欲しいという要求に対しては冗長で堅苦しいよ。
PSR-16はより簡単にキャッシュを扱えるようにすることを目的としているよ。

## 3. Scope

### 3.1 Goals
・キャッシュ操作のための単純なインターフェイス。
・パフォーマンス追求のため、複数キーの一括操作をサポート。
・PSR-6をPSR-16に変換するアダプタクラスを提供。
・ひとつのライブラリでPSR-6とPSR-16を両方とも提供できること。

### 3.2 Non-Goals
・PSR-16で対応しきれないものはPSR-6で解決してるからそっち使えや。

## 4. Approaches

PSR-16では、単純なケースでのみ使用することを目的としているので、必要最低限の機能しかないよ。
全てのライブラリが実装しなければならないというわけでもなく、あらゆるケースに対応できるという代物でもないよ。
PSR-16は単にPSR-6のラッパー的なものにすぎないよ。

## 5. People

### 5.1 Editor(s)

・Paul Dragoonis ([@dragoonis](https://twitter.com/dragoonis))

### 5.2 Sponsors

・Jordi Boggiano ([@seldaek](https://twitter.com/seldaek)) - Composer (Coordinator)
・Fabien Potencier ([@fabpot](https://twitter.com/fabpot)) - Symfony

### 5.3 Contributors

初版を作ってくれた人

・Evert Pot ([@evert](https://twitter.com/evert))
・Florin Pățan ([@dlsniper](https://twitter.com/dlsniper))

初期レビューしてくれた人

・Daniel Messenger ([@dannym87](https://twitter.com/dannym87))

## 6. Votes

[PSR入りの投票](https://groups.google.com/d/topic/php-fig/vyQTKHS6pJ8/discussion)
[受理の投票](https://groups.google.com/d/msg/php-fig/A8e6GvDRGIk/HQBJGEhbDQAJ)


## 7. Relevant Links

@dragoonisによる[既存ライブラリのキャッシュ実装の調査](https://docs.google.com/spreadsheets/d/1_l7lJXXDEfSXgFSjFTYU_F0wDNh1IlhvkD0yC9UjQBM/edit#gid=0)


# その他

## PSR-6とどう違うの

PSR-6は`$data[名前空間][$key]=$value`と二次元配列で保存する形だったけど、PSR-16は単純に`$data[$key]=$value`と一次元配列のような形になっています。
FWなどで大量のデータをキャッシュしたい場合などには不向きですが、気軽にちょっとした値を置いておきたい場合などに使えます。

## 使い方

```php
	// たぶんこんなかんじ
	$cache = new \Path\To\SimpleCache();
	$cache->set('key', new stdClass(), 3600);
	$foo = $cache->get('foo');
	
	$cache->setMultiple(['foo'=>'bar', 'baz'=>'qux']);
	$values = $cache->getMultiple(['hoge', 'fuga'], 'default');
	
	$cache->delete('foo');
	$cache->clear();
	
	$cache->get(1); // InvalidArgumentException
```


## 感想

なんでもかんでもオブジェクトという最近の意向に反して、なんでもかんでも連想配列という一昔前のPHPっぽさはありますが、
PSR-6と比べてCacheItemを作らなくていいので、こちらのほうが扱いやすいですね。

というか`$cache = new \Path\To\SimpleCache($namespace);`って書ければ一番楽な気がする。

フレームワーク毎に使い方をいちいち調べたりしなくていいから、共通化してくれるのは歓迎なんだけど、
でもCakePHPのキャッシングだけSymfonyに差し替えました！！とか実際やってる人って存在するんですかね。