Commit 8b3e463b authored by catch's avatar catch

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() {
* 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 mixed
* The data that was requested.
*/
......@@ -338,8 +331,6 @@ public function set($key, $value) {
if (!$this->isLoaded) {
$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
// not within keys.
......@@ -354,46 +345,6 @@ public function set($key, $value) {
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.
*
......
......@@ -175,8 +175,9 @@ protected function getParser() {
*/
public function encode($data) {
// The level where you switch to inline YAML is set to PHP_INT_MAX to ensure
// this does not occur.
return $this->getDumper()->dump($data, PHP_INT_MAX);
// this does not occur. Also set the exceptionOnInvalidType parameter to
// 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() {
// Ensure that default values are filled in.
$expected_properties = array(
'id' => 'stark.test_block',
'weight' => '',
'status' => '1',
'weight' => NULL,
'status' => TRUE,
'langcode' => language_default()->id,
'region' => '-1',
'region' => -1,
'plugin' => 'test_html_id',
'settings' => array(
'cache' => '1',
'cache' => 1,
'label' => '',
'module' => 'block_test',
'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE,
),
'visibility' => '',
'visibility' => NULL,
);
$this->assertIdentical($actual_properties, $expected_properties, 'The block properties are exported correctly.');
......
......@@ -163,10 +163,10 @@ function testAdmin() {
$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"]');
$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');
$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() {
$filter_format = $filter_format_storage_controller->create(array(
'format' => 'basic_html',
'name' => 'Basic HTML',
'status' => '1',
'status' => TRUE,
'roles' => array('authenticated'),
), 'filter_format');
$filter_format->setFilterConfig('filter_html', array(
'module' => 'filter',
'status' => '1',
'status' => TRUE,
'settings' => array(
'allowed_html' => '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h4> <h5> <h6> <p> <span> <img>',
),
......
......@@ -9,6 +9,7 @@
use Drupal\Core\Config\ConfigNameException;
use Drupal\simpletest\DrupalUnitTestBase;
use Drupal\Core\Config\FileStorage;
/**
* Tests CRUD operations on configuration objects.
......@@ -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() {
// Verify that the entity was overwritten.
$same_id = entity_load('config_test', $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(), '');
$this->assertIdentical($same_id->label(), NULL);
$this->assertNotEqual($same_id->uuid(), $config_test->uuid());
// Delete the overridden entity first.
......
......@@ -123,7 +123,7 @@ function testReadWriteConfig() {
$this->assertEqual($config->get($true_key), '1', format_string("Boolean TRUE value returned the string '1'."));
// Read null value.
$this->assertIdentical($config->get('null'), '');
$this->assertIdentical($config->get('null'), NULL);
// 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'."));
......
......@@ -55,9 +55,9 @@ function testImport() {
'id' => 'new',
'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
'label' => 'New',
'weight' => '0',
'weight' => 0,
'style' => '',
'status' => '1',
'status' => TRUE,
'langcode' => language_default()->id,
'protected_property' => '',
);
......
......@@ -147,9 +147,9 @@ function testNew() {
'id' => 'new',
'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
'label' => 'New',
'weight' => '0',
'weight' => 0,
'style' => '',
'status' => '1',
'status' => TRUE,
'langcode' => language_default()->id,
'protected_property' => '',
);
......
......@@ -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 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 @@
# - may be modified by installation profiles to have other properties.
format: plain_text
name: 'Plain text'
status: '1'
weight: '10'
status: true
weight: 10
roles:
- anonymous
- authenticated
cache: '1'
cache: true
filters:
# Escape all HTML.
filter_html_escape:
......
......@@ -45,6 +45,7 @@ function filter_update_8001() {
$filter['settings'] = unserialize($filter['settings']);
}
$format['filters'] = $filters;
$format['status'] = (bool) $format['status'];
// Save the config object.
$config = \Drupal::config('filter.format.' . $id);
......
......@@ -202,7 +202,7 @@ function filter_formats(AccountInterface $account = NULL) {
$formats['all'] = $cache->data;
}
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');
\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 {
*
* @var bool
*/
public $cache = 0;
public $cache = FALSE;
/**
* Configured filters for this text format.
......
......@@ -4,8 +4,8 @@ weight: '2'
roles:
- anonymous
- authenticated
cache: '1'
status: '1'
cache: true
status: true
langcode: en
filters:
filter_html_escape:
......
......@@ -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.
$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']);
// 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) {
......@@ -457,7 +461,7 @@ function image_field_entity_update(FieldInterface $field) {
}
// 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);
}
}
......
......@@ -3,6 +3,6 @@ uuid: fe61d3d1-e8d5-47a7-a21c-2841f4be3c09
label: English
direction: '0'
weight: '0'
locked: '0'
status: '1'
locked: false
status: true
langcode: en
......@@ -3,6 +3,6 @@ uuid: 87e4ef47-819b-4d89-aa4b-757f9ce5a3b2
label: 'Not specified'
direction: '0'
weight: '1'
locked: '1'
status: '1'
locked: true
status: true
langcode: en
......@@ -3,6 +3,6 @@ uuid: de5bb3a9-1038-4ada-ba05-05cc965ea702
label: 'Not applicable'
direction: '0'
weight: '2'
locked: '1'
status: '1'
locked: true
status: true
langcode: en
......@@ -491,8 +491,8 @@ function language_save($language) {
// Assign language properties to language entity.
$language_entity->label = isset($language->name) ? $language->name : '';
$language_entity->direction = isset($language->direction) ? $language->direction : '0';
$language_entity->locked = isset($language->locked) ? $language->locked : '0';
$language_entity->weight = isset($language->weight) ? $language->weight : '0';
$language_entity->locked = !empty($language->locked);
$language_entity->weight = isset($language->weight) ? $language->weight : 0;
// Save the record and inform others about the change.
$language_entity->save();
......
......@@ -23,7 +23,7 @@ class LanguageListController extends DraggableListController {
* {@inheritdoc}
*/
public function load() {
$entities = $this->storage->loadByProperties(array('locked' => '0'));
$entities = $this->storage->loadByProperties(array('locked' => FALSE));
// Sort the entities using the entity class's sort() method.
// See \Drupal\Core\Config\Entity\ConfigEntityBase::sort().
......
......@@ -108,7 +108,7 @@ function testNodeFormButtons() {
// Set article content type default to unpublished. This will change the
// the initial order of buttons and/or status of the node when creating
// a node.
\Drupal::config('node.type.article')->set('settings.node.options.status', 0)->save();
\Drupal::config('node.type.article')->set('settings.node.options.status', FALSE)->save();
// Verify the buttons on a node add form for an administrator.
$this->drupalLogin($this->admin_user);
......
......@@ -58,7 +58,7 @@ public function testDrupalWriteRecord() {
*/
public function testEnableUserTwice() {
\Drupal::moduleHandler()->install(array('user'), FALSE);
$this->assertIdentical(config('system.module')->get('enabled.user'), '0');
$this->assertIdentical(config('system.module')->get('enabled.user'), 0);
}
/**
......
......@@ -48,7 +48,7 @@ function setUp() {
'filters' => array(
'filter_html' => array(
'module' => 'filter',
'status' => '1',
'status' => TRUE,
'settings' => array(
'allowed_html' => '<a> <em> <strong>',
),
......
format: basic_html
name: 'Basic HTML'
status: '1'
weight: '0'
status: true
weight: 0
roles:
- authenticated
cache: '1'
cache: true
filters:
filter_html:
id: filter_html
......
format: full_html
name: 'Full HTML'
status: '1'
weight: '1'
status: true
weight: 1
roles:
- administrator
cache: '1'
cache: true
filters:
filter_caption:
id: filter_caption
......
format: restricted_html
name: 'Restricted HTML'
status: '1'
weight: '0'
status: true
weight: 0
roles:
- anonymous
cache: '1'
cache: true
filters:
filter_html:
id: filter_html
......
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