Issue #1500238 by alexpott, sun, chx: Encode/decode data via PHP serialize()/unserialize() for DatabaseStorage (storage controller specific data formats).
parent
14fbd55791
commit
e0ba373d6e
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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().
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.');
|
||||
|
|
Loading…
Reference in New Issue