Issue #1175054 by msonnabaum, chx, sun, tim.plunkett, arlinsandbulte, beejeebus: Add a storage (API) for persistent non-configuration state.
parent
6a064f416a
commit
8337955d48
|
@ -2504,10 +2504,28 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) {
|
||||||
$container
|
$container
|
||||||
->register('config.storage.staging', 'Drupal\Core\Config\FileStorage')
|
->register('config.storage.staging', 'Drupal\Core\Config\FileStorage')
|
||||||
->addArgument(config_get_config_directory(CONFIG_STAGING_DIRECTORY));
|
->addArgument(config_get_config_directory(CONFIG_STAGING_DIRECTORY));
|
||||||
|
$container
|
||||||
|
->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage')
|
||||||
|
->addArgument('state');
|
||||||
}
|
}
|
||||||
return $container;
|
return $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the state storage service.
|
||||||
|
*
|
||||||
|
* Use this to store machine-generated data, local to a specific environment
|
||||||
|
* that does not need deploying and does not need human editing; for example,
|
||||||
|
* the last time cron was run. Data which needs to be edited by humans and
|
||||||
|
* needs to be the same across development, production, etc. environments
|
||||||
|
* (for example, the system maintenance message) should use config() instead.
|
||||||
|
*
|
||||||
|
* @return Drupal\Core\KeyValueStore\KeyValueStoreInterface
|
||||||
|
*/
|
||||||
|
function state() {
|
||||||
|
return drupal_container()->get('state.storage');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the test prefix if this is an internal request from SimpleTest.
|
* Returns the test prefix if this is an internal request from SimpleTest.
|
||||||
*
|
*
|
||||||
|
|
|
@ -311,7 +311,9 @@ function install_begin_request(&$install_state) {
|
||||||
$container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
|
$container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
|
||||||
->addArgument(new Reference('config.storage'))
|
->addArgument(new Reference('config.storage'))
|
||||||
->addArgument(new Reference('dispatcher'));
|
->addArgument(new Reference('dispatcher'));
|
||||||
|
$container
|
||||||
|
->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage')
|
||||||
|
->addArgument('state');
|
||||||
drupal_container($container);
|
drupal_container($container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -451,7 +451,7 @@ function menu_get_item($path = NULL, $router_item = NULL) {
|
||||||
if (!isset($router_items[$path])) {
|
if (!isset($router_items[$path])) {
|
||||||
// Rebuild if we know it's needed, or if the menu masks are missing which
|
// Rebuild if we know it's needed, or if the menu masks are missing which
|
||||||
// occurs rarely, likely due to a race condition of multiple rebuilds.
|
// occurs rarely, likely due to a race condition of multiple rebuilds.
|
||||||
if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) {
|
if (state()->get('menu_rebuild_needed') || !variable_get('menu_masks', array())) {
|
||||||
menu_router_rebuild();
|
menu_router_rebuild();
|
||||||
}
|
}
|
||||||
$original_map = arg(NULL, $path);
|
$original_map = arg(NULL, $path);
|
||||||
|
@ -2666,7 +2666,7 @@ function menu_router_rebuild() {
|
||||||
menu_cache_clear_all();
|
menu_cache_clear_all();
|
||||||
_menu_clear_page_cache();
|
_menu_clear_page_cache();
|
||||||
// Indicate that the menu has been successfully rebuilt.
|
// Indicate that the menu has been successfully rebuilt.
|
||||||
variable_del('menu_rebuild_needed');
|
state()->delete('menu_rebuild_needed');
|
||||||
}
|
}
|
||||||
catch (Exception $e) {
|
catch (Exception $e) {
|
||||||
$transaction->rollback();
|
$transaction->rollback();
|
||||||
|
|
|
@ -130,6 +130,36 @@ function update_prepare_d8_bootstrap() {
|
||||||
update_extra_requirements($requirements);
|
update_extra_requirements($requirements);
|
||||||
|
|
||||||
if ($has_required_schema) {
|
if ($has_required_schema) {
|
||||||
|
if (!db_table_exists('key_value')) {
|
||||||
|
$specs = array(
|
||||||
|
'description' => 'Generic key-value storage table. See state() for an example.',
|
||||||
|
'fields' => array(
|
||||||
|
'collection' => array(
|
||||||
|
'description' => 'A named collection of key and value pairs.',
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 128,
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => '',
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => 'The key of the key-value pair. As KEY is a SQL reserved keyword, name was chosen instead.',
|
||||||
|
'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'),
|
||||||
|
);
|
||||||
|
db_create_table('key_value', $specs);
|
||||||
|
}
|
||||||
// Bootstrap variables so we can update theme while preparing the update
|
// Bootstrap variables so we can update theme while preparing the update
|
||||||
// process.
|
// process.
|
||||||
drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);
|
drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\KeyValueStore\AbstractStorage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\KeyValueStore;
|
||||||
|
|
||||||
|
abstract class AbstractStorage implements KeyValueStoreInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the collection holding key and value pairs.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::__construct().
|
||||||
|
*/
|
||||||
|
public function __construct($collection, array $options = array()) {
|
||||||
|
$this->collection = $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getCollectionName().
|
||||||
|
*/
|
||||||
|
public function getCollectionName() {
|
||||||
|
return $this->collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::get().
|
||||||
|
*/
|
||||||
|
public function get($key) {
|
||||||
|
$values = $this->getMultiple(array($key));
|
||||||
|
return reset($values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::setMultiple().
|
||||||
|
*/
|
||||||
|
public function setMultiple(array $data) {
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$this->set($key, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::delete().
|
||||||
|
*/
|
||||||
|
public function delete($key) {
|
||||||
|
$this->deleteMultiple(array($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\KeyValueStore\DatabaseStorage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\KeyValueStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a default key/value store implementation.
|
||||||
|
*
|
||||||
|
* This is Drupal's default key/value store implementation. It uses the database
|
||||||
|
* to store key/value data.
|
||||||
|
*/
|
||||||
|
class DatabaseStorage extends AbstractStorage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides Drupal\Core\KeyValueStore\AbstractStorage::__construct().
|
||||||
|
*
|
||||||
|
* @param string $collection
|
||||||
|
* The name of the collection holding key and value pairs.
|
||||||
|
* @param array $options
|
||||||
|
* An associative array of options for the key/value storage collection.
|
||||||
|
* Keys used:
|
||||||
|
* - table. The name of the SQL table to use, defaults to key_value.
|
||||||
|
*/
|
||||||
|
public function __construct($collection, array $options = array()) {
|
||||||
|
parent::__construct($collection, $options);
|
||||||
|
$this->table = isset($options['table']) ? $options['table'] : 'key_value';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getMultiple().
|
||||||
|
*/
|
||||||
|
public function getMultiple(array $keys) {
|
||||||
|
$values = array();
|
||||||
|
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');
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (isset($result[$key])) {
|
||||||
|
$values[$key] = unserialize($result[$key]->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
// @todo: Perhaps if the database is never going to be available,
|
||||||
|
// key/value requests should return FALSE in order to allow exception
|
||||||
|
// handling to occur but for now, keep it an array, always.
|
||||||
|
}
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::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\Core\KeyValueStore\KeyValueStoreInterface::set().
|
||||||
|
*/
|
||||||
|
public function set($key, $value) {
|
||||||
|
db_merge($this->table)
|
||||||
|
->key(array(
|
||||||
|
'name' => $key,
|
||||||
|
'collection' => $this->collection,
|
||||||
|
))
|
||||||
|
->fields(array('value' => serialize($value)))
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\KeyValueStore\KeyValueStoreInterface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\KeyValueStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the interface for key/value store implementations.
|
||||||
|
*/
|
||||||
|
interface KeyValueStoreInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new key/value collection.
|
||||||
|
*
|
||||||
|
* @param string $collection
|
||||||
|
* The name of the collection holding key and value pairs.
|
||||||
|
* @param array $options
|
||||||
|
* An associative array of options for the key/value storage collection.
|
||||||
|
*/
|
||||||
|
public function __construct($collection, array $options = array());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this collection.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* The name of this collection.
|
||||||
|
*/
|
||||||
|
public function getCollectionName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stored value for a given key.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* The key of the data to retrieve.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* The stored value, or FALSE if no value exists.
|
||||||
|
*/
|
||||||
|
public function get($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stored key/value pairs for a given set of keys.
|
||||||
|
*
|
||||||
|
* @param array $keys
|
||||||
|
* A list of keys to retrieve.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An associative array of items successfully returned, indexed by key.
|
||||||
|
*
|
||||||
|
* @todo What's returned for non-existing keys?
|
||||||
|
*/
|
||||||
|
public function getMultiple(array $keys);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all stored key/value pairs in the collection.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An associative array containing all stored items in the collection.
|
||||||
|
*/
|
||||||
|
public function getAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a value for a given key.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* The key of the data to store.
|
||||||
|
* @param mixed $value
|
||||||
|
* The data to store.
|
||||||
|
*/
|
||||||
|
public function set($key, $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves key/value pairs.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* An associative array of key/value pairs.
|
||||||
|
*/
|
||||||
|
public function setMultiple(array $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an item from the key/value store.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* The item name to delete.
|
||||||
|
*/
|
||||||
|
public function delete($key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes multiple items from the key/value store.
|
||||||
|
*
|
||||||
|
* @param array $keys
|
||||||
|
* A list of item names to delete.
|
||||||
|
*/
|
||||||
|
public function deleteMultiple(array $keys);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\Core\KeyValueStore\MemoryStorage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\KeyValueStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a default key/value store implementation.
|
||||||
|
*
|
||||||
|
* For performance reasons, this implementation is not based on AbstractStorage.
|
||||||
|
*/
|
||||||
|
class MemoryStorage implements KeyValueStoreInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual storage of key-value pairs.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $data = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::__construct().
|
||||||
|
*/
|
||||||
|
public function __construct($collection, array $options = array()) {
|
||||||
|
$this->collection = $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getCollectionName().
|
||||||
|
*/
|
||||||
|
public function getCollectionName() {
|
||||||
|
return $this->collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::get().
|
||||||
|
*/
|
||||||
|
public function get($key) {
|
||||||
|
return array_key_exists($key, $this->data) ? $this->data[$key] : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getMultiple().
|
||||||
|
*/
|
||||||
|
public function getMultiple(array $keys) {
|
||||||
|
return array_intersect_key($this->data, array_flip($keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getAll().
|
||||||
|
*/
|
||||||
|
public function getAll() {
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::set().
|
||||||
|
*/
|
||||||
|
public function set($key, $value) {
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::setMultiple().
|
||||||
|
*/
|
||||||
|
public function setMultiple(array $data) {
|
||||||
|
$this->data = $data + $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::delete().
|
||||||
|
*/
|
||||||
|
public function delete($key) {
|
||||||
|
unset($this->data[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::deleteMultiple().
|
||||||
|
*/
|
||||||
|
public function deleteMultiple(array $keys) {
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
unset($this->data[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1553,7 +1553,7 @@ function hook_field_available_languages_alter(&$langcodes, $context) {
|
||||||
function hook_field_attach_create_bundle($entity_type, $bundle) {
|
function hook_field_attach_create_bundle($entity_type, $bundle) {
|
||||||
// When a new bundle is created, the menu needs to be rebuilt to add the
|
// When a new bundle is created, the menu needs to be rebuilt to add the
|
||||||
// Field UI menu item tabs.
|
// Field UI menu item tabs.
|
||||||
variable_set('menu_rebuild_needed', TRUE);
|
state()->set('menu_rebuild_needed', TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -309,7 +309,7 @@ function field_ui_element_info() {
|
||||||
function field_ui_field_attach_create_bundle($entity_type, $bundle) {
|
function field_ui_field_attach_create_bundle($entity_type, $bundle) {
|
||||||
// When a new bundle is created, the menu needs to be rebuilt to add our
|
// When a new bundle is created, the menu needs to be rebuilt to add our
|
||||||
// menu item tabs.
|
// menu item tabs.
|
||||||
variable_set('menu_rebuild_needed', TRUE);
|
state()->set('menu_rebuild_needed', TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -253,7 +253,7 @@ function image_form_system_file_system_settings_alter(&$form, &$form_state) {
|
||||||
*/
|
*/
|
||||||
function image_system_file_system_settings_submit($form, &$form_state) {
|
function image_system_file_system_settings_submit($form, &$form_state) {
|
||||||
if ($form['file_public_path']['#default_value'] !== $form_state['values']['file_public_path']) {
|
if ($form['file_public_path']['#default_value'] !== $form_state['values']['file_public_path']) {
|
||||||
variable_set('menu_rebuild_needed', TRUE);
|
state()->set('menu_rebuild_needed', TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,7 +175,7 @@ function search_admin_settings_submit($form, &$form_state) {
|
||||||
if ($config->get('active_modules') != $new_modules) {
|
if ($config->get('active_modules') != $new_modules) {
|
||||||
$config->set('active_modules', $new_modules);
|
$config->set('active_modules', $new_modules);
|
||||||
drupal_set_message(t('The active search modules have been changed.'));
|
drupal_set_message(t('The active search modules have been changed.'));
|
||||||
variable_set('menu_rebuild_needed', TRUE);
|
state()->set('menu_rebuild_needed', TRUE);
|
||||||
}
|
}
|
||||||
$config->save();
|
$config->save();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\system\Tests\KeyValueStore\DatabaseStorageTest.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\system\Tests\KeyValueStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the key-value database storage.
|
||||||
|
*/
|
||||||
|
class DatabaseStorageTest extends StorageTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the class to test.
|
||||||
|
*
|
||||||
|
* The tests themselves are in StorageTestBase and use this class.
|
||||||
|
*/
|
||||||
|
protected $storageClass = 'Drupal\Core\KeyValueStore\DatabaseStorage';
|
||||||
|
|
||||||
|
public static function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'Database storage',
|
||||||
|
'description' => 'Tests the key-value database storage.',
|
||||||
|
'group' => 'Key-value store',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
module_load_install('system');
|
||||||
|
$schema = system_schema();
|
||||||
|
db_create_table('key_value', $schema['key_value']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown() {
|
||||||
|
db_drop_table('key_value');
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\system\Tests\KeyValueStore\MemoryStorageTest.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\system\Tests\KeyValueStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the key-value memory storage.
|
||||||
|
*/
|
||||||
|
class MemoryStorageTest extends StorageTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the class to test.
|
||||||
|
*
|
||||||
|
* The tests themselves are in StorageTestBase and use this class.
|
||||||
|
*/
|
||||||
|
protected $storageClass = 'Drupal\Core\KeyValueStore\MemoryStorage';
|
||||||
|
|
||||||
|
public static function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'Memory storage',
|
||||||
|
'description' => 'Tests the key-value memory storage.',
|
||||||
|
'group' => 'Key-value store',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains Drupal\system\Tests\KeyValueStore\StorageTestBase.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\system\Tests\KeyValueStore;
|
||||||
|
|
||||||
|
use Drupal\simpletest\UnitTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for testing key-value storages.
|
||||||
|
*/
|
||||||
|
abstract class StorageTestBase extends UnitTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fully qualified class name of the key-value storage to test.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $storageClass;
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->collection1 = 'first';
|
||||||
|
$this->collection2 = 'second';
|
||||||
|
|
||||||
|
$this->store1 = new $this->storageClass($this->collection1);
|
||||||
|
$this->store2 = new $this->storageClass($this->collection2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests CRUD operations.
|
||||||
|
*/
|
||||||
|
public function testCRUD() {
|
||||||
|
// Verify that each store returns its own collection name.
|
||||||
|
$this->assertEqual($this->store1->getCollectionName(), $this->collection1);
|
||||||
|
$this->assertEqual($this->store2->getCollectionName(), $this->collection2);
|
||||||
|
|
||||||
|
// Verify that an item can be stored.
|
||||||
|
$this->store1->set('foo', 'bar');
|
||||||
|
$this->assertEqual('bar', $this->store1->get('foo'));
|
||||||
|
// Verify that the other collection is not affected.
|
||||||
|
$this->assertFalse($this->store2->get('foo'));
|
||||||
|
|
||||||
|
// Verify that an item can be updated.
|
||||||
|
$this->store1->set('foo', 'baz');
|
||||||
|
$this->assertEqual('baz', $this->store1->get('foo'));
|
||||||
|
// Verify that the other collection is still not affected.
|
||||||
|
$this->assertFalse($this->store2->get('foo'));
|
||||||
|
|
||||||
|
// Verify that a collection/name pair is unique.
|
||||||
|
$this->store2->set('foo', 'other');
|
||||||
|
$this->assertEqual('baz', $this->store1->get('foo'));
|
||||||
|
$this->assertEqual('other', $this->store2->get('foo'));
|
||||||
|
|
||||||
|
// Verify that an item can be deleted.
|
||||||
|
$this->store1->delete('foo');
|
||||||
|
$this->assertFalse($this->store1->get('foo'));
|
||||||
|
|
||||||
|
// Verify that the other collection is not affected.
|
||||||
|
$this->assertEqual('other', $this->store2->get('foo'));
|
||||||
|
$this->store2->delete('foo');
|
||||||
|
$this->assertFalse($this->store2->get('foo'));
|
||||||
|
|
||||||
|
// Verify that multiple items can be stored.
|
||||||
|
$values = array(
|
||||||
|
'foo' => 'bar',
|
||||||
|
'baz' => 'qux',
|
||||||
|
);
|
||||||
|
$this->store1->setMultiple($values);
|
||||||
|
|
||||||
|
// Verify that multiple items can be retrieved.
|
||||||
|
$result = $this->store1->getMultiple(array('foo', 'baz'));
|
||||||
|
$this->assertEqual($values, $result);
|
||||||
|
|
||||||
|
// Verify that the other collection was not affected.
|
||||||
|
$this->assertFalse($this->store2->get('foo'));
|
||||||
|
$this->assertFalse($this->store2->get('baz'));
|
||||||
|
|
||||||
|
// Verify that all items in a collection can be retrieved.
|
||||||
|
// Ensure that an item with the same name exists in the other collection.
|
||||||
|
$this->store2->set('foo', 'other');
|
||||||
|
$result = $this->store1->getAll();
|
||||||
|
// Not using assertIdentical(), since the order is not defined for getAll().
|
||||||
|
$this->assertEqual(count($result), count($values));
|
||||||
|
foreach ($result as $key => $value) {
|
||||||
|
$this->assertEqual($values[$key], $value);
|
||||||
|
}
|
||||||
|
// Verify that all items in the other collection are different.
|
||||||
|
$result = $this->store2->getAll();
|
||||||
|
$this->assertEqual($result, array('foo' => 'other'));
|
||||||
|
|
||||||
|
// Verify that multiple items can be deleted.
|
||||||
|
$this->store1->deleteMultiple(array_keys($values));
|
||||||
|
$this->assertFalse($this->store1->get('foo'));
|
||||||
|
$this->assertFalse($this->store1->get('bar'));
|
||||||
|
$this->assertFalse($this->store1->getMultiple(array('foo', 'baz')));
|
||||||
|
// Verify that the item in the other collection still exists.
|
||||||
|
$this->assertEqual('other', $this->store2->get('foo'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ class RebuildTest extends WebTestBase {
|
||||||
|
|
||||||
// Now we enable the rebuild variable and send a request to rebuild the menu
|
// Now we enable the rebuild variable and send a request to rebuild the menu
|
||||||
// item. Now 'admin' should exist.
|
// item. Now 'admin' should exist.
|
||||||
variable_set('menu_rebuild_needed', TRUE);
|
state()->set('menu_rebuild_needed', TRUE);
|
||||||
// The request should trigger the rebuild.
|
// The request should trigger the rebuild.
|
||||||
$this->drupalGet('<front>');
|
$this->drupalGet('<front>');
|
||||||
$admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
|
$admin_exists = db_query('SELECT path from {menu_router} WHERE path = :path', array(':path' => 'admin'))->fetchField();
|
||||||
|
|
|
@ -868,6 +868,34 @@ function system_schema() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$schema['key_value'] = array(
|
||||||
|
'description' => 'Generic key-value storage table. See state() for an example.',
|
||||||
|
'fields' => array(
|
||||||
|
'collection' => array(
|
||||||
|
'description' => 'A named collection of key and value pairs.',
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 128,
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => '',
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => 'The key of the key-value pair. As KEY is a SQL reserved keyword, name was chosen instead.',
|
||||||
|
'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['menu_router'] = array(
|
$schema['menu_router'] = array(
|
||||||
'description' => 'Maps paths to various callbacks (access, page and title)',
|
'description' => 'Maps paths to various callbacks (access, page and title)',
|
||||||
'fields' => array(
|
'fields' => array(
|
||||||
|
|
Loading…
Reference in New Issue