diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 3b97b69..cd83023 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -90,11 +90,6 @@ class Config { * would return array('bar' => 'baz'). * If no key is specified, then the entire data array is returned. * - * The configuration system does not retain data types. Every saved value is - * casted to a string. In most cases this is not an issue; however, it can - * cause issues with Booleans, which are casted to "1" (TRUE) or "0" (FALSE). - * In particular, code relying on === or !== will no longer function properly. - * * @see http://php.net/manual/language.operators.comparison.php. * * @return @@ -147,9 +142,6 @@ class Config { * @todo */ public function set($key, $value) { - // Type-cast value into a string. - $value = $this->castValue($value); - // The dot/period is a reserved character; it may appear between keys, but // not within keys. $parts = explode('.', $key); @@ -163,45 +155,6 @@ class Config { } /** - * Casts a saved value to a string. - * - * The configuration system only saves strings or arrays. Any scalar - * non-string value is cast to a string. The one exception is boolean FALSE - * which would normally become '' when cast to a string, but is manually - * cast to '0' here for convenience and consistency. - * - * Any non-scalar value that is not an array (aka objects) gets cast - * to an array. - * - * @param $value - * A value being saved into the configuration system. - * @param $value - * The value cast to a string or array. - */ - public function castValue($value) { - if (is_scalar($value)) { - // Handle special case of FALSE, which should be '0' instead of ''. - if ($value === FALSE) { - $value = '0'; - } - else { - $value = (string) $value; - } - } - else { - // Any non-scalar value must be an array. - if (!is_array($value)) { - $value = (array) $value; - } - // Recurse into any nested keys. - foreach ($value as $key => $nested_value) { - $value[$key] = $this->castValue($nested_value); - } - } - return $value; - } - - /** * Unsets value in this config object. * * @param $key @@ -265,6 +218,17 @@ class Config { } /** + * Helper callback for array_walk_recursive(). + * + * @todo Find a better location for this. + */ + public static function validateValueIsScalar($value, $key) { + if (!is_scalar($value)) { + throw new StorageException(sprintf('Unallowed non-scalar value %s in key %s', $value, $key)); + } + } + + /** * Deletes the configuration object. */ public function delete() { diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php index 8ab64f6..6300bb0 100644 --- a/core/lib/Drupal/Core/Config/DatabaseStorage.php +++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php @@ -112,6 +112,7 @@ class DatabaseStorage implements StorageInterface { * Implements Drupal\Core\Config\StorageInterface::encode(). */ public static function encode($data) { + array_walk_recursive('Config::validateValueIsScalar', $data); return serialize($data); } diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php index 727adef..527772f 100644 --- a/core/lib/Drupal/Core/Config/FileStorage.php +++ b/core/lib/Drupal/Core/Config/FileStorage.php @@ -110,6 +110,7 @@ class FileStorage implements StorageInterface { * @throws Symfony\Component\Yaml\Exception\DumpException */ public static function encode($data) { + array_walk_recursive('Config::validateValueIsScalar', $data); // The level where you switch to inline YAML is set to PHP_INT_MAX to ensure // this does not occur. return Yaml::dump($data, PHP_INT_MAX); diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php index abbd2ae..0411614 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php @@ -40,15 +40,15 @@ class ConfigFileContentTest extends WebTestBase { $nested_value = 'pow'; $array_key = 'array'; $array_value = array( - 'foo' => 'bar', 'biff' => array( 'bang' => 'pow', - ) + ), + 'foo' => 'bar', ); - $casting_array_key = 'casting_array'; - $casting_array_false_value_key = 'casting_array.cast.false'; - $casting_array_value = array( - 'cast' => array( + $typed_value_array_key = 'typed_value_array'; + $typed_value_array_false_value_key = 'typed_value_array.boolean.false'; + $typed_value_array_value = array( + 'boolean' => array( 'false' => FALSE, ) ); @@ -90,7 +90,7 @@ class ConfigFileContentTest extends WebTestBase { $config->set($true_key, TRUE); // Add an array with a nested boolean false that should get cast to 0. - $config->set($casting_array_key, $casting_array_value); + $config->set($typed_value_array_key, $typed_value_array_value); $config->save(); // Verify the database entry exists. @@ -101,16 +101,16 @@ class ConfigFileContentTest extends WebTestBase { $config = config($name); $this->assertEqual($config->getName(), $name); $this->assertTrue($config, 'Config object created.'); - $this->assertEqual($config->get($key), 'bar', t('Top level configuration value found.')); + $this->assertIdentical($config->get($key), 'bar', t('Top level configuration value found.')); // Read nested value - $this->assertEqual($config->get($nested_key), $nested_value, t('Nested configuration value found.')); + $this->assertIdentical($config->get($nested_key), $nested_value, t('Nested configuration value found.')); // Read array - $this->assertEqual($config->get($array_key), $array_value, t('Top level array configuration value found.')); + $this->assertIdentical($config->get($array_key), $array_value); // Read nested array - $this->assertEqual($config->get($nested_array_key), $array_value, t('Nested array configuration value found.')); + $this->assertIdentical($config->get($nested_array_key), $array_value); // Read a top level value that doesn't exist $this->assertNull($config->get('i_dont_exist'), t('Non-existent top level value returned NULL.')); @@ -119,13 +119,13 @@ class ConfigFileContentTest extends WebTestBase { $this->assertNull($config->get('i.dont.exist'), t('Non-existent nested value returned NULL.')); // Read false value - $this->assertEqual($config->get($false_key), '0', t('Boolean FALSE value returned the string \'0\'.')); + $this->assertIdentical($config->get($false_key), FALSE); // Read true value - $this->assertEqual($config->get($true_key), '1', t('Boolean TRUE value returned the string \'1\'.')); + $this->assertIdentical($config->get($true_key), TRUE); // Read false that had been nested in an array value - $this->assertEqual($config->get($casting_array_false_value_key), '0', t('Nested boolean FALSE value returned the string \'0\'.')); + $this->assertIdentical($config->get($typed_value_array_false_value_key), FALSE); // Unset a top level value $config->clear($key); @@ -153,7 +153,7 @@ class ConfigFileContentTest extends WebTestBase { // Verify the database entry exists from a chained save. $db_data = $database_storage->read($chained_name); - $this->assertEqual($db_data, $config->get()); + $this->assertIdentical($db_data, $config->get()); // Get file listing for all files starting with 'foo'. Should return // two elements. diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php index e1d77be..7415fc9 100644 --- a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php +++ b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php @@ -118,6 +118,24 @@ abstract class ConfigStorageTestBase extends WebTestBase { } } + function testDataTypes() { + $name = 'config_test.types'; + $data = $this->storage->read($name); + + // Test typed varaibles. + $type_array = array( + 'boolean' => TRUE, + 'date' => strtotime('2001-12-14t21:59:43.10-05:00'), + 'exp' => 1.2e+34, + 'float' => 3.14159, + 'hex' => 0xC, + 'int' => 99, + 'octal' => 0775, + 'string' => 'string', + ); + $this->assertIdentical($data, $type_array); + } + abstract protected function read($name); abstract protected function insert($name, $data); diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php b/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php index 2e1599f..1992b77 100644 --- a/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php @@ -22,7 +22,7 @@ class DatabaseStorageTest extends ConfigStorageTestBase { } function setUp() { - parent::setUp(); + parent::setUp('config_test'); $this->storage = new DatabaseStorage(); $this->invalidStorage = new DatabaseStorage(array('connection' => 'invalid')); } diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php b/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php index 092687f..19d3963 100644 --- a/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php @@ -23,12 +23,17 @@ class FileStorageTest extends ConfigStorageTestBase { } function setUp() { - parent::setUp(); + parent::setUp('config_test'); $this->storage = new FileStorage(); $this->invalidStorage = new FileStorage(array('directory' => $this->configFileDirectory . '/nonexisting')); // FileStorage::listAll() requires other configuration data to exist. $this->storage->write('system.performance', config('system.performance')->get()); + + // Variable type test requires config_test.types to exist.Doing this + // additionally tests the ability of FileStorage to write typed + // configuration. + $this->storage->write('config_test.types', config('config_test.types')->get()); } protected function read($name) { diff --git a/core/modules/config/tests/config_test/config/config_test.types.yml b/core/modules/config/tests/config_test/config/config_test.types.yml new file mode 100644 index 0000000..1eff295 --- /dev/null +++ b/core/modules/config/tests/config_test/config/config_test.types.yml @@ -0,0 +1,8 @@ +boolean: true +date: 2001-12-14t21:59:43.10-05:00 +exp: 1.2e+34 +float: 3.14159 +hex: 0xC +int: 99 +octal: 0775 +string: string