diff --git a/src/Adapter/AbstractAdapter.php b/src/Adapter/AbstractAdapter.php new file mode 100644 index 0000000..186b260 --- /dev/null +++ b/src/Adapter/AbstractAdapter.php @@ -0,0 +1,138 @@ + + */ +namespace TinyCache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; + +abstract class AbstractAdapter implements CacheItemPoolInterface +{ + /** + * Returns a Cache Item representing the specified key. + * + * This method must always return a CacheItemInterface object, even in case of + * a cache miss. It MUST NOT return null. + * + * @param string $key + * The key for which to return the corresponding Cache Item. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return CacheItemInterface + * The corresponding Cache Item. + */ + abstract public function getItem($key); + + /** + * Returns a traversable set of cache items. + * + * @param string[] $keys + * An indexed array of keys of items to retrieve. + * + * @throws InvalidArgumentException + * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return array|\Traversable + * A traversable collection of Cache Items keyed by the cache keys of + * each item. A Cache item will be returned for each key, even if that + * key is not found. However, if no keys are specified then an empty + * traversable MUST be returned instead. + */ + abstract public function getItems(array $keys = []); + + /** + * Confirms if the cache contains specified cache item. + * + * Note: This method MAY avoid retrieving the cached value for performance reasons. + * This could result in a race condition with CacheItemInterface::get(). To avoid + * such situation use CacheItemInterface::isHit() instead. + * + * @param string $key + * The key for which to check existence. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if item exists in the cache, false otherwise. + */ + abstract public function hasItem($key); + + /** + * Deletes all items in the pool. + * + * @return bool + * True if the pool was successfully cleared. False if there was an error. + */ + abstract public function clear(); + + /** + * Removes the item from the pool. + * + * @param string $key + * The key to delete. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if the item was successfully removed. False if there was an error. + */ + abstract public function deleteItem($key); + + /** + * Removes multiple items from the pool. + * + * @param string[] $keys + * An array of keys that should be removed from the pool. + + * @throws InvalidArgumentException + * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if the items were successfully removed. False if there was an error. + */ + abstract public function deleteItems(array $keys); + + /** + * Persists a cache item immediately. + * + * @param CacheItemInterface $item + * The cache item to save. + * + * @return bool + * True if the item was successfully persisted. False if there was an error. + */ + abstract public function save(CacheItemInterface $item); + + /** + * Sets a cache item to be persisted later. + * + * @param CacheItemInterface $item + * The cache item to save. + * + * @return bool + * False if the item could not be queued or if a commit was attempted and failed. True otherwise. + */ + abstract public function saveDeferred(CacheItemInterface $item); + + /** + * Persists any deferred cache items. + * + * @return bool + * True if all not-yet-saved items were successfully saved or there were none. False otherwise. + */ + abstract public function commit(); +} \ No newline at end of file diff --git a/src/Adapter/FilesystemAdapter.php b/src/Adapter/FilesystemAdapter.php new file mode 100644 index 0000000..13df35a --- /dev/null +++ b/src/Adapter/FilesystemAdapter.php @@ -0,0 +1,236 @@ + + */ +namespace TinyCache\Adapter; + +use Psr\Cache\CacheItemInterface; +use TinyCache\Collection; +use TinyCache\Item; +use TinyCache\Hash; + +class FilesystemAdapter extends AbstractAdapter +{ + protected $directory; + + protected $collection; + + public function __construct($directory = null) + { + $this->directory = $directory; + + if (!$directory) { + $this->directory = sys_get_temp_dir().'/tiny-cache'; + if (!file_exists($this->directory)) { + @mkdir($this->directory, 0777, true); + } + } + $this->collection = new Collection; + } + + /** + * Returns a Cache Item representing the specified key. + * + * This method must always return a CacheItemInterface object, even in case of + * a cache miss. It MUST NOT return null. + * + * @param string $key + * The key for which to return the corresponding Cache Item. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return CacheItemInterface + * The corresponding Cache Item. + */ + public function getItem($key) + { + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) { + if ($file->isDir()) { + continue; + } + if (strpos($file->getFilename(),$key) === 0) { + $parts = explode('-', $file->getFilename()); + if ($parts[0] === $key) { + return new Item($key,file_get_contents($file->getRealPath())); + } + } + } + + return new Item(); + } + + /** + * Returns a traversable set of cache items. + * + * @param string[] $keys + * An indexed array of keys of items to retrieve. + * + * @throws InvalidArgumentException + * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return array|\Traversable + * A traversable collection of Cache Items keyed by the cache keys of + * each item. A Cache item will be returned for each key, even if that + * key is not found. However, if no keys are specified then an empty + * traversable MUST be returned instead. + */ + public function getItems(array $keys = []) + { + $collection = new Collection; + foreach ($keys as $key) { + $item = $this->getItem($key); + if ($item->isHit()) { + $collection->set($item->getKey(), $item); + } + } + + return $collection; + } + + /** + * Confirms if the cache contains specified cache item. + * + * Note: This method MAY avoid retrieving the cached value for performance reasons. + * This could result in a race condition with CacheItemInterface::get(). To avoid + * such situation use CacheItemInterface::isHit() instead. + * + * @param string $key + * The key for which to check existence. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if item exists in the cache, false otherwise. + */ + public function hasItem($key) + { + + } + + /** + * Deletes all items in the pool. + * + * @return bool + * True if the pool was successfully cleared. False if there was an error. + */ + public function clear() + { + $ok = true; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) { + $ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok; + } + return $ok; + } + + /** + * Removes the item from the pool. + * + * @param string $key + * The key to delete. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if the item was successfully removed. False if there was an error. + */ + public function deleteItem($key) + { + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) { + if ($file->isDir()) { + continue; + } + if (strpos($file->getFilename(),$key) === 0) { + $parts = explode('-', $file->getFilename()); + if ($parts[0] === $key) { + return (!file_exists($file->getRealPath()) || @unlink($file->getRealPath()) || !file_exists($file->getRealPath())); + } + } + } + } + + /** + * Removes multiple items from the pool. + * + * @param string[] $keys + * An array of keys that should be removed from the pool. + + * @throws InvalidArgumentException + * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if the items were successfully removed. False if there was an error. + */ + public function deleteItems(array $keys) + { + $ok = true; + foreach ($keys as $key) { + $ok = $this->deleteItem($key) && $ok; + } + return $ok; + } + + /** + * Persists a cache item immediately. + * + * @param CacheItemInterface $item + * The cache item to save. + * + * @return bool + * True if the item was successfully persisted. False if there was an error. + */ + public function save(CacheItemInterface $item) + { + $name = $item->getKey().'-'.Hash::string($item->get()); + + $path = $this->directory . DIRECTORY_SEPARATOR . $name; + + if (false === @file_put_contents($path, $item->get())) { + return false; + } + + return true; + } + + /** + * Sets a cache item to be persisted later. + * + * @param CacheItemInterface $item + * The cache item to save. + * + * @return bool + * False if the item could not be queued or if a commit was attempted and failed. True otherwise. + */ + public function saveDeferred(CacheItemInterface $item) + { + $this->collection->set($item->getKey(), $item); + + return $this; + } + + /** + * Persists any deferred cache items. + * + * @return bool + * True if all not-yet-saved items were successfully saved or there were none. False otherwise. + */ + public function commit() + { + $ok = true; + foreach ($this->collection->all() as $key => $item) { + $ok = $this->save($item) && $ok; + } + return $ok; + } +} \ No newline at end of file diff --git a/src/Cache.php b/src/Cache.php index be16e90..095e4cf 100644 --- a/src/Cache.php +++ b/src/Cache.php @@ -8,7 +8,165 @@ */ namespace TinyCache; +use Psr\Cache\CacheItemInterface; +use TinyCache\Adapter\AbstractAdapter; + class Cache { + protected $adapter; + + public function __construct(AbstractAdapter $adapter) + { + $this->adapter = $adapter; + } + + /** + * Returns a Cache Item representing the specified key. + * + * This method must always return a CacheItemInterface object, even in case of + * a cache miss. It MUST NOT return null. + * + * @param string $key + * The key for which to return the corresponding Cache Item. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return CacheItemInterface + * The corresponding Cache Item. + */ + public function getItem($key) + { + return $this->adapter->getItem($key); + } + + /** + * Returns a traversable set of cache items. + * + * @param string[] $keys + * An indexed array of keys of items to retrieve. + * + * @throws InvalidArgumentException + * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return array|\Traversable + * A traversable collection of Cache Items keyed by the cache keys of + * each item. A Cache item will be returned for each key, even if that + * key is not found. However, if no keys are specified then an empty + * traversable MUST be returned instead. + */ + public function getItems(array $keys = []) + { + return $this->adapter->getItems($keys); + } + + /** + * Confirms if the cache contains specified cache item. + * + * Note: This method MAY avoid retrieving the cached value for performance reasons. + * This could result in a race condition with CacheItemInterface::get(). To avoid + * such situation use CacheItemInterface::isHit() instead. + * + * @param string $key + * The key for which to check existence. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if item exists in the cache, false otherwise. + */ + public function hasItem($key) + { + return $this->adapter->hasItem($key); + } + + /** + * Deletes all items in the pool. + * + * @return bool + * True if the pool was successfully cleared. False if there was an error. + */ + public function clear() + { + return $this->adapter->clear(); + } + + /** + * Removes the item from the pool. + * + * @param string $key + * The key to delete. + * + * @throws InvalidArgumentException + * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if the item was successfully removed. False if there was an error. + */ + public function deleteItem($key) + { + return $this->adapter->deleteItem($key); + } + + /** + * Removes multiple items from the pool. + * + * @param string[] $keys + * An array of keys that should be removed from the pool. + + * @throws InvalidArgumentException + * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException + * MUST be thrown. + * + * @return bool + * True if the items were successfully removed. False if there was an error. + */ + public function deleteItems(array $keys) + { + return $this->adapter->deleteItems($keys); + } + + /** + * Persists a cache item immediately. + * + * @param CacheItemInterface $item + * The cache item to save. + * + * @return bool + * True if the item was successfully persisted. False if there was an error. + */ + public function save(CacheItemInterface $item) + { + return $this->adapter->save($item); + } + + /** + * Sets a cache item to be persisted later. + * + * @param CacheItemInterface $item + * The cache item to save. + * + * @return bool + * False if the item could not be queued or if a commit was attempted and failed. True otherwise. + */ + public function saveDeferred(CacheItemInterface $item) + { + return $this->adapter->saveDeferred($item); + } + /** + * Persists any deferred cache items. + * + * @return bool + * True if all not-yet-saved items were successfully saved or there were none. False otherwise. + */ + public function commit() + { + return $this->adapter->commit(); + } } \ No newline at end of file diff --git a/src/Collection.php b/src/Collection.php new file mode 100644 index 0000000..c9ae3ee --- /dev/null +++ b/src/Collection.php @@ -0,0 +1,181 @@ + + */ +namespace TinyCache; + +use ArrayIterator; + +class Collection implements \ArrayAccess +{ + /** + * The source data + * + * @var array + */ + protected $data = []; + /** + * Create new collection + * + * @param array $items Pre-populate collection with this key-value array + */ + public function __construct(array $items = []) + { + $this->replace($items); + } + + /** + * Set collection item + * + * @param string $key The data key + * @param mixed $value The data value + */ + public function set($key, $value) + { + $this->data[$key] = $value; + } + + /** + * Get collection item for key + * + * @param string $key The data key + * @param mixed $default The default value to return if data key does not exist + * + * @return mixed The key's value, or the default value + */ + public function get($key, $default = null) + { + return $this->has($key) ? $this->data[$key] : $default; + } + + /** + * Add item to collection, replacing existing items with the same data key + * + * @param array $items Key-value array of data to append to this collection + */ + public function replace(array $items) + { + foreach ($items as $key => $value) { + $this->set($key, $value); + } + } + + /** + * Get all items in collection + * + * @return array The collection's source data + */ + public function all() + { + return $this->data; + } + + /** + * Get collection keys + * + * @return array The collection's source data keys + */ + public function keys() + { + return array_keys($this->data); + } + + /** + * Does this collection have a given key? + * + * @param string $key The data key + * + * @return bool + */ + public function has($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Remove item from collection + * + * @param string $key The data key + */ + public function remove($key) + { + unset($this->data[$key]); + } + + /** + * Remove all items from collection + */ + public function clear() + { + $this->data = []; + } + + /** + * Does this collection have a given key? + * + * @param string $key The data key + * + * @return bool + */ + public function offsetExists($key) + { + return $this->has($key); + } + + /** + * Get collection item for key + * + * @param string $key The data key + * + * @return mixed The key's value, or the default value + */ + public function offsetGet($key) + { + return $this->get($key); + } + + /** + * Set collection item + * + * @param string $key The data key + * @param mixed $value The data value + */ + public function offsetSet($key, $value) + { + $this->set($key, $value); + } + + /** + * Remove item from collection + * + * @param string $key The data key + */ + public function offsetUnset($key) + { + $this->remove($key); + } + + /** + * Get number of items in collection + * + * @return int + */ + public function count() + { + return count($this->data); + } + + /** + * Get collection iterator + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->data); + } +} \ No newline at end of file diff --git a/src/Hash.php b/src/Hash.php new file mode 100644 index 0000000..e33ea51 --- /dev/null +++ b/src/Hash.php @@ -0,0 +1,32 @@ + + */ +namespace TinyCache; + +class Hash +{ + public static function fileSha1($file) + { + return sha1_file($file); + } + + public static function html($html = '') + { + return self::string($html); + } + + public static function css($css = '') + { + return self::string($css); + } + + public static function string($string = '') + { + return sha1($string); + } +} \ No newline at end of file diff --git a/src/Item.php b/src/Item.php new file mode 100644 index 0000000..a726472 --- /dev/null +++ b/src/Item.php @@ -0,0 +1,140 @@ + + */ +namespace TinyCache; + +use Psr\Cache\CacheItemInterface; + +class Item implements CacheItemInterface +{ + protected $key; + + protected $value; + + public function __construct($key = '',$value = null) + { + $this + ->setKey($key) + ->set($value); + } + + /** + * Returns the key for the current cache item. + * + * The key is loaded by the Implementing Library, but should be available to + * the higher level callers when needed. + * + * @return string + * The key string for this cache item. + */ + public function getKey() + { + return $this->key; + } + + /** + * Retrieves the value of the item from the cache associated with this object's key. + * + * The value returned must be identical to the value originally stored by set(). + * + * If isHit() returns false, this method MUST return null. Note that null + * is a legitimate cached value, so the isHit() method SHOULD be used to + * differentiate between "null value was found" and "no value was found." + * + * @return mixed + * The value corresponding to this cache item's key, or null if not found. + */ + public function get() + { + return $this->value; + } + + /** + * Confirms if the cache item lookup resulted in a cache hit. + * + * Note: This method MUST NOT have a race condition between calling isHit() + * and calling get(). + * + * @return bool + * True if the request resulted in a cache hit. False otherwise. + */ + public function isHit() + { + return $this->value === null && $this->key === '' ? false : true; + } + + /** + * Sets the value represented by this cache item. + * + * The $value argument may be any item that can be serialized by PHP, + * although the method of serialization is left up to the Implementing + * Library. + * + * @param mixed $value + * The serializable value to be stored. + * + * @return static + * The invoked object. + */ + public function set($value) + { + $this->value = $value; + + return $this; + } + + /** + * Sets the key represented by this cache item. + * + * @param string $key + * + * @return static + * The invoked object. + */ + public function setKey($key) + { + $this->key = $key; + + return $this; + } + + /** + * Sets the expiration time for this cache item. + * + * @param \DateTimeInterface|null $expiration + * The point in time after which the item MUST be considered expired. + * If null is passed explicitly, a default value MAY be used. If none is set, + * the value should be stored permanently or for as long as the + * implementation allows. + * + * @return static + * The called object. + */ + public function expiresAt($expiration) + { + + } + + /** + * Sets the expiration time for this cache item. + * + * @param int|\DateInterval|null $time + * The period of time from the present after which the item MUST be considered + * expired. An integer parameter is understood to be the time in seconds until + * expiration. If null is passed explicitly, a default value MAY be used. + * If none is set, the value should be stored permanently or for as long as the + * implementation allows. + * + * @return static + * The called object. + */ + public function expiresAfter($time) + { + + } +} \ No newline at end of file