Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Yet another cache proposal #63

Closed
wants to merge 5 commits into from
@dlsniper

Problem to be solved

Unify the caching interface across libraries and framework in a simple yet
extensible way.

Current implementations

Doctrine 2 has a stable implementation going on.

Zend Framework 2 has something as well.

Stash provides a good standalone library.

Symfony2 has none and I haven't checked the other frameworks / libraries.

Inspiration and credits

This is based on the work of @Evert from here:
https://github.com/evert/fig-standards/blob/master/proposed/objectcache.md

It is also taken feedback from here Symfony2 comunity here:
symfony/symfony#3211

It has been started here as a proposal for a way to implement cache:
symfony/symfony#5902

Also @c-datculescu has been very helpful in identifying possible use cases and
problems as well as bouncing back ideas or helping out with thoughts on the
various issues.

This was later on merged with the solution from @evert while talking on IRC
about our proposals and commonality of approaches.

Proposed solution

You can view it rendered here:
https://github.com/dlsniper/fig-standards/blob/cache-proposal/proposed/psr-cache.md

This solution addresses the basic needs for a driver library since most of the
time caching is done using key/value systems that do not support advanced
features like tags, namespaces, transactions or locking.

Having this fact into consideration the libraries should be kept as simple as
possible and don't reinvent the wheel while help for advanced use cases or
problems should be pointed out.

Lack of certain features, see tagging, namespace or locking

Tagging and namespaces

In regards to the lack of tags/namespace support from the proposed solution,
I give you the following case, that originated in a discussion with a friend
of mine.

- What if the driver doesn't support tagging?

- Simple, it's the proxy job to know what the driver supports and in this case
emulate support for the missing feature.

- Ok, but how would you handle say 5 tags / item with a length of say 10
characters and about 15.000 items in stored?

- Well we'd need implement something that gets the maximum permitted storage
size for the given driver, then create some special, unique entries where
the information is stored.

- Great but now you have another problem. Concurrency/traffic and time spent
in the proxy emulating this feature. In a highly concurrent environment, what
whould happen to the tag pool and changes you want to perform on it?

- Without locking it would introduce a problem as one client could update same
tag as another but with different results. One should then check in the tag
pool if the desired result is there then redo the operation if it failed.

So you can see where this is leading.

Same goes for namespaces.

Locking

Locking is something that could be done easily but I currently don't see many
cases where it would be useful. Locking would be useful in highly concurrent
environment but in that case it would mean that you would rely on users, for
example, to generate the cache, which would mean that you have one user unhappy
because he's performing a potentially heavy operation. Which is not good by
design.

For situations where a locking would be required, think about the implications
about resources implied, from waiting users to running PHP processes that wait
for the initial process to finish processing the cache update or to the
detection of when that process finished/crashed.

Reason for skipping the metioned features from the current proposal

While the common approach with caching is:


$cache = new ApcDriver();

$cacheItem = $cache->get($key, $exists);

if (null === $cacheItem) {
    // make heavy operation
    $cacheValue = $resultFromOperation;
    $cache->set($key, $cacheItem, 300);
} else {
    $cacheValue = $cacheItem->getValue();
}

A better approach would be to have multiple caching layers and just go from
one to another if there's a problem retrieving the data from one layer until
a cronjob makes it available again.


$apcCache = new ApcDriver();

$exists = false;
$cacheItem = $apcCache->get($key);

if (null === $cacheItem) {
    $memcacheCache = new MemcachedDriver();
    $cacheItem = $memcacheCache->get($key);

    if (null === $cacheItem) {
        $dbCache = new DbCache();
        $cacheItem = $dbCache->get($key);

        if (null === $cacheItem) {
            // make heavy operation
            $cacheValue = $resultFromOperation;
            $apcCache->set($key, $cacheValue, 300);
        } else {
            $cacheValue = $cacheItem->getValue();
        }
    } else {
        $cacheValue = $cacheItem->getValue();
    }
} else {
    $cacheValue = $cacheItem->getValue();
}

Note: a nicer approach can be done with the approach @schmittjoh suggests here

Also, in most cases, APC driver should be a good one. But if you need a
distributed solution, Memcached, Redis or even MongoDB (thought it would
not be a recommendation to use it for caching necessarily) make out great
distributed systems which can be clustered and have redundancy and so on.
Solutions like having a cluster and in front of it a HAProxy server to balance
out requests one servers are down are available in such situations.

