在使用array_merge时是需要注意键的类型的,对于string 的key 和 数字key的处理方式是有区别的,虽然应用没问题, 但是偶尔写的时候总有点对预期结果不踏实,所以我们还是从底层来分析下,这样在用时候也就底气十足了
PHP_FUNCTION(array_merge)
{
php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
}
注意传入的参数,
recursive=0, replace=0
( 不递归merge,数字索引不替换 )
static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */
{
zval ***args = NULL;
int argc, i, init_size = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
return;
}
for (i = 0; i < argc; i++) {
if (Z_TYPE_PP(args[i]) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1);
efree(args);
RETURN_NULL();
} else {
int num = zend_hash_num_elements(Z_ARRVAL_PP(args[i]));
/* 使用元素最多的数组的大小作为init_size的大小 */
if (num > init_size) {
init_size = num;
}
}
}
array_init_size(return_value, init_size);
for (i = 0; i < argc; i++) {
if (!replace) {
php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), recursive TSRMLS_CC);
} else if (recursive && i > 0) { /* First array will be copied directly instead */
php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]) TSRMLS_CC);
} else {
zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1);
}
}
efree(args);
}
这里进入php_array_merge 根据传入的参数 recursive=0 省略多余分支,基本流程可归结为
switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)){
case HASH_KEY_IS_STRING:
Z_ADDREF_PP(src_entry);
zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL);
break;
case HASH_KEY_IS_LONG:
Z_ADDREF_PP(src_entry);
zend_hash_next_index_insert(dest, src_entry, sizeof(zval *), NULL);
break;
}
上述代码表明:
对于字符串索引,会更新字符串索引的值,其结果就是参数靠后数组的值会覆盖靠前的数组的值。
而对于数字型索引,PHP执行的zend_hash_next_index_insert操作,也就是插入一个新的元素,这同时也更改了键(例如原来的key=2, array_merge之后,可能变成了0)。
这样我们在理解下面的脚本时就没什么疑问了
$array1 = array( 'key'=>'one_key_a', 10 => 'zero_a' , 2 => 'two_a' , 3 => 'three_a' );
$array2 = array( 'key'=>'one_key_b', 1 => 'one_b' , 3 => 'three_b' , 4 => 'four_b' );
var_dump(array_merge($array1, $array2), ($array1 + $array2), $array1, $array2);
结果:
array (size=7)
'key' => string 'one_key_b' (length=9)
0 => string 'zero_a' (length=6)
1 => string 'two_a' (length=5)
2 => string 'three_a' (length=7)
3 => string 'one_b' (length=5)
4 => string 'three_b' (length=7)
5 => string 'four_b' (length=6)
array (size=6)
'key' => string 'one_key_a' (length=9)
10 => string 'zero_a' (length=6)
2 => string 'two_a' (length=5)
3 => string 'three_a' (length=7)
1 => string 'one_b' (length=5)
4 => string 'four_b' (length=6)
array (size=4)
'key' => string 'one_key_a' (length=9)
10 => string 'zero_a' (length=6)
2 => string 'two_a' (length=5)
3 => string 'three_a' (length=7)
array (size=4)
'key' => string 'one_key_b' (length=9)
1 => string 'one_b' (length=5)
3 => string 'three_b' (length=7)
4 => string 'four_b' (length=6)