Issue #1500238 by alexpott, sun, chx: Encode/decode data via PHP serialize()/unserialize() for DatabaseStorage (storage controller specific data formats).

8.0.x
catch 2012-05-16 12:02:24 +09:00
parent 14fbd55791
commit e0ba373d6e
6 changed files with 103 additions and 118 deletions

View File

@ -1,6 +1,7 @@
<?php
use Drupal\Core\Config\DatabaseStorage;
use Drupal\Core\Config\FileStorage;
/**
* @file
@ -49,7 +50,8 @@ function config_install_default_config($module) {
$config_name = str_replace('.xml', '', $file);
$storage = new DatabaseStorage($config_name);
$storage->write(file_get_contents($module_config_dir . '/' . $file));
$data = FileStorage::decode(file_get_contents($module_config_dir . '/' . $file));
$storage->write($data);
}
}
}
@ -116,111 +118,3 @@ function config_get_storage_names_with_prefix($prefix = '') {
function config($name, $class = 'Drupal\Core\Config\DrupalConfig') {
return new $class(new DatabaseStorage($name));
}
/**
* Decodes configuration data from its native format to an associative array.
*
* @param $data
* Configuration data.
*
* @return
* An associative array representation of the data.
*/
function config_decode($data) {
if (empty($data)) {
return array();
}
// This is the fastest and easiest way to get from a string of XML to a PHP
// array since SimpleXML and json_decode()/encode() are native to PHP. Our
// only other choice would be a custom userspace implementation which would
// be a lot less performant and more complex.
$xml = new SimpleXMLElement($data);
$json = json_encode($xml);
return json_decode($json, TRUE);
}
/**
* Standardizes SimpleXML object output into simple arrays for easier use.
*
* @param $xmlObject
* A valid XML string.
*
* @return
* An array representation of A SimpleXML object.
*/
function config_xml_to_array($data) {
$out = array();
$xmlObject = simplexml_load_string($data);
if (is_object($xmlObject)) {
$attributes = (array) $xmlObject->attributes();
if (isset($attributes['@attributes'])) {
$out['#attributes'] = $attributes['@attributes'];
}
}
if (trim((string) $xmlObject)) {
return trim((string) $xmlObject);
}
foreach ($xmlObject as $index => $content) {
if (is_object($content)) {
$out[$index] = config_xml_to_array($content);
}
}
return $out;
}
/**
* Encodes an array into the native configuration format.
*
* @param $data
* An associative array or an object
*
* @return
* A representation of this array or object in the native configuration
* format.
*
* @todo The loaded XML can be invalid; throwing plenty of PHP warnings but no
* catchable error.
*/
function config_encode($data) {
// Convert the supplied array into a SimpleXMLElement.
$xml_object = new SimpleXMLElement("<?xml version=\"1.0\"?><config></config>");
config_array_to_xml($data, $xml_object);
// Pretty print the result.
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml_object->asXML());
return $dom->saveXML();
}
/**
* Encodes an array into XML
*
* @param $data
* An associative array or an object
*
* @return
* A representation of this array or object in the native configuration
* format.
*/
function config_array_to_xml($array, &$xml_object) {
foreach ($array as $key => $value) {
if (is_array($value)) {
if (!is_numeric($key)){
$subnode = $xml_object->addChild("$key");
config_array_to_xml($value, $subnode);
}
else {
config_array_to_xml($value, $xml_object);
}
}
else {
$xml_object->addChild($key, $value);
}
}
}

View File

