diff --git a/composer.json b/composer.json index 37c23be5303e..5c5816819c65 100644 --- a/composer.json +++ b/composer.json @@ -12,16 +12,9 @@ "ext-mbstring": "*", "kint-php/kint": "^3.3", "laminas/laminas-escaper": "^2.6", - "psr/cache": "^1.0", - "psr/log": "^1.1", - "psr/simple-cache": "^1.0" - }, - "provide": { - "psr/cache-implementation": "^1.0", - "psr/simple-cache-implementation": "^1.0" + "psr/log": "^1.1" }, "require-dev": { - "cache/integration-tests": "^0.17.0", "codeigniter4/codeigniter4-standard": "^1.0", "fakerphp/faker": "^1.9", "mikey179/vfsstream": "^1.6", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 80dd419a5e25..c4f8ee944a89 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -43,9 +43,6 @@ ./tests/system/Database - - ./tests/psr - diff --git a/system/Psr/Cache/CacheArgumentException.php b/system/Psr/Cache/CacheArgumentException.php deleted file mode 100644 index 9b7a6f49d965..000000000000 --- a/system/Psr/Cache/CacheArgumentException.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace CodeIgniter\Psr\Cache; - -use InvalidArgumentException; -use Psr\Cache\InvalidArgumentException as CacheException; -use Psr\SimpleCache\InvalidArgumentException as SimpleCacheException; - -final class CacheArgumentException extends InvalidArgumentException implements CacheException, SimpleCacheException -{ -} diff --git a/system/Psr/Cache/Item.php b/system/Psr/Cache/Item.php deleted file mode 100644 index 48bb82449424..000000000000 --- a/system/Psr/Cache/Item.php +++ /dev/null @@ -1,266 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace CodeIgniter\Psr\Cache; - -use CodeIgniter\Cache\Handlers\BaseHandler; -use CodeIgniter\I18n\Time; -use DateInterval; -use DateTimeInterface; -use InvalidArgumentException; -use Psr\Cache\CacheItemInterface; - -final class Item implements CacheItemInterface -{ - /** - * Reserved characters that cannot be used in a key or tag. - * - * @see https://github.com/symfony/cache-contracts/blob/c0446463729b89dd4fa62e9aeecc80287323615d/ItemInterface.php#L43 - */ - public const RESERVED_CHARACTERS = '{}()/\@:'; - - /** - * @var string - */ - protected $key; - - /** - * @var mixed - */ - protected $value; - - /** - * Whether this Item was the result - * of a cache hit. - * - * @var boolean - */ - protected $hit; - - /** - * The expiration time - * - * @var Time|null - */ - protected $expiration; - - /** - * Validates a cache key according to PSR-6. - * - * @param mixed $key The key to validate - * - * @throws CacheArgumentException When $key is not valid - */ - public static function validateKey($key) - { - // Use the framework's Cache key validation - try - { - BaseHandler::validateKey($key); - } - catch (InvalidArgumentException $e) - { - throw new CacheArgumentException($e->getMessage(), $e->getCode(), $e); - } - } - - /** - * Stores the Item's details. - * - * @param string $key - * @param mixed $value - * @param boolean $hit - */ - public function __construct(string $key, $value, bool $hit) - { - $this->key = $key; - $this->value = $value; - $this->hit = $hit; - } - - /** - * 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(): string - { - 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 boolean - * True if the request resulted in a cache hit. False otherwise. - */ - public function isHit(): bool - { - return $this->hit; - } - - /** - * 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): self - { - $this->value = $value; - - 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): self - { - if ($expiration === null) - { - $this->expiration = null; - } - elseif ($expiration instanceof DateTimeInterface) - { - $this->expiration = Time::createFromInstance($expiration); - } - else - { - throw new CacheArgumentException('Expiration date must be a DateTimeInterface or null'); - } - - return $this; - } - - /** - * Sets the expiration time for this cache item. - * - * @param integer|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): self - { - if ($time === null) - { - $this->expiration = null; - } - elseif ($time instanceof DateInterval) - { - $this->expiration = Time::now()->add($time); - } - elseif (is_int($time)) - { - $this->expiration = Time::now()->addSeconds($time); - } - else - { - throw new CacheArgumentException('Expiration date must be an integer, a DateInterval or null'); - } - - return $this; - } - - /** - * Returns the expiration Time. - * This method is not a requirement of PSR-6 but is necessary - * to pass "testExpiration". - * - * @see https://groups.google.com/g/php-fig/c/Qr4OxCf7J5Y - * - * @return Time|null - */ - public function getExpiration(): ?Time - { - return $this->expiration; - } - - /** - * Returns whether or not this Item is expired. - * This method is not a requirement of PSR-6 but is necessary - * to pass "testSavedExpired". - * - * @see https://groups.google.com/g/php-fig/c/Qr4OxCf7J5Y - * - * @return boolean True if this Item is expired. - */ - public function isExpired(): bool - { - if (isset($this->expiration)) - { - $now = Time::now(); - return $this->expiration->isBefore($now) || $this->expiration->sameAs($now); - } - - return false; - } - - /** - * Sets the hit value. - * This method is not a requirement of PSR-6 but is necessary - * to allow deferred items to count as hits. - * - * @return $this - */ - public function setHit(bool $hit): self - { - $this->hit = $hit; - - return $this; - } -} diff --git a/system/Psr/Cache/Pool.php b/system/Psr/Cache/Pool.php deleted file mode 100644 index 7f13dd573e48..000000000000 --- a/system/Psr/Cache/Pool.php +++ /dev/null @@ -1,302 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace CodeIgniter\Psr\Cache; - -use CodeIgniter\I18n\Time; -use Psr\Cache\CacheItemInterface; -use Psr\Cache\CacheItemPoolInterface; - -final class Pool implements CacheItemPoolInterface -{ - use SupportTrait; - - /** - * Deferred Items to be saved. - * - * @var array - */ - private $deferred = []; - - /** - * Commits any deferred Items. - */ - public function __destruct() - { - $this->commit(); - } - - /** - * 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 CacheArgumentException - * 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): CacheItemInterface - { - Item::validateKey($key); - - // First check for a deferred Item - if (array_key_exists($key, $this->deferred) && ! $this->deferred[$key]->isExpired()) - { - return (clone $this->deferred[$key])->setHit(true); - } - - $meta = $this->adapter->getMetaData($key); - - // If the adapter does not return an array or if the item is expired then it is a miss - if (! is_array($meta) || (is_int($meta['expire']) && $meta['expire'] < time())) - { - return new Item($key, null, false); - } - - // Create the Item with the actual value - $item = new Item($key, $this->adapter->get($key), true); - - // Check for an expiration - if ($meta['expire'] !== null) - { - $item->expiresAt(Time::createFromTimestamp($meta['expire'])); - } - - return $item; - } - - /** - * Returns a traversable set of cache items. - * - * @param string[] $keys - * An indexed array of keys of items to retrieve. - * - * @throws CacheArgumentException - * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException - * MUST be thrown. - * - * @return array - * 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 = []): array - { - // CacheInterface has no spec for multiple item retrieval - // so we have to power through them individually. - $items = []; - foreach ($keys as $key) - { - $items[$key] = $this->getItem($key); - } - - return $items; - } - - /** - * 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 CacheArgumentException - * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException - * MUST be thrown. - * - * @return boolean - * True if item exists in the cache, false otherwise. - */ - public function hasItem($key): bool - { - Item::validateKey($key); - - // First check for a deferred Item - if (array_key_exists($key, $this->deferred) && ! $this->deferred[$key]->isExpired()) - { - return true; - } - - return is_array($this->adapter->getMetaData($key)); - } - - /** - * Deletes all items in the pool. - * - * @return boolean - * True if the pool was successfully cleared. False if there was an error. - */ - public function clear() - { - $this->deferred = []; - - return $this->adapter->clean(); - } - - /** - * Removes the item from the pool. - * - * @param string $key - * The key to delete. - * - * @throws CacheArgumentException - * If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException - * MUST be thrown. - * - * @return boolean - * True if the item was successfully removed. False if there was an error. - */ - public function deleteItem($key): bool - { - Item::validateKey($key); - - // First check for a deferred Item - if (array_key_exists($key, $this->deferred)) - { - unset($this->deferred[$key]); - return true; - } - - if ($this->hasItem($key)) - { - return $this->adapter->delete($key); - } - - return true; - } - - /** - * Removes multiple items from the pool. - * - * @param string[] $keys - * An array of keys that should be removed from the pool. - * - * @throws CacheArgumentException - * If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException - * MUST be thrown. - * - * @return boolean - * True if the items were successfully removed. False if there was an error. - */ - public function deleteItems(array $keys): bool - { - // CacheInterface has no spec for multiple item removal - // so we have to power through them individually. - $return = true; - foreach ($keys as $key) - { - $result = $this->deleteItem($key); - $return = $return && $result; - } - - return $return; - } - - /** - * Persists a cache item immediately. - * - * @param CacheItemInterface $item - * The cache item to save. - * - * @return boolean - * True if the item was successfully persisted. False if there was an error. - */ - public function save(CacheItemInterface $item) - { - // Only deal in our Pool's Items - if (! $item instanceof Item) - { - return false; - } - - // Do not save expired Items - if ($item->isExpired()) - { - $this->deleteItem($item->getKey()); - return false; - } - - // Deteremine TTL - $ttl = ($expiration = $item->getExpiration()) ? Time::now()->difference($expiration)->getSeconds() : 60; - - return $this->adapter->save($item->getKey(), $item->get(), $ttl); - } - - /** - * Sets a cache item to be persisted later. - * - * @param CacheItemInterface $item - * The cache item to save. - * - * @return boolean - * False if the item could not be queued or if a commit was attempted and failed. True otherwise. - */ - public function saveDeferred(CacheItemInterface $item): bool - { - // Only deal in our Pool's Items - if (! $item instanceof Item) - { - return false; - } - - // Do not save expired Items - if ($item->isExpired()) - { - return false; - } - - $this->deferred[$item->getKey()] = clone $item; - - return true; - } - - /** - * Persists any deferred cache items. - * - * @return boolean - * True if all not-yet-saved items were successfully saved or there were none. False otherwise. - */ - public function commit(): bool - { - if ($this->deferred === []) - { - return true; - } - - $failed = []; - foreach ($this->deferred as $item) - { - if (! $this->save($item)) - { - $failed[$item->getKey()] = $item; - } - } - - if ($failed === []) - { - return true; - } - - $this->deferred = $failed; - return false; - } -} diff --git a/system/Psr/Cache/SimpleCache.php b/system/Psr/Cache/SimpleCache.php deleted file mode 100644 index bc180c33df34..000000000000 --- a/system/Psr/Cache/SimpleCache.php +++ /dev/null @@ -1,244 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace CodeIgniter\Psr\Cache; - -use DateInterval; -use Psr\SimpleCache\CacheInterface; -use Traversable; - -final class SimpleCache implements CacheInterface -{ - use SupportTrait; - - /** - * Fetches a value from the cache. - * - * @param string $key The unique key of this item in the cache. - * @param mixed $default Default value to return if the key does not exist. - * - * @return mixed The value of the item from the cache, or $default in case of cache miss. - * - * @throws CacheArgumentException - * MUST be thrown if the $key string is not a legal value. - */ - public function get($key, $default = null) - { - Item::validateKey($key); - - $meta = $this->adapter->getMetaData($key); - - // If the adapter does not return an array or if the item is expired then it is a miss - if (! is_array($meta) || (is_int($meta['expire']) && $meta['expire'] < time())) - { - return $default; - } - - return $this->adapter->get($key); - } - - /** - * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time. - * - * @param string $key The key of the item to store. - * @param mixed $value The value of the item to store. Must be serializable. - * @param null|integer|DateInterval $ttl Optional. The TTL value of this item. If no value is sent and - * the driver supports TTL then the library may set a default value - * for it or let the driver take care of that. - * - * @return boolean True on success and false on failure. - * - * @throws CacheArgumentException - * MUST be thrown if the $key string is not a legal value. - */ - public function set($key, $value, $ttl = null) - { - Item::validateKey($key); - - // Get TTL as an integer (seconds) - if (is_null($ttl)) - { - $ttl = 60; - } - elseif ($ttl instanceof DateInterval) - { - $ttl = $ttl->s; - } - elseif (! is_int($ttl)) - { - throw new CacheArgumentException('TTL value must be one of: null, integer, DateInterval.'); - } - - // Do not save expired items - if ($ttl <= 0) - { - $this->delete($key); - return false; - } - - return $this->adapter->save($key, $value, $ttl); - } - - /** - * Delete an item from the cache by its unique key. - * - * @param string $key The unique cache key of the item to delete. - * - * @return boolean True if the item was successfully removed. False if there was an error. - * - * @throws CacheArgumentException - * MUST be thrown if the $key string is not a legal value. - */ - public function delete($key) - { - Item::validateKey($key); - - // Nonexistant keys return true - if (! is_array($this->adapter->getMetaData($key))) - { - return true; - } - - return $this->adapter->delete($key); - } - - /** - * Wipes clean the entire cache's keys. - * - * @return boolean True on success and false on failure. - */ - public function clear() - { - return $this->adapter->clean(); - } - - /** - * Obtains multiple cache items by their unique keys. - * - * @param iterable $keys A list of keys that can obtained in a single operation. - * @param mixed $default Default value to return for keys that do not exist. - * - * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value. - * - * @throws CacheArgumentException - * MUST be thrown if $keys is neither an array nor a Traversable, - * or if any of the $keys are not a legal value. - */ - public function getMultiple($keys, $default = null) - { - if (! (is_array($keys) || $keys instanceof Traversable)) - { - throw new CacheArgumentException('getMultiple only accepts traversable input.'); - } - - // CacheInterface has no spec for multiple item retrieval - // so we have to power through them individually. - $items = []; - foreach ($keys as $key) - { - $items[$key] = $this->get($key, $default); - } - - return $items; - } - - /** - * Persists a set of key => value pairs in the cache, with an optional TTL. - * - * @param iterable $values A list of key => value pairs for a multiple-set operation. - * @param null|integer|DateInterval $ttl Optional. The TTL value of this item. If no value is sent and - * the driver supports TTL then the library may set a default value - * for it or let the driver take care of that. - * - * @return boolean True on success and false on failure. - * - * @throws CacheArgumentException - * MUST be thrown if $values is neither an array nor a Traversable, - * or if any of the $values are not a legal value. - */ - public function setMultiple($values, $ttl = null) - { - if (! (is_array($values) || $values instanceof Traversable)) - { - throw new CacheArgumentException('setMultiple only accepts traversable input.'); - } - - // CacheInterface has no spec for multiple item storage - // so we have to power through them individually. - $return = true; - foreach ($values as $key => $value) - { - if (is_int($key)) - { - $key = (string) $key; - } - $result = $this->set($key, $value, $ttl); - $return = $result && $return; - } - - return $return; - } - - /** - * Deletes multiple cache items in a single operation. - * - * @param iterable $keys A list of string-based keys to be deleted. - * - * @return boolean True if the items were successfully removed. False if there was an error. - * - * @throws CacheArgumentException - * MUST be thrown if $keys is neither an array nor a Traversable, - * or if any of the $keys are not a legal value. - */ - public function deleteMultiple($keys) - { - if (! (is_array($keys) || $keys instanceof Traversable)) - { - throw new CacheArgumentException('deleteMultiple only accepts traversable input.'); - } - - // CacheInterface has no spec for multiple item removal - // so we have to power through them individually. - $return = true; - foreach ($keys as $key) - { - $result = $this->delete($key); - $return = $result && $return; - } - - return $return; - } - - /** - * Determines whether an item is present in the cache. - * - * NOTE: It is recommended that has() is only to be used for cache warming type purposes - * and not to be used within your live applications operations for get/set, as this method - * is subject to a race condition where your has() will return true and immediately after, - * another script can remove it, making the state of your app out of date. - * - * @param string $key The cache item key. - * - * @return boolean - * - * @throws CacheArgumentException - * MUST be thrown if the $key string is not a legal value. - */ - public function has($key) - { - Item::validateKey($key); - - $meta = $this->adapter->getMetaData($key); - - // The adapter must return an array that is not expired - return is_array($meta) && is_int($meta['expire']) && $meta['expire'] > time(); - } -} diff --git a/system/Psr/Cache/SupportTrait.php b/system/Psr/Cache/SupportTrait.php deleted file mode 100644 index d3534af3a33a..000000000000 --- a/system/Psr/Cache/SupportTrait.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace CodeIgniter\Psr\Cache; - -use CodeIgniter\Cache\CacheInterface; -use Config\Cache; - -/** - * Cache Support Trait - * - * Provides methods common to both - * PSR-6 and PSR-16 drivers. - */ -trait SupportTrait -{ - /** - * The adapter to use. - * - * @var CacheInterface - */ - private $adapter; - - /** - * Initializes the underlying adapter - * from an existing instance or from the - * Cache Service (with optional config). - * - * @param object|null $object - * - * @throws CacheArgumentException - */ - public function __construct($object = null) - { - if (is_null($object)) - { - $this->adapter = service('cache'); - } - elseif ($object instanceof Cache) - { - $this->adapter = service('cache', $object, false); - } - elseif ($object instanceof CacheInterface) - { - $this->adapter = $object; - } - else - { - throw new CacheArgumentException(get_class() . ' constructor only accepts an adapter or configuration'); - } - } -} diff --git a/tests/psr/CachePoolTest.php b/tests/psr/CachePoolTest.php deleted file mode 100644 index d6df7c5cafee..000000000000 --- a/tests/psr/CachePoolTest.php +++ /dev/null @@ -1,16 +0,0 @@ -getPrivateProperty($psr, 'adapter'); - - $this->assertInstanceOf(MockCache::class, $result); - } - - public function testUsesConfig() - { - $config = new Cache(); - $config->handler = 'dummy'; - - $psr = new SimpleCache($config); - $result = $this->getPrivateProperty($psr, 'adapter'); - - $this->assertInstanceOf(DummyHandler::class, $result); - } - - public function testUsesHandler() - { - $psr = new SimpleCache(new MockCache()); - $result = $this->getPrivateProperty($psr, 'adapter'); - - $this->assertInstanceOf(MockCache::class, $result); - } - - public function testThrowsException() - { - $this->expectException(CacheArgumentException::class); - $this->expectExceptionMessage('CodeIgniter\Psr\Cache\SimpleCache constructor only accepts an adapter or configuration'); - - $psr = new SimpleCache(42); - } -} diff --git a/user_guide_src/source/intro/psr.rst b/user_guide_src/source/intro/psr.rst index 4bf04d15ce04..096a2450acc1 100644 --- a/user_guide_src/source/intro/psr.rst +++ b/user_guide_src/source/intro/psr.rst @@ -30,8 +30,8 @@ classes. Our :doc:`Autoloader ` meets the PSR-4 recommenda **PSR-6: Caching Interfaces** **PSR-16: SimpleCache Interface** -While the framework Cache components do not adhere to PSR-6 or PSR-16, a separate set of adapters are -provided for both in ``CodeIgniter\Psr\Cache``. It is recommended that projects use the native Cache +While the framework Cache components do not adhere to PSR-6 or PSR-16, a separate set of adapters will +be provided for both as a supplemental module. It is recommended that projects use the native Cache drivers directly as the adapters are only intended for compatibility with third-party libraries. **PSR-7: HTTP Message Interface**