For lower level websites that just need a simple solution, the concurrency
should not be an issue in terms of having less caching levels as well as
even saving the object from the user request.

The ideal solution would have a cronjob that's able to populate the cache
before the items in it expire.

It is true that we can definitely provide emulation for all of the features,
having the library providing them would mean an additional level of abstraction
that will stand between the user and desired functionality and this is what
enterprise users don't agree with, for good reasons.

It is my opinion that we also need to teach the users how to think better
applications rather that just provide them tools that can magically do
more that they are suppose to do.

This way, people will be aware of the limitations and instead of having
a library/framework doing the workaround they would either create a better
logic in the application or help improve the tools/applications that are
suppose to be handling the issue.

If the above train of thoughts wasn't enough to satisfy the needs of the
community both current and future, then I've also added some 'advanced'
interfaces that are designed to tackle the missing functionality from the
initial proposal and you can find it here:
https://github.com/dlsniper/fig-standards/blob/extended-cache-proposal/proposed/psr-extended-cache.md

I've also created a dedicated topic on the Google Group for this
https://groups.google.com/forum/?fromgroups=#!topic/php-fig/VRUEzicwjb8
where you can leave your feedback.

Thank you!

Best regards.

@schmittjoh

I'd like to suggest to consider returning an Option object instead:

$item = $apcDriver->get('foo')
           ->orElse($memcache->get('foo'))
           ->orElse($dbCache->get('foo'))
           ->getOrCall(function() {
                // heavy
           });

