diff --git a/core/lib/Drupal/Component/KeyValueStore/KeyValueStore.php b/core/lib/Drupal/Component/KeyValueStore/KeyValueStore.php new file mode 100644 index 0000000..edd88a4 --- /dev/null +++ b/core/lib/Drupal/Component/KeyValueStore/KeyValueStore.php @@ -0,0 +1,92 @@ +collection = $storage->getCollectionName(); + $this->storage = $storage; + } + + /** + * Returns data from the key/value store. + * + * @param $key + * The key of the data to retrieve. + * + * @return + * The value or FALSE on failure. + */ + function get($key) { + return $this->storage->get($key); + } + + /** + * Returns data from the key/value store when given an array of keys. + * + * @param $keys + * An array of keys for the data to retrieve. + * + * @return + * An array of the items successfully returned, indexed by key. + */ + function getMultiple($keys) { + return $this->storage->getMultiple($keys); + } + + function getAll() { + return $this->storage->getAll(); + } + + /** + * Stores data in the key/value store. + * + * @param $key + * The key of the data to store. + * @param $value + * The data to store. + */ + function set($key, $value) { + return $this->storage->set($key, $value); + } + + /** + * Stores data in the key/value store. + * + * @param $data + * An array of key/value pairs. + */ + function setMultiple($data) { + return $this->storage->setMultiple($data); + } + + /** + * Deletes an item. + * + * @param $key + * The key to delete. + */ + function delete($key) { + return $this->storage->delete($key); + } + + /** + * Deletes multiple items from the key/value store. + * + * @param $keys + * An array of $keys to delete. + */ + function deleteMultiple(array $keys) { + return $this->storage->deleteMultiple($keys); + } +} diff --git a/core/lib/Drupal/Component/KeyValueStore/KeyValueStoreFactory.php b/core/lib/Drupal/Component/KeyValueStore/KeyValueStoreFactory.php new file mode 100644 index 0000000..a3251e6 --- /dev/null +++ b/core/lib/Drupal/Component/KeyValueStore/KeyValueStoreFactory.php @@ -0,0 +1,88 @@ + 'Drupal\Component\KeyValueStore\Storage\MemcachedStorage', + * 'cache' => 'Drupal\Component\KeyValueStore\Storage\RedisStorage', + * 'default' => 'Drupal\Component\KeyValueStore\Storage\SqlStorage', + * ) + * @endcode + * + * @var array + */ + protected $storageInfo; + + /** + * Instantiated key/value stores, keyed by collection name. + * + * @var array + */ + protected $instances; + + /** + * Constructs the key/value store factory. + * + * @param array $storage_info + * An associative array declaring which storage class to use for which + * collection. + */ + public function __construct(array $storage_info) { + $this->storageInfo = $storage_info; + } + + /** + * Returns the key/value store for a given collection. + * + * @param string $collection + * The name of the key/value collection store instance to return. + * @param array $options + * (optional) The options to pass to the key/value store. Only used on + * initial instantiation. + * + * @return Drupal\Component\KeyValueStore\Storage\StorageInterface + * The key/value store instance. + */ + public function get($collection, $options = array()) { + if (!isset($this->instances[$collection])) { + // Anything up to the first dot treated as the collection base. + if ($pos = strpos($collection, '.')) { + $collection_base = substr($collection, 0, $pos); + } + else { + $collection_base = $collection; + } + + if (isset($this->storageInfo[$collection_base])) { + $class = $this->storageInfo[$collection_base]; + } + else { + $class = $this->storageInfo['default']; + } + $this->instances[$collection] = new $class($collection, $options); + } + return $this->instances[$collection]; + } + +} diff --git a/core/lib/Drupal/Component/KeyValueStore/LICENSE.txt b/core/lib/Drupal/Component/KeyValueStore/LICENSE.txt new file mode 100644 index 0000000..95959d3 --- /dev/null +++ b/core/lib/Drupal/Component/KeyValueStore/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2012 Mark Sonnabaum + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/Drupal/Component/KeyValueStore/Storage/MemoryStorage.php b/core/lib/Drupal/Component/KeyValueStore/Storage/MemoryStorage.php new file mode 100644 index 0000000..6b33b77 --- /dev/null +++ b/core/lib/Drupal/Component/KeyValueStore/Storage/MemoryStorage.php @@ -0,0 +1,92 @@ +collection = $collection; + $this->data = array(); + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::getCollectionName(). + */ + public function getCollectionName() { + return $this->collection; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::get(). + */ + function get($key) { + return isset($this->data[$key]) ? $this->data[$key] : FALSE; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::getMultiple(). + */ + public function getMultiple(array $keys) { + $results = array(); + foreach ($keys as $key) { + $results[$key] = $this->data[$key]; + } + return $results; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::getAll(). + */ + public function getAll() { + return $this->data; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::set(). + */ + public function set($key, $value) { + return $this->data[$key] = $value; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::setMultiple(). + */ + public function setMultiple(array $data) { + $this->data = $data + $this->data; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::delete(). + */ + public function delete($key) { + unset($this->data[$key]); + return TRUE; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::deleteMultiple(). + */ + public function deleteMultiple(array $keys) { + foreach ($keys as $key) { + unset($this->data[$key]); + } + return TRUE; + } +} + diff --git a/core/lib/Drupal/Component/KeyValueStore/Storage/SqlStorage.php b/core/lib/Drupal/Component/KeyValueStore/Storage/SqlStorage.php new file mode 100644 index 0000000..f39ad11 --- /dev/null +++ b/core/lib/Drupal/Component/KeyValueStore/Storage/SqlStorage.php @@ -0,0 +1,133 @@ +collection = $collection; + $this->table = isset($options['table']) ? $options['table'] : 'keyvalue'; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::getCollectionName(). + */ + public function getCollectionName() { + return $this->collection; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::get(). + */ + public function get($key) { + $keys = array($key); + $values = $this->getMultiple($keys); + return reset($values); + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::getMultiple(). + */ + public function getMultiple(array $keys) { + try { + $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE collection = :collection AND name IN (:keys)', array( + ':collection' => $this->collection, + ':keys' => $keys + )); + $values = array(); + + foreach ($result as $item) { + if ($item) { + $values[$item->name] = unserialize($item->value); + } + } + return $values; + } + catch (\Exception $e) { + // If the database is never going to be available, key/value requests should + // return FALSE in order to allow exception handling to occur. + return array(); + } + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::getAll(). + */ + public function getAll() { + $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE collection = :collection', array(':collection' => $this->collection)); + $values = array(); + + foreach ($result as $item) { + if ($item) { + $values[$item->name] = unserialize($item->value); + } + } + return $values; + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::set(). + */ + public function set($key, $value) { + db_merge($this->table) + ->key(array( + 'collection' => $this->collection, + 'name' => $key, + )) + ->fields(array('value' => serialize($value))) + ->execute(); + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::setMultiple(). + */ + public function setMultiple(array $data) { + foreach ($data as $key => $value) { + $this->set($key, $value); + } + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::delete(). + */ + public function delete($key) { + db_delete($this->table) + ->condition('collection', $this->collection) + ->condition('name', $key) + ->execute(); + } + + /** + * Implements Drupal\Component\KeyValueStore\Storage\StorageInterface::deleteMultiple(). + */ + public function deleteMultiple(array $keys) { + // Delete in chunks when a large array is passed. + do { + db_delete($this->table) + ->condition('collection', $this->collection) + ->condition('name', array_splice($keys, 0, 1000), 'IN') + ->execute(); + } + while (count($keys)); + } +} + diff --git a/core/lib/Drupal/Component/KeyValueStore/Storage/StorageInterface.php b/core/lib/Drupal/Component/KeyValueStore/Storage/StorageInterface.php new file mode 100644 index 0000000..73e613d --- /dev/null +++ b/core/lib/Drupal/Component/KeyValueStore/Storage/StorageInterface.php @@ -0,0 +1,97 @@ +register(LANGUAGE_TYPE_CONTENT, 'Drupal\\Core\\Language\\Language'); + // Register the key/value store service. + // @todo Define and register the options for each storage... somehow. + $this->setParameter('keyvalue.storage.info', array( + 'default' => 'Drupal\Component\KeyValueStore\Storage\SqlStorage', + )); + $this->register('keyvaluestore.factory', 'Drupal\Component\KeyValueStore\KeyValueStoreFactory') + ->addArgument('%keyvalue.storage.info%'); + + // Register configuration object factory. + $this->register('config.factory', 'Drupal\Core\Config\ConfigFactory') + ->addArgument(new Reference('config.storage.dispatcher')); + // Register configuration storage dispatcher. $this->setParameter('config.storage.info', array( 'Drupal\Core\Config\DatabaseStorage' => array( diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 96c02c3..16907f2 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -549,6 +549,37 @@ function system_schema() { 'primary key' => array('name'), ); + $schema['keyvalue'] = array( + 'description' => 'Stores key/value pairs by collection.', + 'fields' => array( + 'collection' => array( + 'description' => 'The name of the collection.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'name' => array( + 'description' => 'The name of the key.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'value' => array( + 'description' => 'The value of the variable.', + 'type' => 'blob', + 'not null' => TRUE, + 'size' => 'big', + ), + ), + 'primary key' => array('collection', 'name'), + 'indexes' => array( + 'collection' => array('collection'), + 'name' => array('name'), + ), + ); + $schema['actions'] = array( 'description' => 'Stores action information.', 'fields' => array(