setOptions($options); } } /** * Destructor * * detach all registered plugins to free * event handles of event manager * * @return void */ public function __destruct() { foreach ($this->getPluginRegistry() as $plugin) { $this->removePlugin($plugin); } if ($this->eventHandles) { $events = $this->getEventManager(); foreach ($this->eventHandles as $handle) { $events->detach($handle); } } } /* configuration */ /** * Set options. * * @param array|Traversable|AdapterOptions $options * @return AbstractAdapter * @see getOptions() */ 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; $event = new Event('option', $this, new ArrayObject($options->toArray())); $this->getEventManager()->trigger($event); } return $this; } /** * Get options. * * @return AdapterOptions * @see setOptions() */ public function getOptions() { if (!$this->options) { $this->setOptions(new AdapterOptions()); } return $this->options; } /** * Enable/Disable caching. * * Alias of setWritable and setReadable. * * @see setWritable() * @see setReadable() * @param bool $flag * @return AbstractAdapter */ public function setCaching($flag) { $flag = (bool) $flag; $options = $this->getOptions(); $options->setWritable($flag); $options->setReadable($flag); return $this; } /** * Get caching enabled. * * Alias of getWritable and getReadable. * * @see getWritable() * @see getReadable() * @return bool */ public function getCaching() { $options = $this->getOptions(); return ($options->getWritable() && $options->getReadable()); } /* Event/Plugin handling */ /** * Get the event manager * * @return EventManagerInterface */ public function getEventManager() { if ($this->events === null) { $this->events = new EventManager(array(__CLASS__, get_class($this))); } return $this->events; } /** * Trigger an pre event and return the event response collection * * @param string $eventName * @param ArrayObject $args * @return \Zend\EventManager\ResponseCollection All handler return values */ protected function triggerPre($eventName, ArrayObject $args) { return $this->getEventManager()->trigger(new Event($eventName . '.pre', $this, $args)); } /** * Triggers the PostEvent and return the result value. * * @param string $eventName * @param ArrayObject $args * @param mixed $result * @return mixed */ protected function triggerPost($eventName, ArrayObject $args, & $result) { $postEvent = new PostEvent($eventName . '.post', $this, $args, $result); $eventRs = $this->getEventManager()->trigger($postEvent); if ($eventRs->stopped()) { return $eventRs->last(); } return $postEvent->getResult(); } /** * Trigger an exception event * * If the ExceptionEvent has the flag "throwException" enabled throw the * exception after trigger else return the result. * * @param string $eventName * @param ArrayObject $args * @param mixed $result * @param \Exception $exception * @throws Exception\ExceptionInterface * @return mixed */ protected function triggerException($eventName, ArrayObject $args, & $result, \Exception $exception) { $exceptionEvent = new ExceptionEvent($eventName . '.exception', $this, $args, $result, $exception); $eventRs = $this->getEventManager()->trigger($exceptionEvent); if ($exceptionEvent->getThrowException()) { throw $exceptionEvent->getException(); } if ($eventRs->stopped()) { return $eventRs->last(); } return $exceptionEvent->getResult(); } /** * Check if a plugin is registered * * @param Plugin\PluginInterface $plugin * @return bool */ public function hasPlugin(Plugin\PluginInterface $plugin) { $registry = $this->getPluginRegistry(); return $registry->contains($plugin); } /** * Register a plugin * * @param Plugin\PluginInterface $plugin * @param int $priority * @return AbstractAdapter Fluent interface * @throws Exception\LogicException */ public function addPlugin(Plugin\PluginInterface $plugin, $priority = 1) { $registry = $this->getPluginRegistry(); if ($registry->contains($plugin)) { throw new Exception\LogicException(sprintf( 'Plugin of type "%s" already registered', get_class($plugin) )); } $plugin->attach($this->getEventManager(), $priority); $registry->attach($plugin); return $this; } /** * Unregister an already registered plugin * * @param Plugin\PluginInterface $plugin * @return AbstractAdapter Fluent interface * @throws Exception\LogicException */ public function removePlugin(Plugin\PluginInterface $plugin) { $registry = $this->getPluginRegistry(); if ($registry->contains($plugin)) { $plugin->detach($this->getEventManager()); $registry->detach($plugin); } return $this; } /** * Return registry of plugins * * @return SplObjectStorage */ public function getPluginRegistry() { if (!$this->pluginRegistry instanceof SplObjectStorage) { $this->pluginRegistry = new SplObjectStorage(); } return $this->pluginRegistry; } /* reading */ /** * Get an item. * * @param string $key * @param bool $success * @param mixed $casToken * @return mixed Data on success, null on failure * @throws Exception\ExceptionInterface * * @triggers getItem.pre(PreEvent) * @triggers getItem.post(PostEvent) * @triggers getItem.exception(ExceptionEvent) */ public function getItem($key, & $success = null, & $casToken = null) { if (!$this->getOptions()->getReadable()) { $success = false; return null; } $this->normalizeKey($key); $argn = func_num_args(); $args = array( 'key' => & $key, ); if ($argn > 1) { $args['success'] = & $success; } if ($argn > 2) { $args['casToken'] = & $casToken; } $args = new ArrayObject($args); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } if ($args->offsetExists('success') && $args->offsetExists('casToken')) { $result = $this->internalGetItem($args['key'], $args['success'], $args['casToken']); } elseif ($args->offsetExists('success')) { $result = $this->internalGetItem($args['key'], $args['success']); } else { $result = $this->internalGetItem($args['key']); } return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to get an item. * * @param string $normalizedKey * @param bool $success * @param mixed $casToken * @return mixed Data on success, null on failure * @throws Exception\ExceptionInterface */ abstract protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null); /** * Get multiple items. * * @param array $keys * @return array Associative array of keys and values * @throws Exception\ExceptionInterface * * @triggers getItems.pre(PreEvent) * @triggers getItems.post(PostEvent) * @triggers getItems.exception(ExceptionEvent) */ public function getItems(array $keys) { if (!$this->getOptions()->getReadable()) { return array(); } $this->normalizeKeys($keys); $args = new ArrayObject(array( 'keys' => & $keys, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalGetItems($args['keys']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = array(); return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to get multiple items. * * @param array $normalizedKeys * @return array Associative array of keys and values * @throws Exception\ExceptionInterface */ protected function internalGetItems(array & $normalizedKeys) { $success = null; $result = array(); foreach ($normalizedKeys as $normalizedKey) { $value = $this->internalGetItem($normalizedKey, $success); if ($success) { $result[$normalizedKey] = $value; } } return $result; } /** * Test if an item exists. * * @param string $key * @return bool * @throws Exception\ExceptionInterface * * @triggers hasItem.pre(PreEvent) * @triggers hasItem.post(PostEvent) * @triggers hasItem.exception(ExceptionEvent) */ public function hasItem($key) { if (!$this->getOptions()->getReadable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalHasItem($args['key']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to test if an item exists. * * @param string $normalizedKey * @return bool * @throws Exception\ExceptionInterface */ protected function internalHasItem(& $normalizedKey) { $success = null; $this->internalGetItem($normalizedKey, $success); return $success; } /** * Test multiple items. * * @param array $keys * @return array Array of found keys * @throws Exception\ExceptionInterface * * @triggers hasItems.pre(PreEvent) * @triggers hasItems.post(PostEvent) * @triggers hasItems.exception(ExceptionEvent) */ public function hasItems(array $keys) { if (!$this->getOptions()->getReadable()) { return array(); } $this->normalizeKeys($keys); $args = new ArrayObject(array( 'keys' => & $keys, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalHasItems($args['keys']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = array(); return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to test multiple items. * * @param array $normalizedKeys * @return array Array of found keys * @throws Exception\ExceptionInterface */ protected function internalHasItems(array & $normalizedKeys) { $result = array(); foreach ($normalizedKeys as $normalizedKey) { if ($this->internalHasItem($normalizedKey)) { $result[] = $normalizedKey; } } return $result; } /** * Get metadata of an item. * * @param string $key * @return array|bool Metadata on success, false on failure * @throws Exception\ExceptionInterface * * @triggers getMetadata.pre(PreEvent) * @triggers getMetadata.post(PostEvent) * @triggers getMetadata.exception(ExceptionEvent) */ public function getMetadata($key) { if (!$this->getOptions()->getReadable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalGetMetadata($args['key']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to get metadata of an item. * * @param string $normalizedKey * @return array|bool Metadata on success, false on failure * @throws Exception\ExceptionInterface */ protected function internalGetMetadata(& $normalizedKey) { if (!$this->internalHasItem($normalizedKey)) { return false; } return array(); } /** * Get multiple metadata * * @param array $keys * @return array Associative array of keys and metadata * @throws Exception\ExceptionInterface * * @triggers getMetadatas.pre(PreEvent) * @triggers getMetadatas.post(PostEvent) * @triggers getMetadatas.exception(ExceptionEvent) */ public function getMetadatas(array $keys) { if (!$this->getOptions()->getReadable()) { return array(); } $this->normalizeKeys($keys); $args = new ArrayObject(array( 'keys' => & $keys, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalGetMetadatas($args['keys']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = array(); return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to get multiple metadata * * @param array $normalizedKeys * @return array Associative array of keys and metadata * @throws Exception\ExceptionInterface */ protected function internalGetMetadatas(array & $normalizedKeys) { $result = array(); foreach ($normalizedKeys as $normalizedKey) { $metadata = $this->internalGetMetadata($normalizedKey); if ($metadata !== false) { $result[$normalizedKey] = $metadata; } } return $result; } /* writing */ /** * Store an item. * * @param string $key * @param mixed $value * @return bool * @throws Exception\ExceptionInterface * * @triggers setItem.pre(PreEvent) * @triggers setItem.post(PostEvent) * @triggers setItem.exception(ExceptionEvent) */ public function setItem($key, $value) { if (!$this->getOptions()->getWritable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, 'value' => & $value, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalSetItem($args['key'], $args['value']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to store an item. * * @param string $normalizedKey * @param mixed $value * @return bool * @throws Exception\ExceptionInterface */ abstract protected function internalSetItem(& $normalizedKey, & $value); /** * Store multiple items. * * @param array $keyValuePairs * @return array Array of not stored keys * @throws Exception\ExceptionInterface * * @triggers setItems.pre(PreEvent) * @triggers setItems.post(PostEvent) * @triggers setItems.exception(ExceptionEvent) */ public function setItems(array $keyValuePairs) { if (!$this->getOptions()->getWritable()) { return array_keys($keyValuePairs); } $this->normalizeKeyValuePairs($keyValuePairs); $args = new ArrayObject(array( 'keyValuePairs' => & $keyValuePairs, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalSetItems($args['keyValuePairs']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = array_keys($keyValuePairs); return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to store multiple items. * * @param array $normalizedKeyValuePairs * @return array Array of not stored keys * @throws Exception\ExceptionInterface */ protected function internalSetItems(array & $normalizedKeyValuePairs) { $failedKeys = array(); foreach ($normalizedKeyValuePairs as $normalizedKey => $value) { if (!$this->internalSetItem($normalizedKey, $value)) { $failedKeys[] = $normalizedKey; } } return $failedKeys; } /** * Add an item. * * @param string $key * @param mixed $value * @return bool * @throws Exception\ExceptionInterface * * @triggers addItem.pre(PreEvent) * @triggers addItem.post(PostEvent) * @triggers addItem.exception(ExceptionEvent) */ public function addItem($key, $value) { if (!$this->getOptions()->getWritable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, 'value' => & $value, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalAddItem($args['key'], $args['value']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to add an item. * * @param string $normalizedKey * @param mixed $value * @return bool * @throws Exception\ExceptionInterface */ protected function internalAddItem(& $normalizedKey, & $value) { if ($this->internalHasItem($normalizedKey)) { return false; } return $this->internalSetItem($normalizedKey, $value); } /** * Add multiple items. * * @param array $keyValuePairs * @return array Array of not stored keys * @throws Exception\ExceptionInterface * * @triggers addItems.pre(PreEvent) * @triggers addItems.post(PostEvent) * @triggers addItems.exception(ExceptionEvent) */ public function addItems(array $keyValuePairs) { if (!$this->getOptions()->getWritable()) { return array_keys($keyValuePairs); } $this->normalizeKeyValuePairs($keyValuePairs); $args = new ArrayObject(array( 'keyValuePairs' => & $keyValuePairs, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalAddItems($args['keyValuePairs']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = array_keys($keyValuePairs); return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to add multiple items. * * @param array $normalizedKeyValuePairs * @return array Array of not stored keys * @throws Exception\ExceptionInterface */ protected function internalAddItems(array & $normalizedKeyValuePairs) { $result = array(); foreach ($normalizedKeyValuePairs as $normalizedKey => $value) { if (!$this->internalAddItem($normalizedKey, $value)) { $result[] = $normalizedKey; } } return $result; } /** * Replace an existing item. * * @param string $key * @param mixed $value * @return bool * @throws Exception\ExceptionInterface * * @triggers replaceItem.pre(PreEvent) * @triggers replaceItem.post(PostEvent) * @triggers replaceItem.exception(ExceptionEvent) */ public function replaceItem($key, $value) { if (!$this->getOptions()->getWritable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, 'value' => & $value, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalReplaceItem($args['key'], $args['value']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to replace an existing item. * * @param string $normalizedKey * @param mixed $value * @return bool * @throws Exception\ExceptionInterface */ protected function internalReplaceItem(& $normalizedKey, & $value) { if (!$this->internalhasItem($normalizedKey)) { return false; } return $this->internalSetItem($normalizedKey, $value); } /** * Replace multiple existing items. * * @param array $keyValuePairs * @return array Array of not stored keys * @throws Exception\ExceptionInterface * * @triggers replaceItems.pre(PreEvent) * @triggers replaceItems.post(PostEvent) * @triggers replaceItems.exception(ExceptionEvent) */ public function replaceItems(array $keyValuePairs) { if (!$this->getOptions()->getWritable()) { return array_keys($keyValuePairs); } $this->normalizeKeyValuePairs($keyValuePairs); $args = new ArrayObject(array( 'keyValuePairs' => & $keyValuePairs, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalReplaceItems($args['keyValuePairs']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = array_keys($keyValuePairs); return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to replace multiple existing items. * * @param array $normalizedKeyValuePairs * @return array Array of not stored keys * @throws Exception\ExceptionInterface */ protected function internalReplaceItems(array & $normalizedKeyValuePairs) { $result = array(); foreach ($normalizedKeyValuePairs as $normalizedKey => $value) { if (!$this->internalReplaceItem($normalizedKey, $value)) { $result[] = $normalizedKey; } } return $result; } /** * 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 * @throws Exception\ExceptionInterface * @see getItem() * @see setItem() */ public function checkAndSetItem($token, $key, $value) { if (!$this->getOptions()->getWritable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'token' => & $token, 'key' => & $key, 'value' => & $value, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalCheckAndSetItem($args['token'], $args['key'], $args['value']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to set an item only if token matches * * @param mixed $token * @param string $normalizedKey * @param mixed $value * @return bool * @throws Exception\ExceptionInterface * @see getItem() * @see setItem() */ protected function internalCheckAndSetItem(& $token, & $normalizedKey, & $value) { $oldValue = $this->internalGetItem($normalizedKey); if ($oldValue !== $token) { return false; } return $this->internalSetItem($normalizedKey, $value); } /** * Reset lifetime of an item * * @param string $key * @return bool * @throws Exception\ExceptionInterface * * @triggers touchItem.pre(PreEvent) * @triggers touchItem.post(PostEvent) * @triggers touchItem.exception(ExceptionEvent) */ public function touchItem($key) { if (!$this->getOptions()->getWritable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalTouchItem($args['key']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to reset lifetime of an item * * @param string $normalizedKey * @return bool * @throws Exception\ExceptionInterface */ protected function internalTouchItem(& $normalizedKey) { $success = null; $value = $this->internalGetItem($normalizedKey, $success); if (!$success) { return false; } return $this->internalReplaceItem($normalizedKey, $value); } /** * Reset lifetime of multiple items. * * @param array $keys * @return array Array of not updated keys * @throws Exception\ExceptionInterface * * @triggers touchItems.pre(PreEvent) * @triggers touchItems.post(PostEvent) * @triggers touchItems.exception(ExceptionEvent) */ public function touchItems(array $keys) { if (!$this->getOptions()->getWritable()) { return $keys; } $this->normalizeKeys($keys); $args = new ArrayObject(array( 'keys' => & $keys, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalTouchItems($args['keys']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { return $this->triggerException(__FUNCTION__, $args, $keys, $e); } } /** * Internal method to reset lifetime of multiple items. * * @param array $normalizedKeys * @return array Array of not updated keys * @throws Exception\ExceptionInterface */ protected function internalTouchItems(array & $normalizedKeys) { $result = array(); foreach ($normalizedKeys as $normalizedKey) { if (!$this->internalTouchItem($normalizedKey)) { $result[] = $normalizedKey; } } return $result; } /** * Remove an item. * * @param string $key * @return bool * @throws Exception\ExceptionInterface * * @triggers removeItem.pre(PreEvent) * @triggers removeItem.post(PostEvent) * @triggers removeItem.exception(ExceptionEvent) */ public function removeItem($key) { if (!$this->getOptions()->getWritable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalRemoveItem($args['key']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to remove an item. * * @param string $normalizedKey * @return bool * @throws Exception\ExceptionInterface */ abstract protected function internalRemoveItem(& $normalizedKey); /** * Remove multiple items. * * @param array $keys * @return array Array of not removed keys * @throws Exception\ExceptionInterface * * @triggers removeItems.pre(PreEvent) * @triggers removeItems.post(PostEvent) * @triggers removeItems.exception(ExceptionEvent) */ public function removeItems(array $keys) { if (!$this->getOptions()->getWritable()) { return $keys; } $this->normalizeKeys($keys); $args = new ArrayObject(array( 'keys' => & $keys, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalRemoveItems($args['keys']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { return $this->triggerException(__FUNCTION__, $args, $keys, $e); } } /** * Internal method to remove multiple items. * * @param array $normalizedKeys * @return array Array of not removed keys * @throws Exception\ExceptionInterface */ protected function internalRemoveItems(array & $normalizedKeys) { $result = array(); foreach ($normalizedKeys as $normalizedKey) { if (!$this->internalRemoveItem($normalizedKey)) { $result[] = $normalizedKey; } } return $result; } /** * Increment an item. * * @param string $key * @param int $value * @return int|bool The new value on success, false on failure * @throws Exception\ExceptionInterface * * @triggers incrementItem.pre(PreEvent) * @triggers incrementItem.post(PostEvent) * @triggers incrementItem.exception(ExceptionEvent) */ public function incrementItem($key, $value) { if (!$this->getOptions()->getWritable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, 'value' => & $value, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalIncrementItem($args['key'], $args['value']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to increment an item. * * @param string $normalizedKey * @param int $value * @return int|bool The new value on success, false on failure * @throws Exception\ExceptionInterface */ protected function internalIncrementItem(& $normalizedKey, & $value) { $success = null; $value = (int) $value; $get = (int) $this->internalGetItem($normalizedKey, $success); $newValue = $get + $value; if ($success) { $this->internalReplaceItem($normalizedKey, $newValue); } else { $this->internalAddItem($normalizedKey, $newValue); } return $newValue; } /** * Increment multiple items. * * @param array $keyValuePairs * @return array Associative array of keys and new values * @throws Exception\ExceptionInterface * * @triggers incrementItems.pre(PreEvent) * @triggers incrementItems.post(PostEvent) * @triggers incrementItems.exception(ExceptionEvent) */ public function incrementItems(array $keyValuePairs) { if (!$this->getOptions()->getWritable()) { return array(); } $this->normalizeKeyValuePairs($keyValuePairs); $args = new ArrayObject(array( 'keyValuePairs' => & $keyValuePairs, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalIncrementItems($args['keyValuePairs']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = array(); return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to increment multiple items. * * @param array $normalizedKeyValuePairs * @return array Associative array of keys and new values * @throws Exception\ExceptionInterface */ protected function internalIncrementItems(array & $normalizedKeyValuePairs) { $result = array(); foreach ($normalizedKeyValuePairs as $normalizedKey => $value) { $newValue = $this->internalIncrementItem($normalizedKey, $value); if ($newValue !== false) { $result[$normalizedKey] = $newValue; } } return $result; } /** * Decrement an item. * * @param string $key * @param int $value * @return int|bool The new value on success, false on failure * @throws Exception\ExceptionInterface * * @triggers decrementItem.pre(PreEvent) * @triggers decrementItem.post(PostEvent) * @triggers decrementItem.exception(ExceptionEvent) */ public function decrementItem($key, $value) { if (!$this->getOptions()->getWritable()) { return false; } $this->normalizeKey($key); $args = new ArrayObject(array( 'key' => & $key, 'value' => & $value, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalDecrementItem($args['key'], $args['value']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to decrement an item. * * @param string $normalizedKey * @param int $value * @return int|bool The new value on success, false on failure * @throws Exception\ExceptionInterface */ protected function internalDecrementItem(& $normalizedKey, & $value) { $success = null; $value = (int) $value; $get = (int) $this->internalGetItem($normalizedKey, $success); $newValue = $get - $value; if ($success) { $this->internalReplaceItem($normalizedKey, $newValue); } else { $this->internalAddItem($normalizedKey, $newValue); } return $newValue; } /** * Decrement multiple items. * * @param array $keyValuePairs * @return array Associative array of keys and new values * @throws Exception\ExceptionInterface * * @triggers incrementItems.pre(PreEvent) * @triggers incrementItems.post(PostEvent) * @triggers incrementItems.exception(ExceptionEvent) */ public function decrementItems(array $keyValuePairs) { if (!$this->getOptions()->getWritable()) { return array(); } $this->normalizeKeyValuePairs($keyValuePairs); $args = new ArrayObject(array( 'keyValuePairs' => & $keyValuePairs, )); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalDecrementItems($args['keyValuePairs']); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = array(); return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to decrement multiple items. * * @param array $normalizedKeyValuePairs * @return array Associative array of keys and new values * @throws Exception\ExceptionInterface */ protected function internalDecrementItems(array & $normalizedKeyValuePairs) { $result = array(); foreach ($normalizedKeyValuePairs as $normalizedKey => $value) { $newValue = $this->decrementItem($normalizedKey, $value); if ($newValue !== false) { $result[$normalizedKey] = $newValue; } } return $result; } /* status */ /** * Get capabilities of this adapter * * @return Capabilities * @triggers getCapabilities.pre(PreEvent) * @triggers getCapabilities.post(PostEvent) * @triggers getCapabilities.exception(ExceptionEvent) */ public function getCapabilities() { $args = new ArrayObject(); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $result = $this->internalGetCapabilities(); return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { $result = false; return $this->triggerException(__FUNCTION__, $args, $result, $e); } } /** * Internal method to get capabilities of this adapter * * @return Capabilities */ protected function internalGetCapabilities() { if ($this->capabilities === null) { $this->capabilityMarker = new stdClass(); $this->capabilities = new Capabilities($this, $this->capabilityMarker); } return $this->capabilities; } /* internal */ /** * Validates and normalizes a key * * @param string $key * @return void * @throws Exception\InvalidArgumentException On an invalid key */ protected function normalizeKey(& $key) { $key = (string) $key; if ($key === '') { throw new Exception\InvalidArgumentException( "An empty key isn't allowed" ); } elseif (($p = $this->getOptions()->getKeyPattern()) && !preg_match($p, $key)) { throw new Exception\InvalidArgumentException( "The key '{$key}' doesn't match agains pattern '{$p}'" ); } } /** * Validates and normalizes multiple keys * * @param array $keys * @return void * @throws Exception\InvalidArgumentException On an invalid key */ protected function normalizeKeys(array & $keys) { if (!$keys) { throw new Exception\InvalidArgumentException( "An empty list of keys isn't allowed" ); } array_walk($keys, array($this, 'normalizeKey')); $keys = array_values(array_unique($keys)); } /** * Validates and normalizes an array of key-value pairs * * @param array $keyValuePairs * @return void * @throws Exception\InvalidArgumentException On an invalid key */ protected function normalizeKeyValuePairs(array & $keyValuePairs) { $normalizedKeyValuePairs = array(); foreach ($keyValuePairs as $key => $value) { $this->normalizeKey($key); $normalizedKeyValuePairs[$key] = $value; } $keyValuePairs = $normalizedKeyValuePairs; } }