The overhead of that is in the nano second range (i.e. should not be relevant here), but the API is a lot more elegant (see https://github.com/schmittjoh/php-option).

@dlsniper

@schmittjoh Thanks for your feedback. I think we could have the CacheProxy return null instead of a CacheItem in order for chaining to work as you described.

Another approach that could benefit from your solution would be to make the CacheProxy not have the driver set in the constructor but instead have a method called addCacheDriver($driver, $priority) which can allow for more that one cache driver to be used by the proxy. At this point, we could have the default get and set methods only use the highest priority driver, for consistency, and either add a new parameter to them, $fromFirstAvailable which is default false or create new methods for these operations. This way, the repeating code will be moved into one place and the 'userland` code will be cleaner.

@c-datculescu

@nfx I am pretty sure CAS was taken into consideration, but i really think in this case CAS cannot help. Even more, CAS can produce problems. Basicly the entire purpose of CAS is to check at the moment of writing if the data you read is still untouched, which in high concurrency enviroments will not happen. Let's not forget that a caching mechanism is not intended to be used as some sort of transactional mechanism. In the current case, CAS can actually cause alot more retries if not careful.

Also, please remember that if CAS is implemented in memcached (and only in the memcached extension, not in the memcache one), it does not mean that APC for example provides at least a similar mechanism. Nor do other fast k/v storages.

@dlsniper

@nfx I agree with all that @c-datculescu said above.
Also you could have a memcached driver that uses CAS for set operation just fine along with the other custom drivers should you need such a operation. This is a implementation detail from the driver vendor.
The example I have is how currently the fallback is done, either at logical step or implementation step.

proposed/cache.md
((42 lines not shown))
+operations then it will be the CacheProxy job to implement the missing
+functionality so that the end-user has the same consistent behavior.
+
+If the cache driver provides only some support for multiple (bulk) operations
+then the driver will need to implement the rest of the missing operations
+instead of the CacheProxy.
+
+The same goes for the drivers that have support only for multiple operations
+and no or partial support for single operations.
+
+### CacheProxy
+
+There are two types of cache proxies that can be used.
+The simple one, which provides the basic functionality like get/set/remove.
+
+Cache proxies are reponsible for sending the right data to the drivers, be it
@stof
stof added a note

typo here. Missing s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
proposed/cache.md
((312 lines not shown))
+
+use PSR\Cache\Item\CacheItemInterface;
+use PSR\Cache\Driver\CacheDriverInterface;
+
+/**
+ * This is our cache proxy
+ */
+interface CacheProxyInterface
+{
+
+ /**
+ * Create the proxy that's going to be used by the end-user by adding the
+ *
+ * @param CacheDriverInterface $cacheDriver
+ */
+ public function __construct(CacheDriverInterface $cacheDriver);
@stof
stof added a note

I don't like the fact that you add the constructor in the interface. The constructor is part of the way you instantiate the class, which is tied to the implementation anyway so I don't see the reason to forbid implementors to have other required dependencies.

As far as I know, an interface can not be instantiated and they are used just to specify how to pass data. So what is the purpose of the constructor in there ?

If you need some sort of initialization, maybe an abstract class can serve this purpose better than interface.

@vimishor Because this constructor method has a type hint it is a way of enforcing that a class that implement this interface must use a CacheDriverInterface as the parameter for its construction. But stof does has a point that the construction of an object doesn't need to be enforced at the interface level.

@sumpygump Ah, now I got the idea behind that constructor. Thanks for clarification.

I agree that having the constructor locked is not such a good idea, I'll make the changes for it, thanks for the suggestion @stof.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dlsniper

I've updated the proposal with some things inspired from the LoggerInterface proposal as well as some rewording / enhancements in order for this to be a bit more clear.

proposed/psr-cache.md
((369 lines not shown))
+use Psr\Cache\DriverInterface;
+
+/**
+ * This is our cache proxy
+ */
+interface CacheProxyInterface
+{
+
+ /**
+ * Set the cache driver
+ *
+ * @param DriverInterface $cacheDriver
+ *
+ * @return CacheProxyInterface
+ */
+ public function setCacheDriver(DriverInterface $cacheDriver);
@andrerom
andrerom added a note

Should this be up to implementation so we can support proxy's with several drivers for several layers of cache?
In such a case you would not have to consider returning option object as suggested by @schmittjoh, cause the proxy implementation handles it for you.

@dlsniper
dlsniper added a note

Hi @andrerom, please add your comment on the mailing list so that others can see it and I'll reply to it asap.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dlsniper

@bschussek thanks for grammar check! :+1:

proposed/psr-cache.md
((176 lines not shown))
+
+### 2.3 TtlAwareCacheInterface
+
+```php
+
+<?php
+
+namespace Psr\Cache;
+
+use Psr\Cache\CacheItemInterface;
+use Psr\Cache\CacheInterface;
+
+/**
+ * Interface for a cache driver that supports TTLs
+ */
+interface CacheInterface extends CacheInterface

There is a typo in name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dlsniper

@dragoonis will come with the final proposal soonish.

@dlsniper dlsniper closed this
@dlsniper dlsniper reopened this
@dragoonis
Owner

@dlsniper Some personal issues came up, so the proposal from me will have to wait until Friday. Thanks.

@dlsniper

@dragoonis thanks, I've just opened it to be easier to spot for the time being.
Take your time and solve your issues, there's no rush.
Thanks for work on this.

@dragoonis
Owner
@dragoonis
Owner

Florin has just asked me to close this off. Doing so.

@dragoonis dragoonis closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 7, 2013
  1. @Seldaek @dlsniper

    Add LoggerInterface Proposal

    Seldaek authored dlsniper committed
  2. @Seldaek @dlsniper

    Add naming convention bylaw

    Seldaek authored dlsniper committed
  3. @dragoonis @dlsniper

    Moving PSR3 from proposed to accepted

    dragoonis authored dlsniper committed
  4. @ChristianRiesen @dlsniper
  5. @dlsniper
This page is out of date. Refresh to see the latest.
View
304 accepted/PSR-3-logger-interface.md
@@ -0,0 +1,304 @@
+Logger Interface
+================
+
+This document describes a common interface for logging libraries.
+
+The main goal is to allow libraries to receive a `Psr\Log\LoggerInterface`
+object and write logs to it in a simple and universal way. Frameworks
+and CMSs that have custom needs MAY extend the interface for their own
+purpose, but SHOULD remain compatible with this document. This ensures
+that the third-party libraries an application uses can write to the
+centralized application logs.
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in [RFC 2119][].
+
+The word `implementor` in this document is to be interpreted as someone
+implementing the `LoggerInterface` in a log-related library or framework.
+Users of loggers are refered to as `user`.
+
+[RFC 2119]: http://tools.ietf.org/html/rfc2119
+
+1. Specification
+-----------------
+
+### 1.1 Basics
+
+- The `LoggerInterface` exposes eight methods to write logs to the eight
+ [RFC 5424][] levels (debug, info, notice, warning, error, critical, alert,
+ emergency).
+
+- A ninth method, `log`, accepts a log level as first argument. Calling this
+ method with one of the log level constants MUST have the same result as
+ calling the level-specific method. Calling this method with a level not
+ defined by this specification MUST throw a `Psr\Log\InvalidArgumentException`
+ if the implementation does not know about the level. Users SHOULD NOT use a
+ custom level without knowing for sure the current implementation supports it.
+
+[RFC 5424]: http://tools.ietf.org/html/rfc5424
+
+### 1.2 Message
+
+- Every method accepts a string as the message, or an object with a
+ `__toString()` method. Implementors MAY have special handling for the passed
+ objects. If that is not the case, implementors MUST cast it to a string.
+
+- The message MAY contain placeholders which implementors MAY replace with
+ values from the context array.
+
+ Placeholder names MUST correspond to keys in the context array.
+
+ Placeholder names MUST be delimited with a single opening brace `{` and
+ a single closing brace `}`. There MUST NOT be any whitespace between the
+ delimiters and the placeholder name.
+
+ Placeholder names SHOULD be composed only of the characters `A-Z`, `a-z`,
+ `0-9`, underscore `_`, and period `.`. The use of other characters is
+ reserved for future modifications of the placeholders specification.
+
+ Implementors MAY use placeholders to implement various escaping strategies
+ and translate logs for display. Users SHOULD NOT pre-escape placeholder
+ values since they can not know in which context the data will be displayed.
+
+ The following is an example implementation of placeholder interpolation
+ provided for reference purposes only:
+
+ ```php
+ /**
+ * Interpolates context values into the message placeholders.
+ */
+ function interpolate($message, array $context = array())
+ {
+ // build a replacement array with braces around the context keys
+ $replace = array();
+ foreach ($context as $key => $val) {
+ $replace['{' . $key . '}'] = $val;
+ }
+
+ // interpolate replacement values into the the message and return
+ return strtr($message, $replace);
+ }
+
+ // a message with brace-delimited placeholder names
+ $message = "User {username} created";
+
+ // a context array of placeholder names => replacement values
+ $context = array('username' => 'bolivar');
+
+ // echoes "Username bolivar created"
+ echo interpolate($message, $context);
+ ```
+
+### 1.3 Context
+
+- Every method accepts an array as context data. This is meant to hold any
+ extraneous information that does not fit well in a string. The array can
+ contain anything. Implementors MUST ensure they treat context data with
+ as much lenience as possible. A given value in the context MUST NOT throw
+ an exception nor raise any php error, warning or notice.
+
+- If an `Exception` object is passed in the context data, it MUST be in the
+ `'exception'` key. Logging exceptions is a common pattern and this allows
+ implementors to extract a stack trace from the exception when the log
+ backend supports it. Implementors MUST still verify that the `'exception'`
+ key is actually an `Exception` before using it as such, as it MAY contain
+ anything.
+
+### 1.4 Helper classes and interfaces
+
+- The `Psr\Log\AbstractLogger` class lets you implement the `LoggerInterface`
+ very easily by extending it and implementing the generic `log` method.
+ The other eight methods are forwarding the message and context to it.
+
+- Similarly, using the `Psr\Log\LoggerTrait` only requires you to
+ implement the generic `log` method. Note that since traits can not implement
+ interfaces, in this case you still have to `implement LoggerInterface`.
+
+- The `Psr\Log\NullLogger` is provided together with the interface. It MAY be
+ used by users of the interface to provide a fall-back "black hole"
+ implementation if no logger is given to them. However conditional logging
+ may be a better approach if context data creation is expensive.
+
+- The `Psr\Log\LoggerAwareInterface` only contains a
+ `setLogger(LoggerInterface $logger)` method and can be used by frameworks to
+ auto-wire arbitrary instances with a logger.
+
+- The `Psr\Log\LoggerAwareTrait` trait can be used to implement the equivalent
+ interface easily in any class. It gives you access to `$this->logger`.
+
+- The `Psr\Log\LogLevel` class holds constants for the eight log levels.
+
+2. Package
+----------
+
+The interfaces and classes described as well as relevant exception classes
+and a test suite to verify your implementation are provided as part of the
+[psr/log](https://packagist.org/packages/psr/log) package.
+
+3. `Psr\Log\LoggerInterface`
+----------------------------
+
+```php
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes a logger instance
+ *
+ * The message MUST be a string or object implementing __toString().
+ *
+ * The message MAY contain placeholders in the form: {foo} where foo
+ * will be replaced by the context data in key "foo".
+ *
+ * The context array can contain arbitrary data, the only assumption that
+ * can be made by implementors is that if an Exception instance is given
+ * to produce a stack trace, it MUST be in a key named "exception".
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
+ * for the full interface specification.
+ */
+interface LoggerInterface
+{
+ /**
+ * System is unusable.
+ *
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function emergency($message, array $context = array());
+
+ /**
+ * Action must be taken immediately.
+ *
+ * Example: Entire website down, database unavailable, etc. This should
+ * trigger the SMS alerts and wake you up.
+ *
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function alert($message, array $context = array());
+
+ /**
+ * Critical conditions.
+ *
+ * Example: Application component unavailable, unexpected exception.
+ *
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function critical($message, array $context = array());
+
+ /**
+ * Runtime errors that do not require immediate action but should typically
+ * be logged and monitored.
+ *
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function error($message, array $context = array());
+
+ /**
+ * Exceptional occurrences that are not errors.
+ *
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
+ * that are not necessarily wrong.
+ *
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function warning($message, array $context = array());
+
+ /**
+ * Normal but significant events.
+ *
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function notice($message, array $context = array());
+
+ /**
+ * Interesting events.
+ *
+ * Example: User logs in, SQL logs.
+ *
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function info($message, array $context = array());
+
+ /**
+ * Detailed debug information.
+ *
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function debug($message, array $context = array());
+
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ * @return null
+ */
+ public function log($level, $message, array $context = array());
+}
+```
+
+4. `Psr\Log\LoggerAwareInterface`
+---------------------------------
+
+```php
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes a logger-aware instance
+ */
+interface LoggerAwareInterface
+{
+ /**
+ * Sets a logger instance on the object
+ *
+ * @param LoggerInterface $logger
+ * @return null
+ */
+ public function setLogger(LoggerInterface $logger);
+}
+```
+
+5. `Psr\Log\LogLevel`
+---------------------
+
+```php
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes log levels
+ */
+class LogLevel
+{
+ const EMERGENCY = 'emergency';
+ const ALERT = 'alert';
+ const CRITICAL = 'critical';
+ const ERROR = 'error';
+ const WARNING = 'warning';
+ const NOTICE = 'notice';
+ const INFO = 'info';
+ const DEBUG = 'debug';
+}
+```
View
18 bylaws/002-psr-naming-conventions.md
@@ -0,0 +1,18 @@
+Naming conventions for code released by PHP-FIG
+-----------------------------------------------
+
+1. Interfaces MUST be suffixed by `Interface`: e.g. `Psr\Foo\BarInterface`.
+2. Abstract classes MUST be prefixed by `Abstract`: e.g. `Psr\Foo\AbstractBar`.
+3. Traits MUST be suffixed by `Trait`: e.g. `Psr\Foo\BarTrait`.
+4. PSR-0, 1 and 2 MUST be followed.
+5. The vendor namespace MUST be `Psr`.
+6. There MUST be a package/second-level namespace in relation with the PSR that
+ covers the code.
+7. Composer package MUST be named `psr/<package>` e.g. `psr/log`. If they
+ require an implementation as a virtual package it MUST be named
+ `psr/<package>-implementation` and be required with a specific version like
+ `1.0.0`. Implementors of that PSR can then provide
+ `"psr/<package>-implementation": "1.0.0"` in their package to satisfy that
+ requirement. Specification changes via further PSRs should only lead to a new
+ tag of the `psr/<package>` package, and an equal version bump of the
+ implementation being required.
View
200 proposed/psr-cache.md
@@ -0,0 +1,200 @@
+Common Interface for Caching libraries
+================
+
+
+This document describes a simple yet extensible interface for a cache item and
+a cache driver.
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in [RFC 2119][].
+
+The final implementations MAY be able to decorate the objects with more
+functionality that the one proposed but they MUST implement the indicated
+interfaces/functionality first.
+
+[RFC 2119]: http://tools.ietf.org/html/rfc2119
+
+1. Specification
+-----------------
+
+### 1.1 Quick description
+
+In order to save the user information into a cache system, a simple cache item
+is implemented which facilitates the storing of any and all possible
+information that a user might want to store.
+
+For this, the `Cache` implementation MUST be able to handle any values that
+a user could store, including and not limited to PHP objects, null values,
+boolean values and so on.
+
+All the `TTL` references in this document are defined as the number of seconds
+until the item using it will be rendered invalid/expired/deleted from the
+caching system.
+
+### 1.2 CacheItem
+
+By `CacheItem` we refer to a object that implements the
+`Psr\Cache\CacheItemInterface` interface.
+
+By using the cache item implementations will guarantee consistency across
+various systems and ensure that the user will always retrieve the expected data
+without performing any additional operations.
+
+Refer to the appendix ``` 4.1 Usage of CacheItem ``` for more details on this.
+
+### 1.3 Cache
+
+By `Cache` we refer to a object that implements the `Psr\Cache\CacheInterface`
+interface.
+
+If the user does not provide a TTL value then the `Cache` MUST set a default
+value that is either configured by the user or, if not available, the maximum
+value allowed by cache system. If the cache system does not support a TTL
+option then the user specified or the default TTL values will be ignored.
+
+It will be the implementation job to define what values are considered valid
+or invalid for key names but the user MUST be aware of the accepted values
+by the underlying solution both for TTL values as well as for key names.
+
+`Cache` MUST always return a `CacheItem` when the item is found in the cache
+and `null` when the item is not found.
+
+For ```setMultiple``` the array MUST be associative where the pair key/value
+will represent the key and value of the item in to be stored in the caching
+engine.
+
+2. Interfaces
+----------
+
+### 2.1 CacheItemInterface
+
+```php
+
+<?php
+
+namespace Psr\Cache;
+
+/**
+ * Interface for caching object
+ */
+interface CacheItemInterface
+{
+
+ /**
+ * Get the value of the object
+ *
+ * @return mixed
+ */
+ public function getValue();
+
+}
+
+```
+
+### 2.2 CacheInterface
+
+```php
+
+<?php
+
+namespace Psr\Cache;
+
+use Psr\Cache\CacheItemInterface;
+
+/**
+ * Interface for the caching driver
+ */
+interface CacheInterface
+{
+
+ /**
+ * Get cache entry
+ *
+ * @param string $key
+ *
+ * @return CacheItemInterface|null
+ */
+ public function get($key);
+
+ /**
+ * Set a single cache entry
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int|null $ttl
+ *
+ * @return Boolean Result of the operation
+ */
+ public function set($key, $value, $ttl = null);
+
+ /**
+ * Remove a single cache entry
+ *
+ * @param string $key
+ *
+ * @return Boolean Result of the operation
+ */
+ public function remove($key);
+
+ /**
+ * Get multiple entries the cache
+ *
+ * @param string[] $keys
+ *
+ * @return CacheItemInterface[]
+ */
+ public function getMultiple($keys);
+
+ /**
+ * Set multiple entries in the cache
+ *
+ * @param array $items
+ * @param null|int $ttl
+ */
+ public function setMultiple(array $items, $ttl = null);
+
+ /**
+ * Remove multiple entries from the cache
+ *
+ * @param string[] $keys
+ */
+ public function removeMultiple($keys);
+
+ /**
+ * This allows to clear all the cache contents
+ *
+ * return Boolean
+ */
+ public function clear();
+
+}
+
+```
+
+3. Package
+----------
+
+The interfaces described as well as a test suite to verify your implementation
+are provided as part of the [php-fig/cache](https://packagist.org/packages/php-fig/psr-cache) package.
+
+4. Appendix
+----------
+
+### 4.1 Usage of CacheItem
+
+Since various cache systems / or drivers are not fully capable of storing all
+the nativ data types present in PHP as well as have a consistent return value
+in case the stored value wasn't found that would not conflict otherwise with
+the value stored by the user, the CacheItem approach was used.
+
+This helps implementations store any data type in the cache system then allows
+each implementation do deal with the mentioned shortcomings.
+
+The ```CacheItem``` SHOULD NOT be used be used ouside of the scope described
+by this document but this doesn't mean implementations can't use it for more
+specific implementations such as a full OO implementation with other methods
+attached.
+
+Since the role of ``` CacheItem ``` is to facilitate the returning of any
+stored value, it will be used only by the getter functions.
Something went wrong with that request. Please try again.