Commit 8b3e463b authored by catch's avatar catch
Browse files

Issue #1653026 by damiankloip, alexpott, sun, tim.plunkett, dawehner, mtift:...

Issue #1653026 by damiankloip, alexpott, sun, tim.plunkett, dawehner, mtift: Fixed All configuration values are stored as strings.
parent b8073ec2
...@@ -208,13 +208,6 @@ public function isNew() { ...@@ -208,13 +208,6 @@ public function isNew() {
* would return array('bar' => 'baz'). * would return array('bar' => 'baz').
* If no key is specified, then the entire data array is returned. * 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 mixed * @return mixed
* The data that was requested. * The data that was requested.
*/ */
...@@ -338,8 +331,6 @@ public function set($key, $value) { ...@@ -338,8 +331,6 @@ public function set($key, $value) {
if (!$this->isLoaded) { if (!$this->isLoaded) {
$this->load(); $this->load();
} }
// Type-cast value into a string.
$value = $this->castValue($value);
// The dot/period is a reserved character; it may appear between keys, but // The dot/period is a reserved character; it may appear between keys, but
// not within keys. // not within keys.
...@@ -354,46 +345,6 @@ public function set($key, $value) { ...@@ -354,46 +345,6 @@ public function set($key, $value) {
return $this; return $this;
} }
/**
* 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 mixed $value
* A value being saved into the configuration system.
*
* @return string
* The value cast to a string or array.
*/
public function castValue($value) {
if (is_scalar($value) || $value === NULL) {
// 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. * Unsets value in this config object.
* *
......
...@@ -175,8 +175,9 @@ protected function getParser() { ...@@ -175,8 +175,9 @@ protected function getParser() {
*/ */
public function encode($data) { public function encode($data) {
// The level where you switch to inline YAML is set to PHP_INT_MAX to ensure // The level where you switch to inline YAML is set to PHP_INT_MAX to ensure
// this does not occur. // this does not occur. Also set the exceptionOnInvalidType parameter to
return $this->getDumper()->dump($data, PHP_INT_MAX); // TRUE, so exceptions are thrown for an invalid data type.
return $this->getDumper()->dump($data, PHP_INT_MAX, 0, TRUE);
} }
/** /**
......
...@@ -91,18 +91,18 @@ protected function createTests() { ...@@ -91,18 +91,18 @@ protected function createTests() {
// Ensure that default values are filled in. // Ensure that default values are filled in.
$expected_properties = array( $expected_properties = array(
'id' => 'stark.test_block', 'id' => 'stark.test_block',
'weight' => '', 'weight' => NULL,
'status' => '1', 'status' => TRUE,
'langcode' => language_default()->id, 'langcode' => language_default()->id,
'region' => '-1', 'region' => -1,
'plugin' => 'test_html_id', 'plugin' => 'test_html_id',
'settings' => array( 'settings' => array(
'cache' => '1', 'cache' => 1,
'label' => '', 'label' => '',
'module' => 'block_test', 'module' => 'block_test',
'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE, 'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE,
), ),
'visibility' => '', 'visibility' => NULL,
); );
$this->assertIdentical($actual_properties, $expected_properties, 'The block properties are exported correctly.'); $this->assertIdentical($actual_properties, $expected_properties, 'The block properties are exported correctly.');
......
...@@ -163,10 +163,10 @@ function testAdmin() { ...@@ -163,10 +163,10 @@ function testAdmin() {
$this->drupalGet('admin/config/content/formats/manage/filtered_html'); $this->drupalGet('admin/config/content/formats/manage/filtered_html');
$ultra_llama_mode_checkbox = $this->xpath('//input[@type="checkbox" and @name="editor[settings][plugins][llama_contextual_and_button][ultra_llama_mode]" and @checked="checked"]'); $ultra_llama_mode_checkbox = $this->xpath('//input[@type="checkbox" and @name="editor[settings][plugins][llama_contextual_and_button][ultra_llama_mode]" and @checked="checked"]');
$this->assertTrue(count($ultra_llama_mode_checkbox) === 1, 'The "Ultra llama mode" checkbox exists and is checked.'); $this->assertTrue(count($ultra_llama_mode_checkbox) === 1, 'The "Ultra llama mode" checkbox exists and is checked.');
$expected_settings['plugins']['llama_contextual_and_button']['ultra_llama_mode'] = '1'; $expected_settings['plugins']['llama_contextual_and_button']['ultra_llama_mode'] = 1;
$editor = entity_load('editor', 'filtered_html'); $editor = entity_load('editor', 'filtered_html');
$this->assertTrue($editor instanceof Editor, 'An Editor config entity exists.'); $this->assertTrue($editor instanceof Editor, 'An Editor config entity exists.');
$this->assertIdentical($expected_settings, $editor->settings, 'The Editor config entity has the correct settings.'); $this->assertIdentical($expected_settings, $editor->settings);
} }
} }
...@@ -41,13 +41,13 @@ function setUp() { ...@@ -41,13 +41,13 @@ function setUp() {
$filter_format = $filter_format_storage_controller->create(array( $filter_format = $filter_format_storage_controller->create(array(
'format' => 'basic_html', 'format' => 'basic_html',
'name' => 'Basic HTML', 'name' => 'Basic HTML',
'status' => '1', 'status' => TRUE,
'roles' => array('authenticated'), 'roles' => array('authenticated'),
), 'filter_format'); ), 'filter_format');
$filter_format->setFilterConfig('filter_html', array( $filter_format->setFilterConfig('filter_html', array(
'module' => 'filter', 'module' => 'filter',
'status' => '1', 'status' => TRUE,
'settings' => array( 'settings' => array(
'allowed_html' => '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h4> <h5> <h6> <p> <span> <img>', 'allowed_html' => '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h4> <h5> <h6> <p> <span> <img>',
), ),
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
use Drupal\Core\Config\ConfigNameException; use Drupal\Core\Config\ConfigNameException;
use Drupal\simpletest\DrupalUnitTestBase; use Drupal\simpletest\DrupalUnitTestBase;
use Drupal\Core\Config\FileStorage;
/** /**
* Tests CRUD operations on configuration objects. * Tests CRUD operations on configuration objects.
...@@ -183,5 +184,56 @@ function testNameValidation() { ...@@ -183,5 +184,56 @@ function testNameValidation() {
} }
} }
}
/**
* Tests data type handling.
*/
public function testDataTypes() {
\Drupal::moduleHandler()->install(array('config_test'));
$storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
$name = 'config_test.types';
$config = $this->container->get('config.factory')->get($name);
$original_content = file_get_contents($storage->getFilePath($name));
$this->verbose('<pre>' . $original_content . "\n" . var_export($storage->read($name), TRUE));
// Verify variable data types are intact.
$data = array(
'array' => array(),
'boolean' => TRUE,
'exp' => 1.2e+34,
'float' => 3.14159,
'hex' => 0xC,
'int' => 99,
'octal' => 0775,
'string' => 'string',
'string_int' => '1',
);
$this->assertIdentical($config->get(), $data);
// Re-set each key using Config::set().
foreach($data as $key => $value) {
$config->set($key, $value);
}
$config->save();
$this->assertIdentical($config->get(), $data);
// Assert the data against the file storage.
$this->assertIdentical($storage->read($name), $data);
$this->verbose('<pre>' . file_get_contents($storage->getFilePath($name)) . var_export($storage->read($name), TRUE));
// Set data using config::setData().
$config->setData($data)->save();
$this->assertIdentical($config->get(), $data);
$this->assertIdentical($storage->read($name), $data);
try {
$config->set('stream', fopen(__FILE__, 'r'))->save();
$this->fail('No Exception thrown upon saving invalid data type.');
}
catch (\Exception $e) {
$this->pass(format_string('%class thrown upon saving invalid data type.', array(
'%class' => get_class($e),
)));
}
}
}
...@@ -146,8 +146,7 @@ function testCRUD() { ...@@ -146,8 +146,7 @@ function testCRUD() {
// Verify that the entity was overwritten. // Verify that the entity was overwritten.
$same_id = entity_load('config_test', $config_test->id()); $same_id = entity_load('config_test', $config_test->id());
$this->assertIdentical($same_id->id(), $config_test->id()); $this->assertIdentical($same_id->id(), $config_test->id());
// Note: Reloading loads from FileStorage, and FileStorage enforces strings. $this->assertIdentical($same_id->label(), NULL);
$this->assertIdentical($same_id->label(), '');
$this->assertNotEqual($same_id->uuid(), $config_test->uuid()); $this->assertNotEqual($same_id->uuid(), $config_test->uuid());
// Delete the overridden entity first. // Delete the overridden entity first.
......
...@@ -123,7 +123,7 @@ function testReadWriteConfig() { ...@@ -123,7 +123,7 @@ function testReadWriteConfig() {
$this->assertEqual($config->get($true_key), '1', format_string("Boolean TRUE value returned the string '1'.")); $this->assertEqual($config->get($true_key), '1', format_string("Boolean TRUE value returned the string '1'."));
// Read null value. // Read null value.
$this->assertIdentical($config->get('null'), ''); $this->assertIdentical($config->get('null'), NULL);
// Read false that had been nested in an array value // Read false that had been nested in an array value
$this->assertEqual($config->get($casting_array_false_value_key), '0', format_string("Nested boolean FALSE value returned the string '0'.")); $this->assertEqual($config->get($casting_array_false_value_key), '0', format_string("Nested boolean FALSE value returned the string '0'."));
......
...@@ -55,9 +55,9 @@ function testImport() { ...@@ -55,9 +55,9 @@ function testImport() {
'id' => 'new', 'id' => 'new',
'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651', 'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
'label' => 'New', 'label' => 'New',
'weight' => '0', 'weight' => 0,
'style' => '', 'style' => '',
'status' => '1', 'status' => TRUE,
'langcode' => language_default()->id, 'langcode' => language_default()->id,
'protected_property' => '', 'protected_property' => '',
); );
......
...@@ -147,9 +147,9 @@ function testNew() { ...@@ -147,9 +147,9 @@ function testNew() {
'id' => 'new', 'id' => 'new',
'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651', 'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
'label' => 'New', 'label' => 'New',
'weight' => '0', 'weight' => 0,
'style' => '', 'style' => '',
'status' => '1', 'status' => TRUE,
'langcode' => language_default()->id, 'langcode' => language_default()->id,
'protected_property' => '', 'protected_property' => '',
); );
......
...@@ -161,6 +161,30 @@ function testCRUD() { ...@@ -161,6 +161,30 @@ function testCRUD() {
} }
/**
* Tests storage controller writing and reading data preserving data type.
*/
function testDataTypes() {
$name = 'config_test.types';
$data = array(
'array' => array(),
'boolean' => TRUE,
'exp' => 1.2e+34,
'float' => 3.14159,
'hex' => 0xC,
'int' => 99,
'octal' => 0775,
'string' => 'string',
'string_int' => '1',
);
$result = $this->storage->write($name, $data);
$this->assertIdentical($result, TRUE);
$read_data = $this->storage->read($name);
$this->assertIdentical($read_data, $data);
}
abstract protected function read($name); abstract protected function read($name);
abstract protected function insert($name, $data); abstract protected function insert($name, $data);
......
array: []
boolean: true
exp: 1.2e+34
float: 3.14159
hex: 0xC
int: 99
octal: 0775
string: string
string_int: '1'
...@@ -4,12 +4,12 @@ ...@@ -4,12 +4,12 @@
# - may be modified by installation profiles to have other properties. # - may be modified by installation profiles to have other properties.
format: plain_text format: plain_text
name: 'Plain text' name: 'Plain text'
status: '1' status: true
weight: '10' weight: 10
roles: roles:
- anonymous - anonymous
- authenticated - authenticated
cache: '1' cache: true
filters: filters:
# Escape all HTML. # Escape all HTML.
filter_html_escape: filter_html_escape:
......
...@@ -45,6 +45,7 @@ function filter_update_8001() { ...@@ -45,6 +45,7 @@ function filter_update_8001() {
$filter['settings'] = unserialize($filter['settings']); $filter['settings'] = unserialize($filter['settings']);
} }
$format['filters'] = $filters; $format['filters'] = $filters;
$format['status'] = (bool) $format['status'];
// Save the config object. // Save the config object.
$config = \Drupal::config('filter.format.' . $id); $config = \Drupal::config('filter.format.' . $id);
......
...@@ -202,7 +202,7 @@ function filter_formats(AccountInterface $account = NULL) { ...@@ -202,7 +202,7 @@ function filter_formats(AccountInterface $account = NULL) {
$formats['all'] = $cache->data; $formats['all'] = $cache->data;
} }
else { else {
$formats['all'] = \Drupal::entityManager()->getStorageController('filter_format')->loadByProperties(array('status' => '1')); $formats['all'] = \Drupal::entityManager()->getStorageController('filter_format')->loadByProperties(array('status' => TRUE));
uasort($formats['all'], 'Drupal\Core\Config\Entity\ConfigEntityBase::sort'); uasort($formats['all'], 'Drupal\Core\Config\Entity\ConfigEntityBase::sort');
\Drupal::cache()->set("filter_formats:{$language_interface->id}", $formats['all'], CacheBackendInterface::CACHE_PERMANENT, array('filter_formats' => TRUE)); \Drupal::cache()->set("filter_formats:{$language_interface->id}", $formats['all'], CacheBackendInterface::CACHE_PERMANENT, array('filter_formats' => TRUE));
} }
......
...@@ -105,7 +105,7 @@ class FilterFormat extends ConfigEntityBase implements FilterFormatInterface { ...@@ -105,7 +105,7 @@ class FilterFormat extends ConfigEntityBase implements FilterFormatInterface {
* *
* @var bool * @var bool
*/ */
public $cache = 0; public $cache = FALSE;
/** /**
* Configured filters for this text format. * Configured filters for this text format.
......
...@@ -4,8 +4,8 @@ weight: '2' ...@@ -4,8 +4,8 @@ weight: '2'
roles: roles:
- anonymous - anonymous
- authenticated - authenticated
cache: '1' cache: true
status: '1' status: true
langcode: en langcode: en
filters: filters:
filter_html_escape: filter_html_escape:
......
...@@ -444,8 +444,12 @@ function image_field_entity_update(FieldInterface $field) { ...@@ -444,8 +444,12 @@ function image_field_entity_update(FieldInterface $field) {
// The value of a managed_file element can be an array if #extended == TRUE. // The value of a managed_file element can be an array if #extended == TRUE.
$fid_new = (isset($field->settings['default_image']['fids']) ? $field->settings['default_image']['fids'] : $field->settings['default_image']); $fid_new = (isset($field->settings['default_image']['fids']) ? $field->settings['default_image']['fids'] : $field->settings['default_image']);
$fid_old = (isset($prior_field->settings['default_image']['fids']) ? $prior_field->settings['default_image']['fids'] : $prior_field->settings['default_image']); $fid_old = (isset($prior_field->settings['default_image']['fids']) ? $prior_field->settings['default_image']['fids'] : $prior_field->settings['default_image']);
// Ensure sure that fid_new and old are arrays, because default_image might
// be the fallback value 0, see image_field_info().
$fid_old = (array) $fid_old;
$fid_new = (array) $fid_new;
$file_new = $fid_new ? file_load($fid_new) : FALSE; $file_new = $fid_new ? file_load(reset($fid_new)) : FALSE;
if ($fid_new != $fid_old) { if ($fid_new != $fid_old) {
...@@ -457,7 +461,7 @@ function image_field_entity_update(FieldInterface $field) { ...@@ -457,7 +461,7 @@ function image_field_entity_update(FieldInterface $field) {
} }
// Is there an old file? // Is there an old file?
if ($fid_old && ($file_old = file_load($fid_old[0]))) { if ($fid_old && ($file_old = file_load(reset($fid_old)))) {
file_usage()->delete($file_old, 'image', 'default_image', $field->uuid); file_usage()->delete($file_old, 'image', 'default_image', $field->uuid);
} }
} }
......
...@@ -3,6 +3,6 @@ uuid: fe61d3d1-e8d5-47a7-a21c-2841f4be3c09 ...@@ -3,6 +3,6 @@ uuid: fe61d3d1-e8d5-47a7-a21c-2841f4be3c09
label: English label: English
direction: '0' direction: '0'
weight: '0' weight: '0'
locked: '0' locked: false
status: '1' status: true
langcode: en langcode: en
...@@ -3,6 +3,6 @@ uuid: 87e4ef47-819b-4d89-aa4b-757f9ce5a3b2 ...@@ -3,6 +3,6 @@ uuid: 87e4ef47-819b-4d89-aa4b-757f9ce5a3b2
label: 'Not specified' label: 'Not specified'
direction: '0' direction: '0'
weight: '1' weight: '1'
locked: '1' locked: true
status: '1' status: true
langcode: en langcode: en
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment