From 4434add3c78a314356895f8558efe8589e968dcf Mon Sep 17 00:00:00 2001 From: Vladimir Kolesnikov Date: Sat, 29 Jun 2013 16:05:18 +0300 Subject: [PATCH 1/2] Phalcon\Config::merge() is able to deal with arrays --- ext/config.c | 86 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/ext/config.c b/ext/config.c index b09bd09c44b..b617534c067 100755 --- a/ext/config.c +++ b/ext/config.c @@ -287,6 +287,38 @@ PHP_METHOD(Phalcon_Config, offsetUnset){ RETURN_TRUE; } +static void array_merge_recursive_n(zval **a1, zval *a2 TSRMLS_DC) +{ + HashTable *ah2; + HashPosition hp2; + zval **hd; + zval *key = NULL, *value = NULL; + zval *tmp1 = NULL, *tmp2 = NULL; + + PHALCON_MM_GROW(); + + phalcon_is_iterable(a2, &ah2, &hp2, 0, 0); + while (zend_hash_get_current_data_ex(ah2, (void**) &hd, &hp2) == SUCCESS) { + PHALCON_GET_HKEY(key, ah2, hp2); + PHALCON_GET_HVALUE(value); + + if (!phalcon_array_isset(*a1, key) || Z_TYPE_P(value) != IS_ARRAY) { + phalcon_array_update_zval(a1, key, &value, PH_COPY | PH_SEPARATE TSRMLS_CC); + } + else { + PHALCON_INIT_NVAR(tmp1); + PHALCON_INIT_NVAR(tmp2); + phalcon_array_fetch(&tmp1, *a1, key, PH_NOISY_CC); + phalcon_array_fetch(&tmp2, a2, key, PH_NOISY_CC); + array_merge_recursive_n(&tmp1, tmp2 TSRMLS_CC); + } + + zend_hash_move_forward_ex(ah2, &hp2); + } + + PHALCON_MM_RESTORE(); +} + /** * Merges a configuration into the current one * @@ -300,6 +332,7 @@ PHP_METHOD(Phalcon_Config, offsetUnset){ PHP_METHOD(Phalcon_Config, merge){ zval *config, *array_config, *value = NULL, *key = NULL, *active_value = NULL; + zval *other_array = NULL, *tmp = NULL; HashTable *ah0; HashPosition hp0; zval **hd; @@ -323,29 +356,48 @@ PHP_METHOD(Phalcon_Config, merge){ PHALCON_GET_HKEY(key, ah0, hp0); PHALCON_GET_HVALUE(value); - if (Z_TYPE_P(value) == IS_OBJECT) { + if (phalcon_isset_property_zval(this_ptr, key TSRMLS_CC)) { /** - * If the key is already defined in the object we have to merge it + * The key is already defined in the object, we have to merge it */ - if (phalcon_isset_property_zval(this_ptr, key TSRMLS_CC)) { - - PHALCON_OBS_NVAR(active_value); - phalcon_read_property_zval(&active_value, this_ptr, key, PH_NOISY_CC); - - /** - * If the current value in member is an object try to merge them - */ - if (Z_TYPE_P(active_value) == IS_OBJECT) { - if (phalcon_method_exists_ex(active_value, SS("merge") TSRMLS_CC) == SUCCESS) { - phalcon_call_method_p1_noret(active_value, "merge", value); - zend_hash_move_forward_ex(ah0, &hp0); - continue; - } + PHALCON_OBS_NVAR(active_value); + phalcon_read_property_zval(&active_value, this_ptr, key, PH_NOISY_CC); + + if (Z_TYPE_P(value) == IS_OBJECT && Z_TYPE_P(active_value) == IS_OBJECT) { + if (phalcon_method_exists_ex(active_value, SS("merge") TSRMLS_CC) == SUCCESS) { /* Path AAA in the test */ + phalcon_call_method_p1_noret(active_value, "merge", value); } + else { /* Path AAB in the test */ + phalcon_update_property_zval_zval(this_ptr, key, value TSRMLS_CC); + } + } + else if (Z_TYPE_P(value) == IS_OBJECT && Z_TYPE_P(active_value) == IS_ARRAY) { /* Path AB in the test */ + PHALCON_INIT_NVAR(other_array); + phalcon_call_func_p1(other_array, "get_object_vars", value); + array_merge_recursive_n(&active_value, other_array TSRMLS_CC); + phalcon_update_property_zval_zval(this_ptr, key, active_value TSRMLS_CC); + } + else if (Z_TYPE_P(value) == IS_ARRAY && Z_TYPE_P(active_value) == IS_OBJECT) { /* Path AC in the test */ + PHALCON_INIT_NVAR(other_array); + phalcon_call_func_p1(other_array, "get_object_vars", active_value); + array_merge_recursive_n(&other_array, value TSRMLS_CC); + phalcon_update_property_zval_zval(this_ptr, key, other_array TSRMLS_CC); } + else if (Z_TYPE_P(value) == IS_ARRAY && Z_TYPE_P(active_value) == IS_ARRAY) { /* Path AD in the test */ + array_merge_recursive_n(&active_value, value TSRMLS_CC); + phalcon_update_property_zval_zval(this_ptr, key, active_value TSRMLS_CC); + } + else { /* Path AE in the test */ + phalcon_update_property_zval_zval(this_ptr, key, value TSRMLS_CC); + } + } + else { /* Path B in the test */ + /** + * The key is not defined in the object, add it + */ + phalcon_update_property_zval_zval(this_ptr, key, value TSRMLS_CC); } - phalcon_update_property_zval_zval(this_ptr, key, value TSRMLS_CC); zend_hash_move_forward_ex(ah0, &hp0); } From 50cc77b6218d91ea75043dd06a219db3f6b80bcd Mon Sep 17 00:00:00 2001 From: Vladimir Kolesnikov Date: Sat, 29 Jun 2013 16:06:44 +0300 Subject: [PATCH 2/2] Updated unit test for Phalcon\Config --- unit-tests/ConfigTest.php | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) mode change 100755 => 100644 unit-tests/ConfigTest.php diff --git a/unit-tests/ConfigTest.php b/unit-tests/ConfigTest.php old mode 100755 new mode 100644 index c597aa33901..3244df4da9a --- a/unit-tests/ConfigTest.php +++ b/unit-tests/ConfigTest.php @@ -185,4 +185,42 @@ public function testConfigMerge() } + + public function testIssue731() + { + // Code path AAA, B, AE, B + $a = new Phalcon\Config(array('aaa' => array('b' => 2, 'c' => 3))); + $b = new Phalcon\Config(array('aaa' => array('a' => 10, 'c' => 30, 'd' => 40))); + $c = array('aaa' => array('b' => 2, 'c' => 30, 'a' => 10, 'd' => 40)); + $a->merge($b); + $this->assertEquals($a->toArray(), $c); + + // Code path AAB + $a = new Phalcon\Config(array('aab' => (object)array('n' => 'm'))); + $b = new Phalcon\Config(array('aab' => array('x' => 'y'))); + $c = array('aab' => array('x' => 'y')); + $a->merge($b); + $this->assertEquals($a->toArray(), $c); + + //Code path AB + $a = new Phalcon\Config(array('ab' => array('b' => 1, 'c' => 2))); + $b = new Phalcon\Config(array('ab' => array(0 => -10, 'c' => 20, 'd' => 30))); + $c = array('ab' => array(0 => -10, 'c' => 2, 'd' => 30, 'b' => 1)); + $b->merge($a); + $this->assertEquals($b->toArray(), $c); + + // Code path AC + $a = new Phalcon\Config(array('ac' => array('b' => 1, 'c' => 2))); + $b = new Phalcon\Config(array('ac' => array(0 => -10, 'c' => 20, 'd' => 30))); + $c = array('ac' => array(0 => -10, 'c' => 20, 'd' => 30, 'b' => 1)); + $a->merge($b); + $this->assertEquals($a->toArray(), $c); + + // Code path AD + $a = new Phalcon\Config(array('ad' => array(0 => 1, 1 => 2, 2 => 3))); + $b = new Phalcon\Config(array('ad' => array(1 => 20, 3 => 40))); + $c = array('ad' => array(0 => 1, 1 => 20, 2 => 3, 3 => 40)); + $a->merge($b); + $this->assertEquals($a->toArray(), $c); + } }