PHP Recommendations: PSR-6 vs. PSR-16

The PHP Framework Interop Group (PHP-FIG) features two accepted recommendations related to caching: PSR-6 (Caching Interface) and PSR-16 (Simple Cache). We are going to take a look at both PSR’s to find out what distinguishes them from one another.

PSR-6

PSR-6 provides a common interface for caching systems. It was accepted by PHP-FIG on December 8, 2015, after having been worked on for no less than four years. (Caching is a fairly complicated subject and some reservations still remain, as evidenced by the outcome of the vote.)

Recommendation Goal

The goal of PSR-6 “is to allow developers to create cache-aware libraries that can be integrated into existing frameworks and systems without the need for custom development.” (Source)

Basically, this means that developers of caching libraries don’t have to provide a large number of adapter classes for their library to work with different frameworks.

The PSR’s scope is clearly defined, and does not include some advanced caching features, such as namespacing or tagging. However, some PSR-6 implementations do provide these features.

Terminology

PSR-6 is based on the concepts of cache pools and cache pool items. A pool is an abstraction of a cache backend such as Redis or APCu. It is a collection of pool items, which are key-value pairs cached in that backend. Items are retrieved from and stored to the pool by their unique, immutable key.

Implementation

The recommendation provides four interfaces in the Psr\Cache namespace.

PSR-6 Class Diagram

Figure 1 – PSR-6 Class Diagram

Despite the naming not being very intuitive, it is easily guessed that CacheItemInterface and CacheItemPoolInterface represent cache pool items and cache pools, respectively.

Please note that retrieving an item by its key will not yield the actual value stored in the cache, but rather an object implementing CacheItemInterface. To test whether a cache hit has occurred, call the method isHit() on the cache item, as demonstrated in the following code snippet. To retrieve the actual value, call the get() method on the cache item object.

$pool = new Psr6Implementation();
$item = $pool->getItem("foo");

if ($item->isHit() === false) {
  $value = "bar"; // IRL: Fetch from database, compute value, ...
  $item->set($value);
  $pool->save($item);
} else {
  $value = $item->get();
}

return $value;

Conclusion

PSR-6 provides a rich interface to work with various cache backends. In addition to retrieving and storing individual items, it provides methods to work with collections of items, as well as the possibility to save items in a deferred manner and committing them in bulk.

As for simplicity, the interface may not be super straightforward, as the pool won’t immediately return the actual cached value. Instead, a CacheItemInterface instance will be returned to operate on.

PSR-16

PSR-16 was accepted by PHP-FIG well after PSR-6, on January 2, 2017. The final vote shows a clear approval with 24 positive votes vs. 3 negative ones.

Recommendation Goals

PSR-16 strives to provide a simpler interface for caching operations than PSR-6 does, with a more limited scope.

Terminology

PSR-16 doesn’t have a concept of cache pools and items – it simply abstracts caches.

Implementation

The recommendation provides three interfaces in the Psr\SimpleCache namespace. Two of them are the same exceptions known from PSR-6. The third one is the actual CacheInterface.

PSR-16 Class Diagram

Figure 2 – PSR-16 Class Diagram

PSR-16 looks pretty much like what you’d expect from a typical key-value store interface, having get() and set() methods accepting key and value parameters along with optional default value and TTL parameters.

Revisiting the above example using PSR-16 results in code similar to this snippet:

$cache = new Psr16Implementation();

if ($cache->has("foo") === false) {
 $value = "bar"; // IRL: Fetch from database, compute value, ...
 $cache->set("foo", $value); // Omitting try-catch clause for brevity
} else {
 $value = $cache->get("foo");
}

return $value;

If you don’t want to set the value in case of a cache miss, the code can be reduced to

$cache = new Psr16Implementation();

return $cache->get("foo", "bar");

Conclusion

Using PSR-16 may result in briefer code, as we’re only dealing with a cache object – no cache pools and items. That said, we don’t get as much functionality as PSR-6 would provide, namely the ability to defer cache writes.

Conclusion

PSR-6 provides a versatile caching interface able of covering edge-cases. It also features some advanced caching techniques. For simpler use-cases, the newer PSR-16 may be a bit easier to understand by developers using your cache system.

Luckily, it’s not strictly either-or! Psr\Cache\CacheItemPoolInterface and Psr\SimpleCache\CacheInterface declare only one method of the same name, clear(). As both declarations have the same signature, any cache system may implement both interfaces as long as a PHP dependency of version 5.3.9 or greater is required. PHP Cache is a cache system whose adapters implement both the PSR-6 and PSR-16 interfaces. Also, Symfony 3.3 provides adapters to transform PSR-6 cache pools to PSR-16 caches and vice versa.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.