diff --git a/src/Storage/Adapter/Apc.php b/src/Storage/Adapter/Apc.php index 2196ff5f4..8f4ecf0d9 100644 --- a/src/Storage/Adapter/Apc.php +++ b/src/Storage/Adapter/Apc.php @@ -734,4 +734,19 @@ protected function normalizeMetadata(array & $metadata) $metadata['num_hits'] ); } + + /** + * Internal method to set an item only if token matches + * + * @param mixed $token + * @param string $normalizedKey + * @param mixed $value + * @return bool + * @see getItem() + * @see setItem() + */ + protected function internalCheckAndSetItem(& $token, & $normalizedKey, & $value) + { + return apc_cas($normalizedKey, $token, $value); + } } diff --git a/src/Storage/Adapter/BlackHole.php b/src/Storage/Adapter/BlackHole.php new file mode 100644 index 000000000..52b0309ce --- /dev/null +++ b/src/Storage/Adapter/BlackHole.php @@ -0,0 +1,502 @@ +setOptions($options); + } + } + + /** + * Set options. + * + * @param array|Traversable|Adapter\AdapterOptions $options + * @return StorageInterface Fluent interface + */ + public function setOptions($options) + { + if ($this->options !== $options) { + if (!$options instanceof AdapterOptions) { + $options = new AdapterOptions($options); + } + + if ($this->options) { + $this->options->setAdapter(null); + } + $options->setAdapter($this); + $this->options = $options; + } + return $this; + } + + /** + * Get options + * + * @return Adapter\AdapterOptions + */ + public function getOptions() + { + if (!$this->options) { + $this->setOptions(new AdapterOptions()); + } + return $this->options; + } + + /** + * Get an item. + * + * @param string $key + * @param bool $success + * @param mixed $casToken + * @return mixed Data on success, null on failure + */ + public function getItem($key, & $success = null, & $casToken = null) + { + $success = false; + return null; + } + + /** + * Get multiple items. + * + * @param array $keys + * @return array Associative array of keys and values + */ + public function getItems(array $keys) + { + return array(); + } + + /** + * Test if an item exists. + * + * @param string $key + * @return bool + */ + public function hasItem($key) + { + return false; + } + + /** + * Test multiple items. + * + * @param array $keys + * @return array Array of found keys + */ + public function hasItems(array $keys) + { + return array(); + } + + /** + * Get metadata of an item. + * + * @param string $key + * @return array|bool Metadata on success, false on failure + */ + public function getMetadata($key) + { + return false; + } + + /** + * Get multiple metadata + * + * @param array $keys + * @return array Associative array of keys and metadata + */ + public function getMetadatas(array $keys) + { + return array(); + } + + /** + * Store an item. + * + * @param string $key + * @param mixed $value + * @return bool + */ + public function setItem($key, $value) + { + return false; + } + + /** + * Store multiple items. + * + * @param array $keyValuePairs + * @return array Array of not stored keys + */ + public function setItems(array $keyValuePairs) + { + return array_keys($keyValuePairs); + } + + /** + * Add an item. + * + * @param string $key + * @param mixed $value + * @return bool + */ + public function addItem($key, $value) + { + return false; + } + + /** + * Add multiple items. + * + * @param array $keyValuePairs + * @return array Array of not stored keys + */ + public function addItems(array $keyValuePairs) + { + return array_keys($keyValuePairs); + } + + /** + * Replace an existing item. + * + * @param string $key + * @param mixed $value + * @return bool + */ + public function replaceItem($key, $value) + { + return false; + } + + /** + * Replace multiple existing items. + * + * @param array $keyValuePairs + * @return array Array of not stored keys + */ + public function replaceItems(array $keyValuePairs) + { + return array_keys($keyValuePairs); + } + + /** + * Set an item only if token matches + * + * It uses the token received from getItem() to check if the item has + * changed before overwriting it. + * + * @param mixed $token + * @param string $key + * @param mixed $value + * @return bool + */ + public function checkAndSetItem($token, $key, $value) + { + return false; + } + + /** + * Reset lifetime of an item + * + * @param string $key + * @return bool + */ + public function touchItem($key) + { + return false; + } + + /** + * Reset lifetime of multiple items. + * + * @param array $keys + * @return array Array of not updated keys + */ + public function touchItems(array $keys) + { + return $keys; + } + + /** + * Remove an item. + * + * @param string $key + * @return bool + */ + public function removeItem($key) + { + return false; + } + + /** + * Remove multiple items. + * + * @param array $keys + * @return array Array of not removed keys + */ + public function removeItems(array $keys) + { + return $keys; + } + + /** + * Increment an item. + * + * @param string $key + * @param int $value + * @return int|bool The new value on success, false on failure + */ + public function incrementItem($key, $value) + { + return false; + } + + /** + * Increment multiple items. + * + * @param array $keyValuePairs + * @return array Associative array of keys and new values + */ + public function incrementItems(array $keyValuePairs) + { + return array(); + } + + /** + * Decrement an item. + * + * @param string $key + * @param int $value + * @return int|bool The new value on success, false on failure + */ + public function decrementItem($key, $value) + { + return false; + } + + /** + * Decrement multiple items. + * + * @param array $keyValuePairs + * @return array Associative array of keys and new values + */ + public function decrementItems(array $keyValuePairs) + { + return array(); + } + + /** + * Capabilities of this storage + * + * @return Capabilities + */ + public function getCapabilities() + { + if ($this->capabilities === null) { + // use default capabilities only + $this->capabilityMarker = new stdClass(); + $this->capabilities = new Capabilities($this, $this->capabilityMarker); + } + return $this->capabilities; + } + + /* AvailableSpaceCapableInterface */ + + /** + * Get available space in bytes + * + * @return int|float + */ + public function getAvailableSpace() + { + return 0; + } + + /* ClearByNamespaceInterface */ + + /** + * Remove items of given namespace + * + * @param string $namespace + * @return bool + */ + public function clearByNamespace($namespace) + { + return false; + } + + /* ClearByPrefixInterface */ + + /** + * Remove items matching given prefix + * + * @param string $prefix + * @return bool + */ + public function clearByPrefix($prefix) + { + return false; + } + + /* ClearExpiredInterface */ + + /** + * Remove expired items + * + * @return bool + */ + public function clearExpired() + { + return false; + } + + /* FlushableInterface */ + + /** + * Flush the whole storage + * + * @return bool + */ + public function flush() + { + return false; + } + + /* IterableInterface */ + + /** + * Get the storage iterator + * + * @return KeyIterator + */ + public function getIterator() + { + return new KeyListIterator($this, array()); + } + + /* OptimizableInterface */ + + /** + * Optimize the storage + * + * @return bool + */ + public function optimize() + { + return false; + } + + /* TaggableInterface */ + + /** + * Set tags to an item by given key. + * An empty array will remove all tags. + * + * @param string $key + * @param string[] $tags + * @return bool + */ + public function setTags($key, array $tags) + { + return false; + } + + /** + * Get tags of an item by given key + * + * @param string $key + * @return string[]|FALSE + */ + public function getTags($key) + { + return false; + } + + /** + * Remove items matching given tags. + * + * If $disjunction only one of the given tags must match + * else all given tags must match. + * + * @param string[] $tags + * @param bool $disjunction + * @return bool + */ + public function clearByTags(array $tags, $disjunction = false) + { + return false; + } + + /* TotalSpaceCapableInterface */ + + /** + * Get total space in bytes + * + * @return int|float + */ + public function getTotalSpace() + { + return 0; + } +} diff --git a/src/Storage/AdapterPluginManager.php b/src/Storage/AdapterPluginManager.php index 9f133fb0e..4a5bc8256 100644 --- a/src/Storage/AdapterPluginManager.php +++ b/src/Storage/AdapterPluginManager.php @@ -28,6 +28,7 @@ class AdapterPluginManager extends AbstractPluginManager */ protected $invokableClasses = array( 'apc' => 'Zend\Cache\Storage\Adapter\Apc', + 'blackhole' => 'Zend\Cache\Storage\Adapter\BlackHole', 'dba' => 'Zend\Cache\Storage\Adapter\Dba', 'filesystem' => 'Zend\Cache\Storage\Adapter\Filesystem', 'memcached' => 'Zend\Cache\Storage\Adapter\Memcached', diff --git a/src/StorageFactory.php b/src/StorageFactory.php index 5779e9d09..0cb4fd496 100644 --- a/src/StorageFactory.php +++ b/src/StorageFactory.php @@ -10,6 +10,7 @@ namespace Zend\Cache; use Traversable; +use Zend\EventManager\EventsCapableInterface; use Zend\Stdlib\ArrayUtils; abstract class StorageFactory @@ -71,6 +72,14 @@ public static function factory($cfg) // add plugins if (isset($cfg['plugins'])) { + if (!$adapter instanceof EventsCapableInterface) { + throw new Exception\RuntimeException(sprintf( + "The adapter '%s' doesn't implement '%s' and therefore can't handle plugins", + get_class($adapter), + 'Zend\EventManager\EventsCapableInterface' + )); + } + if (!is_array($cfg['plugins'])) { throw new Exception\InvalidArgumentException( 'Plugins needs to be an array' diff --git a/test/Storage/Adapter/BlackHoleTest.php b/test/Storage/Adapter/BlackHoleTest.php new file mode 100644 index 000000000..f01bc75f1 --- /dev/null +++ b/test/Storage/Adapter/BlackHoleTest.php @@ -0,0 +1,155 @@ +storage = StorageFactory::adapterFactory('BlackHole'); + } + + public function testGetOptions() + { + $options = $this->storage->getOptions(); + $this->assertInstanceOf('Zend\Cache\Storage\Adapter\AdapterOptions', $options); + } + + public function testSetOptions() + { + $this->storage->setOptions(array('namespace' => 'test')); + $this->assertSame('test', $this->storage->getOptions()->getNamespace()); + } + + public function testGetCapabilities() + { + $capabilities = $this->storage->getCapabilities(); + $this->assertInstanceOf('Zend\Cache\Storage\Capabilities', $capabilities); + } + + public function testSingleStorageOperatios() + { + $this->assertFalse($this->storage->setItem('test', 1)); + $this->assertFalse($this->storage->addItem('test', 1)); + $this->assertFalse($this->storage->replaceItem('test', 1)); + $this->assertFalse($this->storage->touchItem('test')); + $this->assertFalse($this->storage->incrementItem('test', 1)); + $this->assertFalse($this->storage->decrementItem('test', 1)); + $this->assertFalse($this->storage->hasItem('test')); + $this->assertNull($this->storage->getItem('test', $success)); + $this->assertFalse($success); + $this->assertFalse($this->storage->getMetadata('test')); + $this->assertFalse($this->storage->removeItem('test')); + } + + public function testMultiStorageOperatios() + { + $this->assertSame(array('test'), $this->storage->setItems(array('test' => 1))); + $this->assertSame(array('test'), $this->storage->addItems(array('test' => 1))); + $this->assertSame(array('test'), $this->storage->replaceItems(array('test' => 1))); + $this->assertSame(array('test'), $this->storage->touchItems(array('test'))); + $this->assertSame(array(), $this->storage->incrementItems(array('test' => 1))); + $this->assertSame(array(), $this->storage->decrementItems(array('test' => 1))); + $this->assertSame(array(), $this->storage->hasItems(array('test'))); + $this->assertSame(array(), $this->storage->getItems(array('test'))); + $this->assertSame(array(), $this->storage->getMetadatas(array('test'))); + $this->assertSame(array('test'), $this->storage->removeItems(array('test'))); + } + + public function testAvailableSpaceCapableInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\AvailableSpaceCapableInterface', $this->storage); + $this->assertSame(0, $this->storage->getAvailableSpace()); + } + + public function testClearByNamespaceInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\ClearByNamespaceInterface', $this->storage); + $this->assertFalse($this->storage->clearByNamespace('test')); + } + + public function testClearByPrefixInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\ClearByPrefixInterface', $this->storage); + $this->assertFalse($this->storage->clearByPrefix('test')); + } + + public function testCleariExpiredInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\ClearExpiredInterface', $this->storage); + $this->assertFalse($this->storage->clearExpired()); + } + + public function testFlushableInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\FlushableInterface', $this->storage); + $this->assertFalse($this->storage->flush()); + } + + public function testIterableInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\IterableInterface', $this->storage); + $iterator = $this->storage->getIterator(); + foreach ($iterator as $item) { + $this->fail('The iterator of the BlackHole adapter should be empty'); + } + } + + public function testOptimizableInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\OptimizableInterface', $this->storage); + $this->assertFalse($this->storage->optimize()); + } + + public function testTaggableInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\TaggableInterface', $this->storage); + $this->assertFalse($this->storage->setTags('test', array('tag1'))); + $this->assertFalse($this->storage->getTags('test')); + $this->assertFalse($this->storage->clearByTags(array('tag1'))); + } + + public function testTotalSpaceCapableInterface() + { + $this->assertInstanceOf('Zend\Cache\Storage\TotalSpaceCapableInterface', $this->storage); + $this->assertSame(0, $this->storage->getTotalSpace()); + } +} diff --git a/test/StorageFactoryTest.php b/test/StorageFactoryTest.php index febf526ba..610006a72 100644 --- a/test/StorageFactoryTest.php +++ b/test/StorageFactoryTest.php @@ -135,6 +135,16 @@ public function testFactoryWithPlugins() } } + public function testFactoryInstantiateAdapterWithPluginsWithoutEventsCapableInterfaceThrowsException() + { + // The BlackHole adapter doesn't implement EventsCapableInterface + $this->setExpectedException('Zend\Cache\Exception\RuntimeException'); + Cache\StorageFactory::factory(array( + 'adapter' => 'blackhole', + 'plugins' => array('Serializer'), + )); + } + public function testFactoryWithPluginsAndOptionsArray() { $factory = array(