<?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ namespace Zend\Cache\Storage; use ArrayObject; use stdClass; use Zend\Cache\Exception; use Zend\EventManager\EventsCapableInterface; class Capabilities { /** * The storage instance * * @var StorageInterface */ protected $storage; /** * A marker to set/change capabilities * * @var stdClass */ protected $marker; /** * Base capabilities * * @var null|Capabilities */ protected $baseCapabilities; /** * Expire read * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|bool */ protected $expiredRead; /** * Max. key length * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|int */ protected $maxKeyLength; /** * Min. TTL (0 means items never expire) * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|int */ protected $minTtl; /** * Max. TTL (0 means infinite) * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|int */ protected $maxTtl; /** * Namespace is prefix * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|bool */ protected $namespaceIsPrefix; /** * Namespace separator * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|string */ protected $namespaceSeparator; /** * Static ttl * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|bool */ protected $staticTtl; /** * Supported datatypes * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|array */ protected $supportedDatatypes; /** * Supported metdata * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|array */ protected $supportedMetadata; /** * TTL precision * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|int */ protected $ttlPrecision; /** * Use request time * * If it's NULL the capability isn't set and the getter * returns the base capability or the default value. * * @var null|bool */ protected $useRequestTime; /** * Constructor * * @param StorageInterface $storage * @param stdClass $marker * @param array $capabilities * @param null|Capabilities $baseCapabilities */ public function __construct( StorageInterface $storage, stdClass $marker, array $capabilities = array(), Capabilities $baseCapabilities = null ) { $this->storage = $storage; $this->marker = $marker; $this->baseCapabilities = $baseCapabilities; foreach ($capabilities as $name => $value) { $this->setCapability($marker, $name, $value); } } /** * Get the storage adapter * * @return StorageInterface */ public function getAdapter() { return $this->storage; } /** * Get supported datatypes * * @return array */ public function getSupportedDatatypes() { return $this->getCapability('supportedDatatypes', array( 'NULL' => false, 'boolean' => false, 'integer' => false, 'double' => false, 'string' => true, 'array' => false, 'object' => false, 'resource' => false, )); } /** * Set supported datatypes * * @param stdClass $marker * @param array $datatypes * @throws Exception\InvalidArgumentException * @return Capabilities Fluent interface */ public function setSupportedDatatypes(stdClass $marker, array $datatypes) { $allTypes = array( 'array', 'boolean', 'double', 'integer', 'NULL', 'object', 'resource', 'string', ); // check/normalize datatype values foreach ($datatypes as $type => &$toType) { if (!in_array($type, $allTypes)) { throw new Exception\InvalidArgumentException("Unknown datatype '{$type}'"); } if (is_string($toType)) { $toType = strtolower($toType); if (!in_array($toType, $allTypes)) { throw new Exception\InvalidArgumentException("Unknown datatype '{$toType}'"); } } else { $toType = (bool) $toType; } } // add missing datatypes as not supported $missingTypes = array_diff($allTypes, array_keys($datatypes)); foreach ($missingTypes as $type) { $datatypes[$type] = false; } return $this->setCapability($marker, 'supportedDatatypes', $datatypes); } /** * Get supported metadata * * @return array */ public function getSupportedMetadata() { return $this->getCapability('supportedMetadata', array()); } /** * Set supported metadata * * @param stdClass $marker * @param string[] $metadata * @throws Exception\InvalidArgumentException * @return Capabilities Fluent interface */ public function setSupportedMetadata(stdClass $marker, array $metadata) { foreach ($metadata as $name) { if (!is_string($name)) { throw new Exception\InvalidArgumentException('$metadata must be an array of strings'); } } return $this->setCapability($marker, 'supportedMetadata', $metadata); } /** * Get minimum supported time-to-live * * @return int 0 means items never expire */ public function getMinTtl() { return $this->getCapability('minTtl', 0); } /** * Set minimum supported time-to-live * * @param stdClass $marker * @param int $minTtl * @throws Exception\InvalidArgumentException * @return Capabilities Fluent interface */ public function setMinTtl(stdClass $marker, $minTtl) { $minTtl = (int) $minTtl; if ($minTtl < 0) { throw new Exception\InvalidArgumentException('$minTtl must be greater or equal 0'); } return $this->setCapability($marker, 'minTtl', $minTtl); } /** * Get maximum supported time-to-live * * @return int 0 means infinite */ public function getMaxTtl() { return $this->getCapability('maxTtl', 0); } /** * Set maximum supported time-to-live * * @param stdClass $marker * @param int $maxTtl * @throws Exception\InvalidArgumentException * @return Capabilities Fluent interface */ public function setMaxTtl(stdClass $marker, $maxTtl) { $maxTtl = (int) $maxTtl; if ($maxTtl < 0) { throw new Exception\InvalidArgumentException('$maxTtl must be greater or equal 0'); } return $this->setCapability($marker, 'maxTtl', $maxTtl); } /** * Is the time-to-live handled static (on write) * or dynamic (on read) * * @return bool */ public function getStaticTtl() { return $this->getCapability('staticTtl', false); } /** * Set if the time-to-live handled static (on write) or dynamic (on read) * * @param stdClass $marker * @param bool $flag * @return Capabilities Fluent interface */ public function setStaticTtl(stdClass $marker, $flag) { return $this->setCapability($marker, 'staticTtl', (bool) $flag); } /** * Get time-to-live precision * * @return float */ public function getTtlPrecision() { return $this->getCapability('ttlPrecision', 1); } /** * Set time-to-live precision * * @param stdClass $marker * @param float $ttlPrecision * @throws Exception\InvalidArgumentException * @return Capabilities Fluent interface */ public function setTtlPrecision(stdClass $marker, $ttlPrecision) { $ttlPrecision = (float) $ttlPrecision; if ($ttlPrecision <= 0) { throw new Exception\InvalidArgumentException('$ttlPrecision must be greater than 0'); } return $this->setCapability($marker, 'ttlPrecision', $ttlPrecision); } /** * Get use request time * * @return bool */ public function getUseRequestTime() { return $this->getCapability('useRequestTime', false); } /** * Set use request time * * @param stdClass $marker * @param bool $flag * @return Capabilities Fluent interface */ public function setUseRequestTime(stdClass $marker, $flag) { return $this->setCapability($marker, 'useRequestTime', (bool) $flag); } /** * Get if expired items are readable * * @return bool */ public function getExpiredRead() { return $this->getCapability('expiredRead', false); } /** * Set if expired items are readable * * @param stdClass $marker * @param bool $flag * @return Capabilities Fluent interface */ public function setExpiredRead(stdClass $marker, $flag) { return $this->setCapability($marker, 'expiredRead', (bool) $flag); } /** * Get maximum key lenth * * @return int -1 means unknown, 0 means infinite */ public function getMaxKeyLength() { return $this->getCapability('maxKeyLength', -1); } /** * Set maximum key length * * @param stdClass $marker * @param int $maxKeyLength * @throws Exception\InvalidArgumentException * @return Capabilities Fluent interface */ public function setMaxKeyLength(stdClass $marker, $maxKeyLength) { $maxKeyLength = (int) $maxKeyLength; if ($maxKeyLength < -1) { throw new Exception\InvalidArgumentException('$maxKeyLength must be greater or equal than -1'); } return $this->setCapability($marker, 'maxKeyLength', $maxKeyLength); } /** * Get if namespace support is implemented as prefix * * @return bool */ public function getNamespaceIsPrefix() { return $this->getCapability('namespaceIsPrefix', true); } /** * Set if namespace support is implemented as prefix * * @param stdClass $marker * @param bool $flag * @return Capabilities Fluent interface */ public function setNamespaceIsPrefix(stdClass $marker, $flag) { return $this->setCapability($marker, 'namespaceIsPrefix', (bool) $flag); } /** * Get namespace separator if namespace is implemented as prefix * * @return string */ public function getNamespaceSeparator() { return $this->getCapability('namespaceSeparator', ''); } /** * Set the namespace separator if namespace is implemented as prefix * * @param stdClass $marker * @param string $separator * @return Capabilities Fluent interface */ public function setNamespaceSeparator(stdClass $marker, $separator) { return $this->setCapability($marker, 'namespaceSeparator', (string) $separator); } /** * Get a capability * * @param string $property * @param mixed $default * @return mixed */ protected function getCapability($property, $default = null) { if ($this->$property !== null) { return $this->$property; } elseif ($this->baseCapabilities) { $getMethod = 'get' . $property; return $this->baseCapabilities->$getMethod(); } return $default; } /** * Change a capability * * @param stdClass $marker * @param string $property * @param mixed $value * @return Capabilities Fluent interface * @throws Exception\InvalidArgumentException */ protected function setCapability(stdClass $marker, $property, $value) { if ($this->marker !== $marker) { throw new Exception\InvalidArgumentException('Invalid marker'); } if ($this->$property !== $value) { $this->$property = $value; // trigger event if ($this->storage instanceof EventsCapableInterface) { $this->storage->getEventManager()->trigger('capability', $this->storage, new ArrayObject(array( $property => $value ))); } } return $this; } }