中秋节快乐

This commit is contained in:
Alone
2024-09-14 17:00:49 +08:00
parent 610dee88cd
commit dff1b038b1
4286 changed files with 807503 additions and 6 deletions
+559
View File
@@ -0,0 +1,559 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common;
use Cache\Adapter\Common\Exception\CacheException;
use Cache\Adapter\Common\Exception\CachePoolException;
use Cache\Adapter\Common\Exception\InvalidArgumentException;
use Psr\Cache\CacheItemInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Psr\SimpleCache\CacheInterface;
/**
* @author Aaron Scherer <aequasi@gmail.com>
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
abstract class AbstractCachePool implements PhpCachePool, LoggerAwareInterface, CacheInterface
{
const SEPARATOR_TAG = '!';
/**
* @type LoggerInterface
*/
private $logger;
/**
* @type PhpCacheItem[] deferred
*/
protected $deferred = [];
/**
* @param PhpCacheItem $item
* @param int|null $ttl seconds from now
*
* @return bool true if saved
*/
abstract protected function storeItemInCache(PhpCacheItem $item, $ttl);
/**
* Fetch an object from the cache implementation.
*
* If it is a cache miss, it MUST return [false, null, [], null]
*
* @param string $key
*
* @return array with [isHit, value, tags[], expirationTimestamp]
*/
abstract protected function fetchObjectFromCache($key);
/**
* Clear all objects from cache.
*
* @return bool false if error
*/
abstract protected function clearAllObjectsFromCache();
/**
* Remove one object from cache.
*
* @param string $key
*
* @return bool
*/
abstract protected function clearOneObjectFromCache($key);
/**
* Get an array with all the values in the list named $name.
*
* @param string $name
*
* @return array
*/
abstract protected function getList($name);
/**
* Remove the list.
*
* @param string $name
*
* @return bool
*/
abstract protected function removeList($name);
/**
* Add a item key on a list named $name.
*
* @param string $name
* @param string $key
*/
abstract protected function appendListItem($name, $key);
/**
* Remove an item from the list.
*
* @param string $name
* @param string $key
*/
abstract protected function removeListItem($name, $key);
/**
* Make sure to commit before we destruct.
*/
public function __destruct()
{
$this->commit();
}
/**
* {@inheritdoc}
*/
public function getItem($key)
{
$this->validateKey($key);
if (isset($this->deferred[$key])) {
/** @type CacheItem $item */
$item = clone $this->deferred[$key];
$item->moveTagsToPrevious();
return $item;
}
$func = function () use ($key) {
try {
return $this->fetchObjectFromCache($key);
} catch (\Exception $e) {
$this->handleException($e, __FUNCTION__);
}
};
return new CacheItem($key, $func);
}
/**
* {@inheritdoc}
*/
public function getItems(array $keys = [])
{
$items = [];
foreach ($keys as $key) {
$items[$key] = $this->getItem($key);
}
return $items;
}
/**
* {@inheritdoc}
*/
public function hasItem($key)
{
try {
return $this->getItem($key)->isHit();
} catch (\Exception $e) {
$this->handleException($e, __FUNCTION__);
}
}
/**
* {@inheritdoc}
*/
public function clear()
{
// Clear the deferred items
$this->deferred = [];
try {
return $this->clearAllObjectsFromCache();
} catch (\Exception $e) {
$this->handleException($e, __FUNCTION__);
}
}
/**
* {@inheritdoc}
*/
public function deleteItem($key)
{
try {
return $this->deleteItems([$key]);
} catch (\Exception $e) {
$this->handleException($e, __FUNCTION__);
}
}
/**
* {@inheritdoc}
*/
public function deleteItems(array $keys)
{
$deleted = true;
foreach ($keys as $key) {
$this->validateKey($key);
// Delete form deferred
unset($this->deferred[$key]);
// We have to commit here to be able to remove deferred hierarchy items
$this->commit();
$this->preRemoveItem($key);
if (!$this->clearOneObjectFromCache($key)) {
$deleted = false;
}
}
return $deleted;
}
/**
* {@inheritdoc}
*/
public function save(CacheItemInterface $item)
{
if (!$item instanceof PhpCacheItem) {
$e = new InvalidArgumentException('Cache items are not transferable between pools. Item MUST implement PhpCacheItem.');
$this->handleException($e, __FUNCTION__);
}
$this->removeTagEntries($item);
$this->saveTags($item);
$timeToLive = null;
if (null !== $timestamp = $item->getExpirationTimestamp()) {
$timeToLive = $timestamp - time();
if ($timeToLive < 0) {
return $this->deleteItem($item->getKey());
}
}
try {
return $this->storeItemInCache($item, $timeToLive);
} catch (\Exception $e) {
$this->handleException($e, __FUNCTION__);
}
}
/**
* {@inheritdoc}
*/
public function saveDeferred(CacheItemInterface $item)
{
$this->deferred[$item->getKey()] = $item;
return true;
}
/**
* {@inheritdoc}
*/
public function commit()
{
$saved = true;
foreach ($this->deferred as $item) {
if (!$this->save($item)) {
$saved = false;
}
}
$this->deferred = [];
return $saved;
}
/**
* @param string $key
*
* @throws InvalidArgumentException
*/
protected function validateKey($key)
{
if (!is_string($key)) {
$e = new InvalidArgumentException(sprintf(
'Cache key must be string, "%s" given',
gettype($key)
));
$this->handleException($e, __FUNCTION__);
}
if (!isset($key[0])) {
$e = new InvalidArgumentException('Cache key cannot be an empty string');
$this->handleException($e, __FUNCTION__);
}
if (preg_match('|[\{\}\(\)/\\\@\:]|', $key)) {
$e = new InvalidArgumentException(sprintf(
'Invalid key: "%s". The key contains one or more characters reserved for future extension: {}()/\@:',
$key
));
$this->handleException($e, __FUNCTION__);
}
}
/**
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* Logs with an arbitrary level if the logger exists.
*
* @param mixed $level
* @param string $message
* @param array $context
*/
protected function log($level, $message, array $context = [])
{
if ($this->logger !== null) {
$this->logger->log($level, $message, $context);
}
}
/**
* Log exception and rethrow it.
*
* @param \Exception $e
* @param string $function
*
* @throws CachePoolException
*/
private function handleException(\Exception $e, $function)
{
$level = 'alert';
if ($e instanceof InvalidArgumentException) {
$level = 'warning';
}
$this->log($level, $e->getMessage(), ['exception' => $e]);
if (!$e instanceof CacheException) {
$e = new CachePoolException(sprintf('Exception thrown when executing "%s". ', $function), 0, $e);
}
throw $e;
}
/**
* @param array $tags
*
* @return bool
*/
public function invalidateTags(array $tags)
{
$itemIds = [];
foreach ($tags as $tag) {
$itemIds = array_merge($itemIds, $this->getList($this->getTagKey($tag)));
}
// Remove all items with the tag
$success = $this->deleteItems($itemIds);
if ($success) {
// Remove the tag list
foreach ($tags as $tag) {
$this->removeList($this->getTagKey($tag));
$l = $this->getList($this->getTagKey($tag));
}
}
return $success;
}
public function invalidateTag($tag)
{
return $this->invalidateTags([$tag]);
}
/**
* @param PhpCacheItem $item
*/
protected function saveTags(PhpCacheItem $item)
{
$tags = $item->getTags();
foreach ($tags as $tag) {
$this->appendListItem($this->getTagKey($tag), $item->getKey());
}
}
/**
* Removes the key form all tag lists. When an item with tags is removed
* we MUST remove the tags. If we fail to remove the tags a new item with
* the same key will automatically get the previous tags.
*
* @param string $key
*
* @return $this
*/
protected function preRemoveItem($key)
{
$item = $this->getItem($key);
$this->removeTagEntries($item);
return $this;
}
/**
* @param PhpCacheItem $item
*/
private function removeTagEntries(PhpCacheItem $item)
{
$tags = $item->getPreviousTags();
foreach ($tags as $tag) {
$this->removeListItem($this->getTagKey($tag), $item->getKey());
}
}
/**
* @param string $tag
*
* @return string
*/
protected function getTagKey($tag)
{
return 'tag'.self::SEPARATOR_TAG.$tag;
}
/**
* {@inheritdoc}
*/
public function get($key, $default = null)
{
$item = $this->getItem($key);
if (!$item->isHit()) {
return $default;
}
return $item->get();
}
/**
* {@inheritdoc}
*/
public function set($key, $value, $ttl = null)
{
$item = $this->getItem($key);
$item->set($value);
$item->expiresAfter($ttl);
return $this->save($item);
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return $this->deleteItem($key);
}
/**
* {@inheritdoc}
*/
public function getMultiple($keys, $default = null)
{
if (!is_array($keys)) {
if (!$keys instanceof \Traversable) {
throw new InvalidArgumentException('$keys is neither an array nor Traversable');
}
// Since we need to throw an exception if *any* key is invalid, it doesn't
// make sense to wrap iterators or something like that.
$keys = iterator_to_array($keys, false);
}
$items = $this->getItems($keys);
return $this->generateValues($default, $items);
}
/**
* @param $default
* @param $items
*
* @return \Generator
*/
private function generateValues($default, $items)
{
foreach ($items as $key => $item) {
/** @type $item CacheItemInterface */
if (!$item->isHit()) {
yield $key => $default;
} else {
yield $key => $item->get();
}
}
}
/**
* {@inheritdoc}
*/
public function setMultiple($values, $ttl = null)
{
if (!is_array($values)) {
if (!$values instanceof \Traversable) {
throw new InvalidArgumentException('$values is neither an array nor Traversable');
}
}
$keys = [];
$arrayValues = [];
foreach ($values as $key => $value) {
if (is_int($key)) {
$key = (string) $key;
}
$this->validateKey($key);
$keys[] = $key;
$arrayValues[$key] = $value;
}
$items = $this->getItems($keys);
$itemSuccess = true;
foreach ($items as $key => $item) {
$item->set($arrayValues[$key]);
try {
$item->expiresAfter($ttl);
} catch (InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
$itemSuccess = $itemSuccess && $this->saveDeferred($item);
}
return $itemSuccess && $this->commit();
}
/**
* {@inheritdoc}
*/
public function deleteMultiple($keys)
{
if (!is_array($keys)) {
if (!$keys instanceof \Traversable) {
throw new InvalidArgumentException('$keys is neither an array nor Traversable');
}
// Since we need to throw an exception if *any* key is invalid, it doesn't
// make sense to wrap iterators or something like that.
$keys = iterator_to_array($keys, false);
}
return $this->deleteItems($keys);
}
/**
* {@inheritdoc}
*/
public function has($key)
{
return $this->hasItem($key);
}
}
+269
View File
@@ -0,0 +1,269 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common;
use Cache\Adapter\Common\Exception\InvalidArgumentException;
use Cache\TagInterop\TaggableCacheItemInterface;
/**
* @author Aaron Scherer <aequasi@gmail.com>
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class CacheItem implements PhpCacheItem
{
/**
* @type array
*/
private $prevTags = [];
/**
* @type array
*/
private $tags = [];
/**
* @type \Closure
*/
private $callable;
/**
* @type string
*/
private $key;
/**
* @type mixed
*/
private $value;
/**
* The expiration timestamp is the source of truth. This is the UTC timestamp
* when the cache item expire. A value of zero means it never expires. A nullvalue
* means that no expiration is set.
*
* @type int|null
*/
private $expirationTimestamp = null;
/**
* @type bool
*/
private $hasValue = false;
/**
* @param string $key
* @param \Closure|bool $callable or boolean hasValue
*/
public function __construct($key, $callable = null, $value = null)
{
$this->key = $key;
if ($callable === true) {
$this->hasValue = true;
$this->value = $value;
} elseif ($callable !== false) {
// This must be a callable or null
$this->callable = $callable;
}
}
/**
* {@inheritdoc}
*/
public function getKey()
{
return $this->key;
}
/**
* {@inheritdoc}
*/
public function set($value)
{
$this->value = $value;
$this->hasValue = true;
$this->callable = null;
return $this;
}
/**
* {@inheritdoc}
*/
public function get()
{
if (!$this->isHit()) {
return;
}
return $this->value;
}
/**
* {@inheritdoc}
*/
public function isHit()
{
$this->initialize();
if (!$this->hasValue) {
return false;
}
if ($this->expirationTimestamp !== null) {
return $this->expirationTimestamp > time();
}
return true;
}
/**
* {@inheritdoc}
*/
public function getExpirationTimestamp()
{
return $this->expirationTimestamp;
}
/**
* {@inheritdoc}
*/
public function expiresAt($expiration)
{
if ($expiration instanceof \DateTimeInterface) {
$this->expirationTimestamp = $expiration->getTimestamp();
} elseif (is_int($expiration) || null === $expiration) {
$this->expirationTimestamp = $expiration;
} else {
throw new InvalidArgumentException('Cache item ttl/expiresAt must be of type integer or \DateTimeInterface.');
}
return $this;
}
/**
* {@inheritdoc}
*/
public function expiresAfter($time)
{
if ($time === null) {
$this->expirationTimestamp = null;
} elseif ($time instanceof \DateInterval) {
$date = new \DateTime();
$date->add($time);
$this->expirationTimestamp = $date->getTimestamp();
} elseif (is_int($time)) {
$this->expirationTimestamp = time() + $time;
} else {
throw new InvalidArgumentException('Cache item ttl/expiresAfter must be of type integer or \DateInterval.');
}
return $this;
}
/**
* {@inheritdoc}
*/
public function getPreviousTags()
{
$this->initialize();
return $this->prevTags;
}
/**
* {@inheritdoc}
*/
public function getTags()
{
return $this->tags;
}
/**
* {@inheritdoc}
*/
public function setTags(array $tags)
{
$this->tags = [];
$this->tag($tags);
return $this;
}
/**
* Adds a tag to a cache item.
*
* @param string|string[] $tags A tag or array of tags
*
* @throws InvalidArgumentException When $tag is not valid.
*
* @return TaggableCacheItemInterface
*/
private function tag($tags)
{
$this->initialize();
if (!is_array($tags)) {
$tags = [$tags];
}
foreach ($tags as $tag) {
if (!is_string($tag)) {
throw new InvalidArgumentException(sprintf('Cache tag must be string, "%s" given', is_object($tag) ? get_class($tag) : gettype($tag)));
}
if (isset($this->tags[$tag])) {
continue;
}
if (!isset($tag[0])) {
throw new InvalidArgumentException('Cache tag length must be greater than zero');
}
if (isset($tag[strcspn($tag, '{}()/\@:')])) {
throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters {}()/\@:', $tag));
}
$this->tags[$tag] = $tag;
}
return $this;
}
/**
* If callable is not null, execute it an populate this object with values.
*/
private function initialize()
{
if ($this->callable !== null) {
// $func will be $adapter->fetchObjectFromCache();
$func = $this->callable;
$result = $func();
$this->hasValue = $result[0];
$this->value = $result[1];
$this->prevTags = isset($result[2]) ? $result[2] : [];
$this->expirationTimestamp = null;
if (isset($result[3]) && is_int($result[3])) {
$this->expirationTimestamp = $result[3];
}
$this->callable = null;
}
}
/**
* @internal This function should never be used and considered private.
*
* Move tags from $tags to $prevTags
*/
public function moveTagsToPrevious()
{
$this->prevTags = $this->tags;
$this->tags = [];
}
}
+67
View File
@@ -0,0 +1,67 @@
# Change Log
The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
## 1.2.0
### Added
* Support for PHP 8
## 1.1.0
### Added
- Support for storing binary data
### Fixed
- Issue with one character variables
### Changed
- Tests are now extending `PHPUnit\Framework\TestCase`
## 1.0.0
* No changes since 0.4.0.
## 0.4.0
### Added
* `AbstractCachePool` has 4 new abstract methods: `getList`, `removeList`, `appendListItem` and `removeListItem`.
* `AbstractCachePool::invalidateTags` and `AbstractCachePool::invalidateTags`
* Added interfaces for our items and pools `PhpCachePool` and `PhpCacheItem`
* Trait to help adapters to support tags. `TagSupportWithArray`.
### Changed
* First parameter to `AbstractCachePool::storeItemInCache` must be a `PhpCacheItem`.
* Return value from `AbstractCachePool::fetchObjectFromCache` must be a an array with 4 values. Added expiration timestamp.
* `HasExpirationDateInterface` is replaced by `HasExpirationTimestampInterface`
* We do not work with `\DateTime` internally anymore. We work with timestamps.
## 0.3.3
### Fixed
* Bugfix when you fetch data from the cache storage that was saved as "non-tagging item" but fetch as a tagging item.
## 0.3.2
### Added
* Cache pools do implement `LoggerAwareInterface`
## 0.3.0
### Changed
* The `AbstractCachePool` does not longer implement `TaggablePoolInterface`. However, the `CacheItem` does still implement `TaggableItemInterface`.
* `CacheItem::getKeyFromTaggedKey` has been removed
* The `CacheItem`'s second parameter is a callable that must return an array with 3 elements; [`hasValue`, `value`, `tags`].
## 0.2.0
* No changelog before this version
@@ -0,0 +1,23 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common\Exception;
use Psr\Cache\CacheException as CacheExceptionInterface;
/**
* A base exception. All exceptions in this organization will extend this exception.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
abstract class CacheException extends \RuntimeException implements CacheExceptionInterface
{
}
@@ -0,0 +1,21 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common\Exception;
/**
* If an exception is caused by a pool or by the cache storage.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class CachePoolException extends CacheException
{
}
@@ -0,0 +1,19 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common\Exception;
use Psr\Cache\InvalidArgumentException as CacheInvalidArgumentException;
use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInvalidArgumentException;
class InvalidArgumentException extends CacheException implements CacheInvalidArgumentException, SimpleCacheInvalidArgumentException
{
}
@@ -0,0 +1,26 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common;
/**
* @author Aaron Scherer <aequasi@gmail.com>
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface HasExpirationTimestampInterface
{
/**
* The timestamp when the object expires.
*
* @return int|null
*/
public function getExpirationTimestamp();
}
+68
View File
@@ -0,0 +1,68 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common;
/**
* This trait provides common routines for safely encoding binary and non-UTF8 data in
* JSON. This is needed for components that use JSON natively (currently, the MongoDB
* adapter and EncryptedCachePool).
*
* @author Stephen Clouse <stephen.clouse@noaa.gov>
*/
trait JsonBinaryArmoring
{
private static $ESCAPE_JSON_CHARACTERS = [
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
"\x08", "\x09", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F",
];
private static $ENCODED_JSON_CHARACTERS = [
'\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007',
'\u0008', '\u0009', '\u000A', '\u000B', '\u000C', '\u000D', '\u000E', '\u000F',
'\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', '\u0017',
'\u0018', '\u0019', '\u001A', '\u001B', '\u001C', '\u001D', '\u001E', '\u001F',
];
/**
* Armor a value going into a JSON document.
*
* @param string $value
*
* @return string
*/
protected static function jsonArmor($value)
{
return str_replace(
static::$ESCAPE_JSON_CHARACTERS,
static::$ENCODED_JSON_CHARACTERS,
utf8_encode($value)
);
}
/**
* De-armor a value from a JSON document.
*
* @param string $value
*
* @return string
*/
protected static function jsonDeArmor($value)
{
return utf8_decode(str_replace(
static::$ENCODED_JSON_CHARACTERS,
static::$ESCAPE_JSON_CHARACTERS,
$value
));
}
}
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Aaron Scherer, Tobias Nyholm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+32
View File
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common;
use Cache\TagInterop\TaggableCacheItemInterface;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface PhpCacheItem extends HasExpirationTimestampInterface, TaggableCacheItemInterface
{
/**
* Get the current tags. These are not the same tags as getPrevious tags. This
* is the tags that has been added to the item after the item was fetched from
* the cache storage.
*
* WARNING: This is generally not the function you want to use. Please see
* `getPreviousTags`.
*
* @return array
*/
public function getTags();
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common;
use Cache\TagInterop\TaggableCacheItemPoolInterface;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface PhpCachePool extends TaggableCacheItemPoolInterface
{
/**
* {@inheritdoc}
*
* @return PhpCacheItem
*/
public function getItem($key);
/**
* {@inheritdoc}
*
* @return array|\Traversable|PhpCacheItem[]
*/
public function getItems(array $keys = []);
}
+15
View File
@@ -0,0 +1,15 @@
# Common PSR-6 Cache pool
[![Gitter](https://badges.gitter.im/php-cache/cache.svg)](https://gitter.im/php-cache/cache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Latest Stable Version](https://poser.pugx.org/cache/adapter-common/v/stable)](https://packagist.org/packages/cache/adapter-common)
[![codecov.io](https://codecov.io/github/php-cache/adapter-common/coverage.svg?branch=master)](https://codecov.io/github/php-cache/adapter-common?branch=master)
[![Total Downloads](https://poser.pugx.org/cache/adapter-common/downloads)](https://packagist.org/packages/cache/adapter-common)
[![Monthly Downloads](https://poser.pugx.org/cache/adapter-common/d/monthly.png)](https://packagist.org/packages/cache/adapter-common)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
This repository contains shared classes and interfaces used by the PHP Cache organisation. To read about
features like tagging and hierarchy support please read the shared documentation at [www.php-cache.com](http://www.php-cache.com).
### Contribute
Contributions are very welcome! Send a pull request to the [main repository](https://github.com/php-cache/cache) or
report any issues you find on the [issue tracker](http://issues.php-cache.com).
+88
View File
@@ -0,0 +1,88 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Common;
/**
* This trait could be used by adapters that do not have a native support for lists.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
trait TagSupportWithArray
{
/**
* Get a value from the storage.
*
* @param string $name
*
* @return mixed
*/
abstract public function getDirectValue($name);
/**
* Set a value to the storage.
*
* @param string $name
* @param mixed $value
*/
abstract public function setDirectValue($name, $value);
/**
* {@inheritdoc}
*/
protected function appendListItem($name, $value)
{
$data = $this->getDirectValue($name);
if (!is_array($data)) {
$data = [];
}
$data[] = $value;
$this->setDirectValue($name, $data);
}
/**
* {@inheritdoc}
*/
protected function getList($name)
{
$data = $this->getDirectValue($name);
if (!is_array($data)) {
$data = [];
}
return $data;
}
/**
* {@inheritdoc}
*/
protected function removeList($name)
{
$this->setDirectValue($name, []);
return true;
}
/**
* {@inheritdoc}
*/
protected function removeListItem($name, $key)
{
$data = $this->getList($name);
foreach ($data as $i => $value) {
if ($key === $value) {
unset($data[$i]);
}
}
return $this->setDirectValue($name, $data);
}
}
+55
View File
@@ -0,0 +1,55 @@
{
"name": "cache/adapter-common",
"type": "library",
"description": "Common classes for PSR-6 adapters",
"keywords": [
"cache",
"psr-6",
"tag"
],
"homepage": "http://www.php-cache.com/en/latest/",
"license": "MIT",
"authors": [
{
"name": "Aaron Scherer",
"email": "aequasi@gmail.com",
"homepage": "https://github.com/aequasi"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
}
],
"require": {
"php": "^5.6 || ^7.0 || ^8.0",
"cache/tag-interop": "^1.0",
"psr/cache": "^1.0",
"psr/log": "^1.0",
"psr/simple-cache": "^1.0"
},
"require-dev": {
"cache/integration-tests": "^0.16",
"phpunit/phpunit": "^5.7.21"
},
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
},
"autoload": {
"psr-4": {
"Cache\\Adapter\\Common\\": ""
}
},
"autoload-dev": {
"psr-4": {
"Cache\\Adapter\\Common\\Tests\\": "Tests/"
},
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"prefer-stable": true
}
+58
View File
@@ -0,0 +1,58 @@
# Change Log
The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
## UNRELEASED
## 1.1.0
### Added
* Support for PHP 8
### Changed
* Use `League\Flysystem\FilesystemInterface` instead of concrete `League\Flysystem\Filesystem` class
## 1.0.0
* No changes since 0.4.0
## 0.4.0
### Added
* Support for the new `TaggableCacheItemPoolInterface`.
* Support for PSR-16 SimpleCache
### Changed
* The behavior of `CacheItem::getTags()` has changed. It will not return the tags stored in the cache storage.
### Removed
* `CacheItem::getExpirationDate()`. Use `CacheItem::getExpirationTimestamp()`
* `CacheItem::getTags()`. Use `CacheItem::getPreviousTags()`
* `CacheItem::addTag()`. Use `CacheItem::setTags()`
## 0.3.3
### Fixed
* Race condition in `fetchObjectFromCache`.
## 0.3.2
### Changed
* Using `Filesystem::update` instead of `Filesystem::delete` and `Filesystem::write`.
## 0.3.1
### Added
* Add ability to change cache path in FilesystemCachePool
## 0.3.0
* No changelog before this version
+213
View File
@@ -0,0 +1,213 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\Adapter\Filesystem;
use Cache\Adapter\Common\AbstractCachePool;
use Cache\Adapter\Common\Exception\InvalidArgumentException;
use Cache\Adapter\Common\PhpCacheItem;
use League\Flysystem\FileExistsException;
use League\Flysystem\FileNotFoundException;
use League\Flysystem\FilesystemInterface;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class FilesystemCachePool extends AbstractCachePool
{
/**
* @type FilesystemInterface
*/
private $filesystem;
/**
* The folder should not begin nor end with a slash. Example: path/to/cache.
*
* @type string
*/
private $folder;
/**
* @param FilesystemInterface $filesystem
* @param string $folder
*/
public function __construct(FilesystemInterface $filesystem, $folder = 'cache')
{
$this->folder = $folder;
$this->filesystem = $filesystem;
$this->filesystem->createDir($this->folder);
}
/**
* @param string $folder
*/
public function setFolder($folder)
{
$this->folder = $folder;
}
/**
* {@inheritdoc}
*/
protected function fetchObjectFromCache($key)
{
$empty = [false, null, [], null];
$file = $this->getFilePath($key);
try {
$data = @unserialize($this->filesystem->read($file));
if ($data === false) {
return $empty;
}
} catch (FileNotFoundException $e) {
return $empty;
}
// Determine expirationTimestamp from data, remove items if expired
$expirationTimestamp = $data[2] ?: null;
if ($expirationTimestamp !== null && time() > $expirationTimestamp) {
foreach ($data[1] as $tag) {
$this->removeListItem($this->getTagKey($tag), $key);
}
$this->forceClear($key);
return $empty;
}
return [true, $data[0], $data[1], $expirationTimestamp];
}
/**
* {@inheritdoc}
*/
protected function clearAllObjectsFromCache()
{
$this->filesystem->deleteDir($this->folder);
$this->filesystem->createDir($this->folder);
return true;
}
/**
* {@inheritdoc}
*/
protected function clearOneObjectFromCache($key)
{
return $this->forceClear($key);
}
/**
* {@inheritdoc}
*/
protected function storeItemInCache(PhpCacheItem $item, $ttl)
{
$data = serialize(
[
$item->get(),
$item->getTags(),
$item->getExpirationTimestamp(),
]
);
$file = $this->getFilePath($item->getKey());
if ($this->filesystem->has($file)) {
// Update file if it exists
return $this->filesystem->update($file, $data);
}
try {
return $this->filesystem->write($file, $data);
} catch (FileExistsException $e) {
// To handle issues when/if race conditions occurs, we try to update here.
return $this->filesystem->update($file, $data);
}
}
/**
* @param string $key
*
* @throws InvalidArgumentException
*
* @return string
*/
private function getFilePath($key)
{
if (!preg_match('|^[a-zA-Z0-9_\.! ]+$|', $key)) {
throw new InvalidArgumentException(sprintf('Invalid key "%s". Valid filenames must match [a-zA-Z0-9_\.! ].', $key));
}
return sprintf('%s/%s', $this->folder, $key);
}
/**
* {@inheritdoc}
*/
protected function getList($name)
{
$file = $this->getFilePath($name);
if (!$this->filesystem->has($file)) {
$this->filesystem->write($file, serialize([]));
}
return unserialize($this->filesystem->read($file));
}
/**
* {@inheritdoc}
*/
protected function removeList($name)
{
$file = $this->getFilePath($name);
$this->filesystem->delete($file);
}
/**
* {@inheritdoc}
*/
protected function appendListItem($name, $key)
{
$list = $this->getList($name);
$list[] = $key;
return $this->filesystem->update($this->getFilePath($name), serialize($list));
}
/**
* {@inheritdoc}
*/
protected function removeListItem($name, $key)
{
$list = $this->getList($name);
foreach ($list as $i => $item) {
if ($item === $key) {
unset($list[$i]);
}
}
return $this->filesystem->update($this->getFilePath($name), serialize($list));
}
/**
* @param $key
*
* @return bool
*/
private function forceClear($key)
{
try {
return $this->filesystem->delete($this->getFilePath($key));
} catch (FileNotFoundException $e) {
return true;
}
}
}
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Aaron Scherer, Tobias Nyholm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+45
View File
@@ -0,0 +1,45 @@
# Filesystem PSR-6 Cache pool
[![Gitter](https://badges.gitter.im/php-cache/cache.svg)](https://gitter.im/php-cache/cache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Latest Stable Version](https://poser.pugx.org/cache/filesystem-adapter/v/stable)](https://packagist.org/packages/cache/filesystem-adapter)
[![codecov.io](https://codecov.io/github/php-cache/filesystem-adapter/coverage.svg?branch=master)](https://codecov.io/github/php-cache/filesystem-adapter?branch=master)
[![Total Downloads](https://poser.pugx.org/cache/filesystem-adapter/downloads)](https://packagist.org/packages/cache/filesystem-adapter)
[![Monthly Downloads](https://poser.pugx.org/cache/filesystem-adapter/d/monthly.png)](https://packagist.org/packages/cache/filesystem-adapter)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
This is a PSR-6 cache implementation using Filesystem. It is a part of the PHP Cache organisation. To read about
features like tagging and hierarchy support please read the shared documentation at [www.php-cache.com](http://www.php-cache.com).
This implementation is using the excellent [Flysystem](http://flysystem.thephpleague.com/).
### Install
```bash
composer require cache/filesystem-adapter
```
### Use
To create an instance of `FilesystemCachePool` you need to configure a `Filesystem` and its adapter.
```php
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use Cache\Adapter\Filesystem\FilesystemCachePool;
$filesystemAdapter = new Local(__DIR__.'/');
$filesystem = new Filesystem($filesystemAdapter);
$pool = new FilesystemCachePool($filesystem);
```
You can change the folder the cache pool will write to through the `setFolder` setter:
```php
$pool = new FilesystemCachePool($filesystem);
$pool->setFolder('path/to/cache');
```
### Contribute
Contributions are very welcome! Send a pull request to the [main repository](https://github.com/php-cache/cache) or
report any issues you find on the [issue tracker](http://issues.php-cache.com).
+55
View File
@@ -0,0 +1,55 @@
{
"name": "cache/filesystem-adapter",
"type": "library",
"description": "A PSR-6 cache implementation using filesystem. This implementation supports tags",
"keywords": [
"cache",
"psr-6",
"filesystem",
"tag"
],
"homepage": "http://www.php-cache.com/en/latest/",
"license": "MIT",
"authors": [
{
"name": "Aaron Scherer",
"email": "aequasi@gmail.com",
"homepage": "https://github.com/aequasi"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
}
],
"require": {
"php": "^5.6 || ^7.0 || ^8.0",
"cache/adapter-common": "^1.0",
"league/flysystem": "^1.0",
"psr/cache": "^1.0",
"psr/simple-cache": "^1.0"
},
"provide": {
"psr/cache-implementation": "^1.0",
"psr/simple-cache-implementation": "^1.0"
},
"require-dev": {
"cache/integration-tests": "^0.16",
"phpunit/phpunit": "^5.7.21"
},
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
},
"autoload": {
"psr-4": {
"Cache\\Adapter\\Filesystem\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"prefer-stable": true
}
@@ -0,0 +1,5 @@
This is a READ ONLY repository.
Please make your pull request to https://github.com/php-cache/cache
Thank you for contributing.
+2
View File
@@ -0,0 +1,2 @@
composer.lock
vendor
+22
View File
@@ -0,0 +1,22 @@
language: php
sudo: false
matrix:
include:
- php: 7.1
cache:
directories:
- "$HOME/.composer/cache"
install:
- composer update --prefer-dist --prefer-stable
script:
- ./vendor/bin/phpunit --coverage-clover=coverage.xml
after_success:
- pip install --user codecov && codecov
notifications:
email: false
+18
View File
@@ -0,0 +1,18 @@
# Change Log
The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
## 1.1.0
* Support PHP 8.1
* Support for psr/cache v2
## 1.0.1
* Support PHP 8
## 1.0.0
* First release
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Aaron Scherer, Tobias Nyholm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+25
View File
@@ -0,0 +1,25 @@
# Tag support for PSR-6 Cache
[![Gitter](https://badges.gitter.im/php-cache/cache.svg)](https://gitter.im/php-cache/cache?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Latest Stable Version](https://poser.pugx.org/cache/tag-interop/v/stable)](https://packagist.org/packages/cache/tag-interop)
[![Total Downloads](https://poser.pugx.org/cache/tag-interop/downloads)](https://packagist.org/packages/cache/tag-interop)
[![Monthly Downloads](https://poser.pugx.org/cache/tag-interop/d/monthly.png)](https://packagist.org/packages/cache/tag-interop)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
This repository holds two interfaces for tagging. These interfaces will make their
way into PHP Fig. Representatives from Symfony, PHP-cache and Drupal has worked
together to agree on these interfaces.
### Install
```bash
composer require cache/tag-interop
```
### Use
Read the [documentation on usage](http://www.php-cache.com/).
### Contribute
Contributions are very welcome! Send a pull request to the [main repository](https://github.com/php-cache/cache) or
report any issues you find on the [issue tracker](http://issues.php-cache.com).
+43
View File
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\TagInterop;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\InvalidArgumentException;
/**
* An item that supports tags. This interface is a soon-to-be-PSR.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
interface TaggableCacheItemInterface extends CacheItemInterface
{
/**
* Get all existing tags. These are the tags the item has when the item is
* returned from the pool.
*
* @return array
*/
public function getPreviousTags();
/**
* Overwrite all tags with a new set of tags.
*
* @param string[] $tags An array of tags
*
* @throws InvalidArgumentException When a tag is not valid.
*
* @return TaggableCacheItemInterface
*/
public function setTags(array $tags);
}
@@ -0,0 +1,60 @@
<?php
/*
* This file is part of php-cache organization.
*
* (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Cache\TagInterop;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\InvalidArgumentException;
/**
* Interface for invalidating cached items using tags. This interface is a soon-to-be-PSR.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
interface TaggableCacheItemPoolInterface extends CacheItemPoolInterface
{
/**
* Invalidates cached items using a tag.
*
* @param string $tag The tag to invalidate
*
* @throws InvalidArgumentException When $tags is not valid
*
* @return bool True on success
*/
public function invalidateTag($tag);
/**
* Invalidates cached items using tags.
*
* @param string[] $tags An array of tags to invalidate
*
* @throws InvalidArgumentException When $tags is not valid
*
* @return bool True on success
*/
public function invalidateTags(array $tags);
/**
* {@inheritdoc}
*
* @return TaggableCacheItemInterface
*/
public function getItem($key);
/**
* {@inheritdoc}
*
* @return array|\Traversable|TaggableCacheItemInterface[]
*/
public function getItems(array $keys = []);
}
+39
View File
@@ -0,0 +1,39 @@
{
"name": "cache/tag-interop",
"description": "Framework interoperable interfaces for tags",
"license": "MIT",
"type": "library",
"keywords": [
"cache",
"psr6",
"tag",
"psr"
],
"authors": [
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/nyholm"
},
{
"name": "Nicolas Grekas ",
"email": "p@tchwork.com",
"homepage": "https://github.com/nicolas-grekas"
}
],
"homepage": "https://www.php-cache.com/en/latest/",
"require": {
"php": "^5.5 || ^7.0 || ^8.0",
"psr/cache": "^1.0 || ^2.0"
},
"autoload": {
"psr-4": {
"Cache\\TagInterop\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
}
}