|
|
|
No because array_merge() is not recursive... and array_merge_recursive() has a bad behaviour with the keys (see comments http://php.net/manual/en/function.array-merge-recursive.php
I used this static method for a personal project public static function mergeArrays( &$offset1, $offset2 ) { $type1 = is_array($offset1); $type2 = is_array($offset2); // if both offsets don't have same type : problem if( $type1 != $type2 ) throw new MergeArraysException( $offset1, $offset2 ); // both are not an array (string, int, ...) ? it means we must replace the current value if( !$type1 ) // && !$type2 $offset1 = $offset2; // both are an array : recursivity else foreach ( $offset2 as $key2=>$foo ) self::mergeArrays($offset1[$key2], $offset2[$key2]); } Hi,
I modified Config like this public function __construct($array, $allowModifications = false) Why does inter-section inheritance not fulfill your need to inherit and override configuration data? I'm unsure from the problem description that you could not structure your configuration data into sections:
; ini format example [development] database.host = 'db.dev.example.com' database.name = 'mydata' ; ... [production : development] database.host = 'db.example.com' ; ... In the above example, you could load the development or the production section. If you load the production section, it inherits all the values from development, and, as you can see, database.host has been overridden to a different server. The database.name value, however, would remain equal to mydata. Zend_Config was specifically designed to support config data inheritance; for what reasons can you not use the built-in support? This functionality is useful when you want a settings file that can be checked in to your repository and one that is developer/environment-specific, but that you don't want checked in. Having two separate files is essential, but having multiple Zend_Config objects is a hassle.
For example, say I have the basic configuration file: [production] ; settings... [development : production] ; other settings... In groups, an individual developer will need to supply his own settings that might override development settings. For example, they will need to change database.port to their database instance. You don't want this file checked in accidentally and somehow missed by QA--that would be bad. Thus, separate configurations that are merged together. Here's one way to do it. I've implemented this functionality based on a function written by Keith Devens (i.e., _mergeArrays(), posted on his blog In Zend_Config: /**
* Merge two configurations.
*
* @see _mergeArrays()
* @param Zend_Config $otherConfig Configuration to merge with
* @return Dna_Configuration
*/
public function mergeWith(Zend_Config $otherConfig)
{
$array = $this->_mergeArrays($this->toArray(), $otherConfig->toArray());
$this->__construct($array, $this->_allowModifications);
return $this;
}
/**
* Recursively merges two arrays and returns the result.
*
* Where there is duplication, values from the second array overwrite
* values from the first. This differs from array_merge_recursive() and
* the + operator, which append those values into a new array.
*
* @param array $a First array
* @param array $b Second array (supercedes first array)
* @return array Merged array
*/
protected function _mergeArrays($a, $b)
{
if (is_array($a) and is_array($b) and !array_key_exists(0, $a)) {
while (list($key) = each($b)) {
if (array_key_exists($key, $a)) {
$a[$key] = $this->_mergeArrays($a[$key], $b[$key]);
} else {
$a[$key] = $b[$key];
}
}
} else {
$a = $b;
}
return $a;
}
Alternatively, you can solve the problem by having each developer with his own settings that inherit from the "development" section. If committing such information is undesirable, this is also easily solved by using svn:ignore
This is the same methodology used in the test suite, where TestConfiguration.php.dist is the default configuration, and users can copy this file to TestConfiguration.php, making whatever changes they like, and SVN ignores the file so that it is not committed with other work. I don't like hacking classes; Here is a non-destructive OO approach
class Ixulai_Config_Compound extends Zend_Config { public function __construct(Array $configs, $allowModifications = false) { $compoundConfig = array(); foreach($configs as $config) { if(!($config instanceof Zend_Config)) { throw new Zend_Config_Exception('All elements of config array must be an instance of Zend_Config'); } $compoundConfig = $this->_mergeRecursive($compoundConfig, $config->toArray()); } parent::__construct($compoundConfig, $allowModifications); } protected function _mergeRecursive($array1, $array2) { if(is_array($array1) && is_array($array2)) { $keys = array_keys($array2); foreach($keys as $key) { if(isset($array1[$key])) { $array1[$key] = $this->_mergeRecursive($array1[$key], $array2[$key]); } else { $array1[$key] = $array2[$key]; } } } else { $array1 = $array2; } return $array1; } } I believe I may have borrowed the mergeRecursive function from the same place as the one above though mine looks a bit different. I've had it kicking around for a while. It doesn't matter; any recursive merge function would work, even array_merge_recursive if thats what you need. You can compound configs as many times as you like... $config1 = new Zend_Config_Xml('test.xml'); $config2 = new Zend_Config_Ini('test.ini'); $config3 = new Zend_Config(array('moo')); //Create a merge of 1 & 2 $config4 = new Ixulai_Config_Compound($config1, $config2); //Create a merge of 3 with the result from merging 1 & 2 $config5 = new Ixulai_Config_Compound($config3, $config4); Doh; Examples above for config4 and 5 should have arguments in an array.
Thats what you get for not testing examples. Pasting from original message
<?php
function _fix_merge($array) {
foreach($array as $k => $v) {
if (!is_array($v)) {
continue;
}
if (is_int(key($v))) {
$array[$k] = $v[1];
} else {
$array[$k] = _fix_merge($v);
}
}
return $array;
}
$config1 = new Zend_Config_Ini(...);
$config2 = new Zend_Config_Ini(...);
$config = new Zend_Config(_fix_merge(array_merge_recursive($config1->toArray(), $config2->toArray())));
?>
Suggested patch:
/** * Merge another Zend_Config with this one. The items * in $merge will override the same named items in * the current config. * * @param Zend_Config $merge * @return Zend_Config */ public function merge(Zend_Config $merge) { foreach($merge as $key => $item) { if(array_key_exists($key, $this->_data)) { if(get_class($item) == 'Zend_Config' && get_class($this->$key) == 'Zend_Config') { $this->$key = $this->$key->merge($item); } else { $this->$key = $item; } } else { $this->$key = $item; } } return $this; } Test: public function testMerge() { $stdArray = array( 'test_feature' => false, 'some_files' => array( 'foo'=>'dir/foo.xml', 'bar'=>'dir/bar.xml', ), 2 => 123, ); $stdConfig = new Zend_Config($stdArray, true); $devArray = array( 'test_feature'=>true, 'some_files' => array( 'bar' => 'myDir/bar.xml', 'baz' => 'myDir/baz.xml', ), 2 => 456, ); $devConfig = new Zend_Config($devArray); $stdConfig->merge($devConfig); $this->assertTrue($stdConfig->test_feature); $this->assertEquals('myDir/bar.xml', $stdConfig->some_files->bar); $this->assertEquals('myDir/baz.xml', $stdConfig->some_files->baz); $this->assertEquals('dir/foo.xml', $stdConfig->some_files->foo); $this->assertEquals(456, $stdConfig->{2}); } Anyone have any objections to me committing this to the trunk? Regards, Rob... I like this patch, Rob. Why did you choose get_class() over instanceof? The former approach does not support extending Zend_Config as easily, since one will have to override the merge() method in such cases where the method will be used in an extending class. Otherwise, I think this would be a nice, self-contained feature we could squeeze into 1.0.2.
Updated merge function:
public function merge(Zend_Config $merge) { foreach($merge as $key => $item) { if(array_key_exists($key, $this->_data)) { if($item instanceof Zend_Config && $this->$key instanceof Zend_Config) { $this->$key = $this->$key->merge($item); } else { $this->$key = $item; } } else { $this->$key = $item; } } return $this; } Commited to trunk in svn revision 6387. Thanks, Rob. Now I can get rid of my custom class.
Fixing Fix Version to follow issue tracker conventions.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
$merged = new Zend_Config(array_merge($default_conf->asArray(), $user_conf->asArray()));work?