Skip to content

Commit

Permalink
Merge pull request #739 from sjinks/issue-731
Browse files Browse the repository at this point in the history
Properly Merge Arrays in Phalcon\Config::merge()
  • Loading branch information
Phalcon committed Jun 29, 2013
2 parents 0eb3752 + 50cc77b commit 1e3e20d
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 17 deletions.
86 changes: 69 additions & 17 deletions ext/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down
38 changes: 38 additions & 0 deletions unit-tests/ConfigTest.php
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

0 comments on commit 1e3e20d

Please sign in to comment.