Details
-
Type:
Bug
-
Status:
Resolved
-
Priority:
Major
-
Resolution: Fixed
-
Affects Version/s: None
-
Fix Version/s: 1.6.1
-
Component/s: Zend_Config
-
Labels:None
Description
From Daniel Skinner
================
I am getting unexpected behaviour when cloning a Zend_Config object:
Example (actual behaviour):
$parent = new Zend_Config(array('key' => array('nested' => 'parent')), true); //allow read-write for merging $newConfig = clone $parent; $newConfig->merge(new Zend_Config(array('key' => array('nested' => 'override')), true)); echo $newConfig->key->nested; // 'override' - as expected echo $parent->key->nested; // 'override' - I was expecting this to be 'parent'
Intuitive behaviour (what makes sense):
Cloning a Zend_Config object should completely separate the new instance from the original - i.e. there should be no cross-references.
$parent = new Zend_Config(array('key' => array('nested' => 'parent')), true); //allow read-write for merging $newConfig = clone $parent; $newConfig->merge(new Zend_Config(array('key' => array('nested' => 'override')), true)); echo $newConfig->key->nested; // 'override' echo $parent->key->nested; // 'parent'
This is occurring because cloning Zend_Config only creates a shallow clone currently.
Solution 1: Cast to an array and create a new instance:
This can be achieved already and effectively creates a deep clone.
$parent = new Zend_Config(array('key' => array('nested' => 'parent')), true); //allow read-write for merging $newConfig = new Zend_Config($parent->toArray(), true); //cast the parent object to an array and create a new Zend_Config $newConfig->merge(new Zend_Config(array('key' => array('nested' => 'override')), true)); echo $newConfig->key->nested; // 'override' - as expected echo $parent->key->nested; // 'parent' - as expected
Solution 2: Fixing Zend_Config to perform a deep clone:
/** * Perform a deep clone of this instance to allow side-effect free cloning. * @return void */ public function __clone() { $data = array(); foreach ($this->_data as $key => $value) { if ($value instanceof Zend_Config) { $data[$key] = clone $value; } else { $data[$key] = $value; } } $this->_data = $data; }
Gives:
$parent = new Zend_Config(array('key' => array('nested' => 'parent')), true); //allow read-write for merging $newConfig = clone $parent; $newConfig->merge(new Zend_Config(array('key' => array('nested' => 'override')), true)); echo $newConfig->key->nested; // 'override' - as expected echo $parent->key->nested; // 'parent' - as expected
It makes sense to me that this is the expected behaviour when cloning and a deep clone greatly reduces the chance of hard to detect side-effects. Does anybody disagree?
A full description of the above can be found at: http://www.daniel-skinner.co.uk/cloning-zend_config-without-side-effects/26/08/2008
Test cases and a patch can be found at: http://www.destiny-denied.co.uk/files/ZendConfigClone.zip
Thanks Daniel for doing the research!
Resolved on trunk in svn r11113.