@ -11,24 +11,30 @@ use Exception;
class DatabaseStorage extends StorageBase {
/**
* Overrides StorageBase::read().
* Implements StorageInterface::read().
*/
public function read() {
// There are situations, like in the installer, where we may attempt a
// read without actually having the database available. In this case,
// catch the exception and just return an empty array so the caller can
// handle it if need be.
$data = array();
try {
return db_query('SELECT data FROM {config} WHERE name = :name', array(':name' => $this->name))->fetchField();
} catch (Exception $e) {
return array();
$raw = db_query('SELECT data FROM {config} WHERE name = :name', array(':name' => $this->name))->fetchField();
if ($raw !== FALSE) {
$data = $this->decode($raw);
}
}
catch (Exception $e) {
}
return $data;
}
/**
* Implements StorageInterface::writeToActive().
*/
public function writeToActive($data) {
$data = $this->encode($data);
return db_merge('config')
->key(array('name' => $this->name))
->fields(array('data' => $data))
@ -44,6 +50,20 @@ class DatabaseStorage extends StorageBase {
->execute();
}
/**
* Implements StorageInterface::encode().
*/
public static function encode($data) {
return serialize($data);
}
/**
* Implements StorageInterface::decode().
*/
public static function decode($raw) {
return unserialize($raw);
}
/**
* Implements StorageInterface::getNamesWithPrefix().
*/

View File

@ -36,7 +36,7 @@ class DrupalConfig {
* Reads config data from the active store into our object.
*/
public function read() {
$active = (array) config_decode($this->storage->read());
$active = (array) $this->storage->read();
foreach ($active as $key => $value) {
// If the setting is empty, return an empty string rather than an array.
// This is necessary because SimpleXML's default behavior is to return
@ -201,7 +201,7 @@ class DrupalConfig {
* Saves the configuration object to disk as XML.
*/
public function save() {
$this->storage->write(config_encode($this->data));
$this->storage->write($this->data);
}
/**

View File

@ -67,6 +67,7 @@ class FileStorage {
* Exception
*/
public function write($data) {
$data = $this->encode($data);
if (!file_put_contents($this->getFilePath(), $data)) {
throw new FileStorageException('Failed to write configuration file: ' . $this->getFilePath());
}
@ -81,7 +82,7 @@ class FileStorage {
public function read() {
if ($this->exists()) {
$data = $this->readData();
return $data;
return $this->decode($data);
}
return FALSE;
}
@ -93,4 +94,64 @@ class FileStorage {
// Needs error handling and etc.
@drupal_unlink($this->getFilePath());
}
/**
* Implements StorageInterface::encode().
*/
public static function encode($data) {
// Convert the supplied array into a SimpleXMLElement.
$xml_object = new \SimpleXMLElement("<?xml version=\"1.0\"?><config></config>");
self::encodeArrayToXml($data, $xml_object);
// Pretty print the result.
$dom = new \DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml_object->asXML());
return $dom->saveXML();
}
/**
* Encodes an array into XML
*
* @param $array
* An associative array to encode.
*
* @return
* A representation of $array in XML.
*/
protected static function encodeArrayToXml($array, &$xml_object) {
foreach ($array as $key => $value) {
if (is_array($value)) {
if (!is_numeric($key)){
$subnode = $xml_object->addChild("$key");
self::encodeArrayToXml($value, $subnode);
}
else {
self::encodeArrayToXml($value, $xml_object);
}
}
else {
$xml_object->addChild($key, $value);
}
}
}
/**
* Implements StorageInterface::decode().
*/
public static function decode($raw) {
if (empty($raw)) {
return array();
}
// This is the fastest and easiest way to get from a string of XML to a PHP
// array since SimpleXML and json_decode()/encode() are native to PHP. Our
// only other choice would be a custom userspace implementation which would
// be a lot less performant and more complex.
$xml = new \SimpleXMLElement($raw);
$json = json_encode($xml);
return json_decode($json, TRUE);
}
}

View File

@ -74,6 +74,16 @@ interface StorageInterface {
*/
function writeToFile($data);
/**
* Encodes configuration data into the storage-specific format.
*/
public static function encode($data);
/**
* Decodes configuration data from the storage-specific format.
*/
public static function decode($raw);
/**
* Gets names starting with this prefix.
*

View File

@ -14,7 +14,7 @@ use Drupal\simpletest\WebTestBase;
class ConfigFileSecurityTestCase extends WebTestBase {
protected $filename = 'foo.bar';
protected $testContent = 'Good morning, Denver!';
protected $testContent = array('greeting' => 'Good morning, Denver!');
public static function getInfo() {
return array(
@ -40,7 +40,7 @@ class ConfigFileSecurityTestCase extends WebTestBase {
$file = new FileStorage($this->filename);
$saved_content = $file->read();
$this->assertEqual($saved_content, $this->testContent, 'A file can be read back successfully.');
$this->assertEqual($saved_content, $this->testContent);
}
catch (Exception $e) {
$this->fail('File failed verification when being read.');