diff --git a/core/lib/Drupal/Core/KeyValueStore/AbstractStorage.php b/core/lib/Drupal/Core/KeyValueStore/AbstractStorage.php new file mode 100644 index 0000000..9111a84 --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/AbstractStorage.php @@ -0,0 +1,74 @@ +collection = $collection; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getCollectionName(). + */ + public function getCollectionName() { + return $this->collection; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::get(). + */ + public function get($key) { + $values = $this->getMultiple(array($key)); + return reset($values); + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getMultiple(). + */ + abstract public function getMultiple(array $keys); + + /** + * Implements KeyValueStore\Storage\StorageInterface::getAll(). + */ + abstract public function getAll(); + + /** + * Implements KeyValueStore\Storage\StorageInterface::set(). + */ + abstract public function set($key, $value); + + /** + * Implements KeyValueStore\Storage\StorageInterface::setMultiple(). + */ + public function setMultiple(array $data) { + foreach ($data as $key => $value) { + $this->set($key, $value); + } + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::delete(). + */ + public function delete($key) { + $this->deleteMultiple(array($key)); + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::deleteMultiple(). + */ + abstract public function deleteMultiple(array $keys); + +} diff --git a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php new file mode 100644 index 0000000..d1b206c --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php @@ -0,0 +1,90 @@ +table = isset($options['table']) ? $options['table'] : 'keyvalue'; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getMultiple(). + */ + public function getMultiple(array $keys) { + try { + $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE name IN (:keys) AND collection = :collection', array(':keys' => $keys, ':collection' => $this->collection))->fetchAllAssoc('name'); + $values = array(); + foreach ($keys as $key) { + if (isset($result[$key]) && ($value = unserialize($result[$key]->value))) { + $values[$key] = $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 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 KeyValueStore\Storage\StorageInterface::set(). + */ + public function set($key, $value) { + db_merge($this->table) + ->key(array('name' => $key)) + ->fields(array( + 'collection' => $this->collection, + 'value' => serialize($value), + )) + ->execute(); + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::deleteMultiple(). + */ + public function deleteMultiple(array $keys) { + // Delete in chunks when a large array is passed. + do { + db_delete($this->table) + ->condition('name', array_splice($keys, 0, 1000)) + ->condition('collection', $this->collection) + ->execute(); + } + while (count($keys)); + } + +} + diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueStoreInterface.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueStoreInterface.php new file mode 100644 index 0000000..d4d7b6c --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueStoreInterface.php @@ -0,0 +1,98 @@ +collection = $collection; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getCollectionName(). + */ + public function getCollectionName() { + return $this->collection; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::get(). + */ + public function get($key) { + return isset($this->data[$key]) ? $this->data[$key] : FALSE; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getMultiple(). + */ + public function getMultiple(array $keys) { + $results = array(); + // Foreach only needs to traverse $keys once, the PHP implementation + // array_intersect_key(array_flip(array_values($keys)), $this->data); + // would be slower. + foreach ($keys as $key) { + if (isset($this->data[$key])) { + $results[$key] = $this->data[$key]; + } + } + return $results; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::getAll(). + */ + public function getAll() { + return $this->data; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::set(). + */ + public function set($key, $value) { + $this->data[$key] = $value; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::setMultiple(). + */ + public function setMultiple(array $data) { + $this->data = $data + $this->data; + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::delete(). + */ + public function delete($key) { + unset($this->data[$key]); + } + + /** + * Implements KeyValueStore\Storage\StorageInterface::deleteMultiple(). + */ + public function deleteMultiple(array $keys) { + foreach ($keys as $key) { + unset($this->data[$key]); + } + } + +} + diff --git a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php new file mode 100644 index 0000000..9b3f6ad --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php @@ -0,0 +1,21 @@ + 'Database Storage', + 'description' => 'Test the key-value database storage.', + 'group' => 'Key-value store' + ); + } + + protected function setUp() { + parent::setUp(); + module_load_install('system'); + $schema = system_schema(); + db_create_table('keyvalue', $schema['keyvalue']); + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/MemoryStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/MemoryStorageTest.php new file mode 100644 index 0000000..d3db3bc --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/MemoryStorageTest.php @@ -0,0 +1,15 @@ + 'Memory Storage', + 'description' => 'Test the key-value memory storage.', + 'group' => 'Key-value store' + ); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/StorageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/StorageTestBase.php new file mode 100644 index 0000000..8fb38a0 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/StorageTestBase.php @@ -0,0 +1,41 @@ +collection = end($class); + $storageClass = '\\Drupal\\Core\\KeyValueStore\\' . substr($this->collection, 0, -4); + $this->kv = new $storageClass($this->collection); + } + + public function testSetGet() { + $this->assertEqual($this->kv->getCollectionName(), $this->collection); + // get-set test. + $this->kv->set('foo', 'bar'); + $this->assertEqual('bar', $this->kv->get('foo')); + $this->kv->delete('foo'); + $this->assertFalse($this->kv->get('foo')); + + // multiple test. + $values = array('foo' => 'bar', 'baz' => 'qux'); + $this->kv->setMultiple($values); + $result = $this->kv->getMultiple(array('foo', 'baz')); + $this->assertEqual($values, $result); + $result = $this->kv->getAll(); + // $result might not equal to $values, the order is not defined for + // getAll(). + $this->assertEqual(count($result), count($values)); + foreach ($result as $key => $value) { + $this->assertEqual($values[$key], $value); + } + $this->kv->deleteMultiple(array_keys($values)); + $this->assertFalse($this->kv->get('foo')); + $this->assertFalse($this->kv->get('bar')); + $this->assertFalse($this->kv->getMultiple(array('foo', 'baz'))); + } +} diff --git a/core/modules/system/system.install b/core/modules/system/system.install index ac983aa..87b3150 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -549,6 +549,33 @@ function system_schema() { ), 'primary key' => array('name'), ); + $schema['keyvalue'] = array( + 'description' => 'Generic key-value storage table.', + 'fields' => array( + 'name' => array( + 'description' => 'The key.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'collection' => array( + 'description' => 'The collection of the variable.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'value' => array( + 'description' => 'The value.', + 'type' => 'blob', + 'not null' => TRUE, + 'size' => 'big', + 'translatable' => TRUE, + ), + ), + 'primary key' => array('collection', 'name'), + ); $schema['actions'] = array( 'description' => 'Stores action information.',