From 5a86700c302a7b29e925e683f24fff9960b25638 Mon Sep 17 00:00:00 2001 From: Keckeis Martin Date: Fri, 26 Feb 2016 15:13:03 +0100 Subject: [PATCH 1/2] adding memory adapter --- .travis.yml | 57 +++-- composer.json | 5 +- src/Adapter/MemoryCacheItemPool.php | 196 ++++++++++++++++++ src/{ => Adapter}/NullCacheItemPool.php | 5 +- src/CacheItem.php | 55 ++++- .../unit/Adapter/MemoryCacheItemPoolTest.php | 138 ++++++++++++ .../{ => Adapter}/NullCacheItemPoolTest.php | 8 +- tests/unit/CacheItemTest.php | 42 +++- 8 files changed, 459 insertions(+), 47 deletions(-) create mode 100644 src/Adapter/MemoryCacheItemPool.php rename src/{ => Adapter}/NullCacheItemPool.php (98%) create mode 100644 tests/unit/Adapter/MemoryCacheItemPoolTest.php rename tests/unit/{ => Adapter}/NullCacheItemPoolTest.php (94%) diff --git a/.travis.yml b/.travis.yml index bd0657f..d4c6242 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,28 @@ -## Run on container environment -sudo: false - -language: php - -php: - - 5.6 - - 7 - - hhvm - -env: - matrix: - - PREFER_LOWEST="--prefer-lowest" - - PREFER_LOWEST="" - -matrix: - allow_failures: - - php: hhvm - -before_script: - - composer update -o --prefer-source $PREFER_LOWEST - -script: - - vendor/bin/phpunit --coverage-clover build/logs/clover.xml - - vendor/bin/php-cs-fixer fix --dry-run -vv - -after_script: - - if [ "$TRAVIS_PHP_VERSION" != "7" ] && [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi - - if [ "$TRAVIS_PHP_VERSION" != "7" ] && [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml; fi +## Run on container environment +sudo: false + +language: php + +php: + - 7 + - hhvm + +env: + matrix: + - PREFER_LOWEST="--prefer-lowest" + - PREFER_LOWEST="" + +matrix: + allow_failures: + - php: hhvm + +before_script: + - composer update -o --prefer-source $PREFER_LOWEST + +script: + - vendor/bin/phpunit --coverage-clover build/logs/clover.xml + - vendor/bin/php-cs-fixer fix --dry-run -vv + +after_script: + - if [ "$TRAVIS_PHP_VERSION" != "7" ] && [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi + - if [ "$TRAVIS_PHP_VERSION" != "7" ] && [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml; fi diff --git a/composer.json b/composer.json index c7d0528..8519fb6 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "thadafinser/psr6-null-cache", - "description": "PSR-6 cache NullObject implementation, to use it as default and or testing", + "description": "PSR-6 cache NullObject implementation, to avoid null checks and for testing", "license": "MIT", @@ -24,7 +24,8 @@ }, "require": { - "php": ">=5.6", + "php": "~7.0", + "psr/cache": "^1.0" }, diff --git a/src/Adapter/MemoryCacheItemPool.php b/src/Adapter/MemoryCacheItemPool.php new file mode 100644 index 0000000..4fc0fb6 --- /dev/null +++ b/src/Adapter/MemoryCacheItemPool.php @@ -0,0 +1,196 @@ +hasItem($key) !== true) { + $this->data[$key] = new CacheItem($key, null, false); + } + + return $this->data[$key]; + } + + /** + * Returns a traversable set of cache items. + * + * @param array $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 = []) + { + $result = []; + + foreach ($keys as $key) { + $result[$key] = $this->getItem($key); + } + + return $result; + } + + /** + * 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) + { + if (isset($this->data[$key])) { + + /* @var $item \Psr6NullCache\CacheItem */ + $item = $this->data[$key]; + + if ($item->isHit() === true && ($item->getExpires() === null || $item->getExpires() > new DateTime())) { + return true; + } + } + + return false; + } + + /** + * Deletes all items in the pool. + * + * @return bool True if the pool was successfully cleared. False if there was an error. + */ + public function clear() + { + $this->data = []; + + return true; + } + + /** + * Removes the item from the pool. + * + * @param string $key + * The key for which 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) + { + unset($this->data[$key]); + + return true; + } + + /** + * Removes multiple items from the pool. + * + * @param array $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) + { + foreach ($keys as $key) { + $this->deleteItem($key); + } + + return true; + } + + /** + * 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) + { + $item->setIsHit(true); + + $this->data[$item->getKey()] = $item; + + 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->deferred[$item->getKey()] = $item; + + return true; + } + + /** + * 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() + { + foreach ($this->deferred as $item) { + /* @var $item \Psr6NullCache\CacheItem */ + $this->save($item); + } + + $this->deferred = []; + + return true; + } +} diff --git a/src/NullCacheItemPool.php b/src/Adapter/NullCacheItemPool.php similarity index 98% rename from src/NullCacheItemPool.php rename to src/Adapter/NullCacheItemPool.php index 85e75aa..e7fbf71 100644 --- a/src/NullCacheItemPool.php +++ b/src/Adapter/NullCacheItemPool.php @@ -1,8 +1,9 @@ key = $key; $this->value = $value; $this->isHit = (bool) $isHit; + $this->expires = $expires; } /** @@ -57,9 +67,18 @@ public function getKey() */ public function get() { + if ($this->isHit() !== true) { + return null; + } + return $this->value; } + public function setIsHit($mode = true) + { + $this->isHit = $mode; + } + /** * Confirms if the cache item lookup resulted in a cache hit. * @@ -73,6 +92,15 @@ public function isHit() return $this->isHit; } + /** + * + * @return DateTimeInterface null + */ + public function getExpires() + { + return $this->expires; + } + /** * Sets the value represented by this cache item. * @@ -95,7 +123,7 @@ public function set($value) /** * Sets the expiration time for this cache item. * - * @param \DateTimeInterface $expiration + * @param \DateTimeInterface $expires * 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 @@ -103,8 +131,14 @@ public function set($value) * * @return static The called object. */ - public function expiresAt($expiration) + public function expiresAt($expires) { + if ($expires instanceof DateTimeInterface) { + $this->expires = $expires; + } else { + $this->expires = null; + } + return $this; } @@ -122,6 +156,19 @@ public function expiresAt($expiration) */ public function expiresAfter($time) { + if ($time instanceof DateInterval) { + $expires = new DateTime(); + $expires->add($time); + + $this->expires = $expires; + } elseif (is_numeric($time)) { + $expires = new DateTime('now +' . $time . ' seconds'); + + $this->expires = $expires; + } else { + $this->expires = null; + } + return $this; } -} +} diff --git a/tests/unit/Adapter/MemoryCacheItemPoolTest.php b/tests/unit/Adapter/MemoryCacheItemPoolTest.php new file mode 100644 index 0000000..8b29820 --- /dev/null +++ b/tests/unit/Adapter/MemoryCacheItemPoolTest.php @@ -0,0 +1,138 @@ +getItem('myKey'); + + $this->assertInstanceOf('Psr6NullCache\CacheItem', $item); + $this->assertEquals('myKey', $item->getKey()); + // not from cache -> new object, so false! + $this->assertFalse($item->isHit()); + $this->assertNull($item->get()); + } + + public function testGetItems() + { + $pool = new MemoryCacheItemPool(); + + $items = $pool->getItems([ + 'myKey', + 'myKey2' + ]); + + $this->assertInternalType('array', $items); + $this->assertCount(2, $items); + + $this->assertArrayHasKey('myKey', $items); + $this->assertArrayHasKey('myKey2', $items); + + $this->assertInstanceOf('Psr6NullCache\CacheItem', $items['myKey']); + $this->assertInstanceOf('Psr6NullCache\CacheItem', $items['myKey2']); + } + + public function testHasItem() + { + $pool = new MemoryCacheItemPool(); + + // not in $this->data + $this->assertFalse($pool->hasItem('myKey')); + + // isHit() is false (not saved) + $item = $pool->getItem('myKey'); + $this->assertFalse($pool->hasItem('myKey')); + + // saved, so all ok! + $pool->save($item); + $this->assertTrue($pool->hasItem('myKey')); + + // expired item + $date = new \DateTime('now -5 seconds'); + $item->expiresAt($date); + $pool->save($item); + $this->assertFalse($pool->hasItem('myKey')); + } + + public function testClear() + { + $pool = new MemoryCacheItemPool(); + $this->assertTrue($pool->clear()); + + $item = $pool->getItem('myKey'); + $pool->save($item); + $this->assertTrue($pool->hasItem('myKey')); + $this->assertTrue($pool->clear()); + $this->assertFalse($pool->hasItem('myKey')); + } + + public function testDeleteItem() + { + $pool = new MemoryCacheItemPool(); + $this->assertTrue($pool->deleteItem('not-available-key')); + + $item = $pool->getItem('myKey'); + $pool->save($item); + $this->assertTrue($pool->hasItem('myKey')); + $this->assertTrue($pool->deleteItem('myKey')); + $this->assertFalse($pool->hasItem('myKey')); + } + + public function testDeleteItems() + { + $pool = new MemoryCacheItemPool(); + $this->assertTrue($pool->deleteItems([])); + + $item = $pool->getItem('myKey'); + $pool->save($item); + $item = $pool->getItem('myKey2'); + $pool->save($item); + + $this->assertTrue($pool->hasItem('myKey')); + $this->assertTrue($pool->hasItem('myKey2')); + $this->assertTrue($pool->deleteItems([ + 'myKey', + 'myKey2' + ])); + $this->assertFalse($pool->hasItem('myKey')); + $this->assertFalse($pool->hasItem('myKey2')); + } + + public function testSave() + { + $pool = new MemoryCacheItemPool(); + + $item = $pool->getItem('myKey'); + $this->assertFalse($item->isHit()); + + $pool->save($item); + $this->assertTrue($item->isHit()); + $this->assertTrue($pool->hasItem('myKey')); + } + + public function testDeferred() + { + $pool = new MemoryCacheItemPool(); + + $item = $pool->getItem('myKey'); + + $pool->saveDeferred($item); + $this->assertFalse($item->isHit()); + $this->assertFalse($pool->hasItem('myKey')); + + $pool->commit(); + $this->assertTrue($item->isHit()); + $this->assertTrue($pool->hasItem('myKey')); + } +} diff --git a/tests/unit/NullCacheItemPoolTest.php b/tests/unit/Adapter/NullCacheItemPoolTest.php similarity index 94% rename from tests/unit/NullCacheItemPoolTest.php rename to tests/unit/Adapter/NullCacheItemPoolTest.php index 765d780..5b1f261 100644 --- a/tests/unit/NullCacheItemPoolTest.php +++ b/tests/unit/Adapter/NullCacheItemPoolTest.php @@ -1,12 +1,12 @@ assertTrue($pool->commit()); } -} +} diff --git a/tests/unit/CacheItemTest.php b/tests/unit/CacheItemTest.php index 1b6005f..494b484 100644 --- a/tests/unit/CacheItemTest.php +++ b/tests/unit/CacheItemTest.php @@ -15,22 +15,33 @@ public function testCreateSimpleCacheItem() $item = new CacheItem(123, 'myValue', false); $this->assertEquals(123, $item->getKey()); - $this->assertEquals('myValue', $item->get()); $this->assertFalse($item->isHit()); + // because it's no hit, remove the value! + $this->assertNull($item->get()); } public function testCreateAnotherCacheItem() { - $item = new CacheItem('id', null, true); + $item = new CacheItem('id', 'myValue', true); $this->assertEquals('id', $item->getKey()); - $this->assertNull($item->get()); $this->assertTrue($item->isHit()); + $this->assertEquals('myValue', $item->get()); } - public function testSet() + public function testSetIsHit() { $item = new CacheItem('id', 'a value', false); + + $this->assertFalse($item->isHit()); + + $item->setIsHit(true); + $this->assertTrue($item->isHit()); + } + + public function testSet() + { + $item = new CacheItem('id', 'a value', true); $this->assertEquals('a value', $item->get()); @@ -38,17 +49,36 @@ public function testSet() $this->assertEquals('changed value', $item->get()); } - public function testExpiresAtFluidInterface() + public function testExpiresAt() { $item = new CacheItem(123, 'myValue', false); + $this->assertNull($item->getExpires()); $this->assertSame($item, $item->expiresAt('date in the future')); + $this->assertNull($item->getExpires()); + + // valid expiration + $myDate = new \DateTime(); + $item->expiresAt($myDate); + $this->assertSame($myDate, $item->getExpires()); } public function testExpiresAfterFluidInterface() { $item = new CacheItem(123, 'myValue', false); + $this->assertNull($item->getExpires()); $this->assertSame($item, $item->expiresAfter('date in the future')); + $this->assertNull($item->getExpires()); + + // add 5 seconds + $myDate = new \DateTime(); + $item->expiresAfter(5); + $this->assertGreaterThan($myDate, $item->getExpires()); + + // add 3 months + $interval = new \DateInterval('P3M'); + $item->expiresAfter($interval); + $this->assertGreaterThan($myDate, $item->getExpires()); } -} +} From ab990a3624ea8830ea4548cab38a90ae3521819c Mon Sep 17 00:00:00 2001 From: Keckeis Martin Date: Fri, 26 Feb 2016 15:16:05 +0100 Subject: [PATCH 2/2] php-cs-fixer --- src/Adapter/NullCacheItemPool.php | 2 +- src/CacheItem.php | 2 +- tests/unit/Adapter/MemoryCacheItemPoolTest.php | 2 +- tests/unit/Adapter/NullCacheItemPoolTest.php | 2 +- tests/unit/CacheItemTest.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Adapter/NullCacheItemPool.php b/src/Adapter/NullCacheItemPool.php index e7fbf71..b64408e 100644 --- a/src/Adapter/NullCacheItemPool.php +++ b/src/Adapter/NullCacheItemPool.php @@ -149,4 +149,4 @@ public function commit() { return true; } -} +} diff --git a/src/CacheItem.php b/src/CacheItem.php index 1de3ad9..24af8bb 100644 --- a/src/CacheItem.php +++ b/src/CacheItem.php @@ -171,4 +171,4 @@ public function expiresAfter($time) return $this; } -} +} diff --git a/tests/unit/Adapter/MemoryCacheItemPoolTest.php b/tests/unit/Adapter/MemoryCacheItemPoolTest.php index 8b29820..d212bb9 100644 --- a/tests/unit/Adapter/MemoryCacheItemPoolTest.php +++ b/tests/unit/Adapter/MemoryCacheItemPoolTest.php @@ -135,4 +135,4 @@ public function testDeferred() $this->assertTrue($item->isHit()); $this->assertTrue($pool->hasItem('myKey')); } -} +} diff --git a/tests/unit/Adapter/NullCacheItemPoolTest.php b/tests/unit/Adapter/NullCacheItemPoolTest.php index 5b1f261..b1d2731 100644 --- a/tests/unit/Adapter/NullCacheItemPoolTest.php +++ b/tests/unit/Adapter/NullCacheItemPoolTest.php @@ -100,4 +100,4 @@ public function testCommit() $this->assertTrue($pool->commit()); } -} +} diff --git a/tests/unit/CacheItemTest.php b/tests/unit/CacheItemTest.php index 494b484..fa3e8af 100644 --- a/tests/unit/CacheItemTest.php +++ b/tests/unit/CacheItemTest.php @@ -81,4 +81,4 @@ public function testExpiresAfterFluidInterface() $item->expiresAfter($interval); $this->assertGreaterThan($myDate, $item->getExpires()); } -} +}