作者归档:admin

PHP源码阅读笔记十八:array_diff_key,array_diff_assoc,array_udiff_assoc 函数

PHP源码阅读笔记十八:array_diff_key,array_diff_assoc,array_udiff_assoc
【array_diff_key】

(PHP 5 >= 5.1.0RC1)
array_diff_key — 使用键名比较计算数组的差集
说明

array array_diff_key ( array array1, array array2 [, array ...] )

array_diff_key() 返回一个数组,该数组包括了所有出现在 array1 中但是未出现在任何其它参数数组中的键名的值。注意关联关系保留不变。

【array_diff_assoc】

(PHP 4 >= 4.3.0, PHP 5)
array_diff_assoc — 带索引检查计算数组的差集
说明

array array_diff_assoc ( array array1, array array2 [, array ...] )

array_diff_assoc() 返回一个数组,该数组包括了所有在 array1 中但是不在任何其它参数数组中的值。注意和 array_diff() 不同的是键名也用于比较。

之所以把这三个函数放在一起是因为他们调用的最后是一个函数:php_array_diff_key
【array_diff_uassoc】

(PHP 5)
array_diff_uassoc — 用用户提供的回调函数做索引检查来计算数组的差集
说明

array array_diff_uassoc ( array array1, array array2 [, array ..., callback key_compare_func] )

array_diff_uassoc() 返回一个数组,该数组包括了所有在 array1 中但是不在任何其它参数数组中的值。

这三个函数调用php-array_diff_key的方式如下:

1
2
3
array_diff_key:         php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
array_diff_assoc:      php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
array_udiff_assoc:    php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);

其中参数的宏定义如下:

1
2
3
4
5
#define INTERNAL_FUNCTION_PARAM_PASSTHRU ht, return_value, return_value_ptr, this_ptr, return_value_used TSRMLS_CC
 
#define DIFF_COMP_DATA_NONE    -1
#define DIFF_COMP_DATA_INTERNAL 0
#define DIFF_COMP_DATA_USER     1

php_array_diff_key函数首先要根据传入的data_compare_type(即上面定义的三个宏),判断所要使用的比较函数,然后遍历第一个数组,针对每一个元素与其它数组比较,看其它数字是否存在
如果存在则删
针对此函数的源码做一个注释性的说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 
    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);
            RETVAL_NULL();
            goto out;    //    goto语句,这个貌似一般是高手用的,嘿嘿
        }
    }
 
    array_init(return_value);    //    返回数组初始化,初始化存储此数组的Hash Table
 
    for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) {    //    遍历第一个参数包含的双向链表
        if (p->nKeyLength == 0) {    //    数组索引为数字
            ok = 1;
            for (i = 1; i < argc; i++) {    //    对其它的数组进行判断
                if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == SUCCESS &&
                    (!diff_data_compare_func ||
                     diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)) {
                    ok = 0;
                    break;
                }
            }
            if (ok) {    在第一个数组中,不在其它数组中
                (*((zval**)p->pData))->refcount++;
                zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL);
            }
        } else {    //    索引为字符串
            ok = 1;
            for (i = 1; i < argc; i++) {
                if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == SUCCESS &&
                    (!diff_data_compare_func ||
                     diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0)) {
                    ok = 0;
                    break;
                }
            }
            if (ok) {
                (*((zval**)p->pData))->refcount++;
                zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL);
            }
        }
    }

关于比较函数:
DIFF_COMP_DATA_NONE参数对应的是NULL,即不存在比较,使用索引查找就可以了
DIFF_COMP_DATA_INTERNAL参数对应的是zval_compare,即索引和值都会比较
DIFF_COMP_DATA_USER参数对应的是zval_user_compare,即用户自定义的函数

EOF

PHP源码阅读笔记十七:array_diff,array_udiff,array_diff_ukey,array_diff_uassoc,array_udiff_uassoc

PHP源码阅读笔记十七:array_diff,array_udiff,array_diff_ukey,array_diff_uassoc,array_udiff_uassoc
这4个函数调用的是同一个函数php_array_diff,所不同的是他们的参数。
虽然这五个函数都是调用同一个方法,但是在这个方法中,除了对输入的处理和释放相关内存的操作外,程序的共用并不是太多
难道是因为在语义的相似?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
array_diff:        php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL,
                                    DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_INTERNAL);
 
array_diff_ukey: php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY,
                                 DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
 
array_udiff:    php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL,
                            DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
f
array_diff_uassoc: php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC,
                            DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
 
array_udiff_uassoc: php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC,
                            DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);

程序说明:
在获得了输入和作了相关错误处理后
程序首先确认比较函数是哪个,此处,针对不同的behavior和data_compare_type有不同的比较函数,也有不同的输入参数错误处理
然后对输入的参数复制并按照之前得到的diff_key_compare_func进行排序,排序调用的是zend_qsort函数
然后初始化返回数组的hash table,并将第一个参数复制到返回数组
最后遍历第一个参数的所有值,并判断是否不在其它参数中,
在遍历过程中,如果某一个值在其它参数中则删除返回数组中所有等于这个值的元素,如果某一个值不在其它参数中,则跳过
这样留下来的就是需要返回的值。

EOF

PHP源码阅读笔记十六:array_count_values函数

PHP源码阅读笔记十六:array_count_values
array_count_values

(PHP 4, PHP 5)
array_count_values — 统计数组中所有的值出现的次数
说明

array array_count_values ( array input )

array_count_values() 返回一个数组,该数组用 input 数组中的值作为键名,该值在 input 数组中出现的次数作为值。

源程序说明:
在源代码中的两句注释就说明了这个函数的实现

1
2
3
4
    /* Initialize return array */
    array_init(return_value);
 
    /* Go through input array and add values to the return array */

但是其中还有一些细节需要注意:
1、此函数只能识别字符串和数字,所以程序中使用了类似于下面的语句

1
2
3
4
5
if (Z_TYPE_PP(entry) == IS_LONG) {
} else if (Z_TYPE_PP(entry) == IS_STRING) {
} else {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only count STRING and INTEGER values!");
}

2、在遍历过程中,首先判断是否不存在,此判断过程在针对字符串和数字时也有不同,但最终都是针对hash table的操作
在代码中针对zval的初始化使用的是宏zval *data; MAKE_STD_ZVAL(data);
跟踪此宏的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MAKE_STD_ZVAL(data);
==> #define MAKE_STD_ZVAL(zv)                 \    zend.h 586行
    ALLOC_ZVAL(zv); \
    INIT_PZVAL(zv);
 
==> #define ALLOC_ZVAL(z)    \
    ZEND_FAST_ALLOC(z, zval, ZVAL_CACHE_LIST)    zend_alloc.h 165行
 
==> #define ZEND_FAST_ALLOC(p, type, fc_type)    \
    (p) = (type *) emalloc(sizeof(type))                   zend_alloc.h  152行
 
==> #define emalloc(size)                        _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)    zend_alloc.h 56行
==> ZEND_API void *_emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)      zend_alloc.c 2288行 程序实现
 
==>  #define INIT_PZVAL(z)        \                 zend.h 576行
    (z)->refcount = 1;        \
    (z)->is_ref = 0;

EOF