array_intersect_ukey通过自定义函数来比较键,计算数组的交集。
/**
* Computes the intersection of arrays using a callback function on the keys for comparison
* @link https://php.net/manual/en/function.array-intersect-ukey.php
* @param array $array1 <p>
* Initial array for comparison of the arrays.
* </p>
* @param array $array2 <p>
* First array to compare keys against.
* </p>
* @param array $_ [optional]
* @param callback $key_compare_func <p>
* User supplied callback function to do the comparison.
* </p>
* @return array the values of array1 whose keys exist
* in all the arguments.
* @meta
*/
function array_intersect_ukey(array $array1, array $array2, array $_ = null, $key_compare_func) { }
示例:
$array1 = [
'a1' => 'aaa',
'b' => 'bbb',
'c1' => 'ccc'
];
$array2 = [
'a2' => 'aaa',
'b' => 'bbb2',
'c2' => 'ccc'
];
$result = array_intersect_ukey($array1, $array2, function($k1, $k2){
//echo $k1.'--'.$k2."\n";
if ($k1 == $k2) {
return 0;
} elseif ($k1 < $k2) {
return -1;
} else {
return 1;
}
});
var_dump($result);
//结果
//array(1) {
// 'b' =>
// string(3) "bbb"
//}
php7.4.8 数组函数 array_intersect_ukey实现源码
PHP_FUNCTION(array_intersect_ukey)
{
php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
}
php_array_intersect函数
static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
{
zval *args = NULL;
HashTable *hash;
int arr_argc, i, c = 0;
uint32_t idx;
Bucket **lists, *list, **ptrs, *p;
uint32_t req_args;
char *param_spec;
zend_fcall_info fci1, fci2;
zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
zend_fcall_info *fci_key = NULL, *fci_data;
zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
PHP_ARRAY_CMP_FUNC_VARS;
int (*intersect_key_compare_func)(const void *, const void *);
int (*intersect_data_compare_func)(const void *, const void *);
if (behavior == INTERSECT_NORMAL) {
intersect_key_compare_func = php_array_key_compare_string;
if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
/* array_intersect() */
req_args = 2;
param_spec = "+";
intersect_data_compare_func = php_array_data_compare_string;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
/* array_uintersect() */
req_args = 3;
param_spec = "+f";
intersect_data_compare_func = php_array_user_compare;
} else {
php_error_docref(NULL, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type);
return;
}
if (ZEND_NUM_ARGS() < req_args) {
php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
return;
}
fci_data = &fci1;
fci_data_cache = &fci1_cache;
} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
/* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
* no comparison of the data is done (part of INTERSECT_ASSOC) */
if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
/* array_intersect_assoc() or array_intersect_key() */
req_args = 2;
param_spec = "+";
intersect_key_compare_func = php_array_key_compare_string;
intersect_data_compare_func = php_array_data_compare_string;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
/* array_uintersect_assoc() */
req_args = 3;
param_spec = "+f";
intersect_key_compare_func = php_array_key_compare_string;
intersect_data_compare_func = php_array_user_compare;
fci_data = &fci1;
fci_data_cache = &fci1_cache;
} else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
/* array_intersect_uassoc() or array_intersect_ukey() */
req_args = 3;
param_spec = "+f";
intersect_key_compare_func = php_array_user_key_compare;
intersect_data_compare_func = php_array_data_compare_string;
fci_key = &fci1;
fci_key_cache = &fci1_cache;
} else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
/* array_uintersect_uassoc() */
req_args = 4;
param_spec = "+ff";
intersect_key_compare_func = php_array_user_key_compare;
intersect_data_compare_func = php_array_user_compare;
fci_data = &fci1;
fci_data_cache = &fci1_cache;
fci_key = &fci2;
fci_key_cache = &fci2_cache;
} else {
php_error_docref(NULL, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type);
return;
}
if (ZEND_NUM_ARGS() < req_args) {
php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
return;
}
} else {
php_error_docref(NULL, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior);
return;
}
PHP_ARRAY_CMP_FUNC_BACKUP();
/* for each argument, create and sort list with pointers to the hash buckets */
lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
BG(user_compare_fci) = *fci_data;
BG(user_compare_fci_cache) = *fci_data_cache;
} else if (behavior & INTERSECT_ASSOC && key_compare_type == INTERSECT_COMP_KEY_USER) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
}
for (i = 0; i < arr_argc; i++) {
if (Z_TYPE(args[i]) != IS_ARRAY) {
php_error_docref(NULL, E_WARNING, "Expected parameter %d to be an array, %s given", i + 1, zend_zval_type_name(&args[i]));
arr_argc = i; /* only free up to i - 1 */
goto out;
}
hash = Z_ARRVAL(args[i]);
list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
lists[i] = list;
ptrs[i] = list;
for (idx = 0; idx < hash->nNumUsed; idx++) {
p = hash->arData + idx;
if (Z_TYPE(p->val) == IS_UNDEF) continue;
*list++ = *p;
}
ZVAL_UNDEF(&list->val);
if (hash->nNumOfElements > 1) {
if (behavior == INTERSECT_NORMAL) {
zend_sort((void *) lists[i], hash->nNumOfElements,
sizeof(Bucket), intersect_data_compare_func, (swap_func_t)zend_hash_bucket_swap);
} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
zend_sort((void *) lists[i], hash->nNumOfElements,
sizeof(Bucket), intersect_key_compare_func, (swap_func_t)zend_hash_bucket_swap);
}
}
}
/* copy the argument array */
RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
/* go through the lists and look for common values */
while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
&& key_compare_type == INTERSECT_COMP_KEY_USER) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
}
for (i = 1; i < arr_argc; i++) {
if (behavior & INTERSECT_NORMAL) {
while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
ptrs[i]++;
}
} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
ptrs[i]++;
}
if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
/* this means that ptrs[i] is not NULL so we can compare
* and "c==0" is from last operation
* in this branch of code we enter only when INTERSECT_ASSOC
* since when we have INTERSECT_KEY compare of data is not wanted. */
if (data_compare_type == INTERSECT_COMP_DATA_USER) {
BG(user_compare_fci) = *fci_data;
BG(user_compare_fci_cache) = *fci_data_cache;
}
if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
c = 1;
if (key_compare_type == INTERSECT_COMP_KEY_USER) {
BG(user_compare_fci) = *fci_key;
BG(user_compare_fci_cache) = *fci_key_cache;
/* When KEY_USER, the last parameter is always the callback */
}
/* we are going to the break */
} else {
/* continue looping */
}
}
}
if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
/* delete any values corresponding to remains of ptrs[0] */
/* and exit because they do not present in at least one of */
/* the other arguments */
for (;;) {
p = ptrs[0]++;
if (Z_TYPE(p->val) == IS_UNDEF) {
goto out;
}
if (p->key == NULL) {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
} else {
zend_hash_del(Z_ARRVAL_P(return_value), p->key);
}
}
}
if (c) /* here we get if not all are equal */
break;
ptrs[i]++;
}
if (c) {
/* Value of ptrs[0] not in all arguments, delete all entries */
/* with value < value of ptrs[i] */
for (;;) {
p = ptrs[0];
if (p->key == NULL) {
zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
} else {
zend_hash_del(Z_ARRVAL_P(return_value), p->key);
}
if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
goto out;
}
if (behavior == INTERSECT_NORMAL) {
if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
break;
}
} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
/* no need of looping because indexes are unique */
break;
}
}
} else {
/* ptrs[0] is present in all the arguments */
/* Skip all entries with same value as ptrs[0] */
for (;;) {
if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
goto out;
}
if (behavior == INTERSECT_NORMAL) {
if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
break;
}
} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
/* no need of looping because indexes are unique */
break;
}
}
}
}
out:
for (i = 0; i < arr_argc; i++) {
hash = Z_ARRVAL(args[i]);
pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
}
PHP_ARRAY_CMP_FUNC_RESTORE();
efree(ptrs);
efree(lists);
}
php_array_user_key_compare函数
static int php_array_user_key_compare(const void *a, const void *b) /* {{{ */
{
Bucket *f;
Bucket *s;
zval args[2];
zval retval;
zend_long result;
f = (Bucket *) a;
s = (Bucket *) b;
if (f->key == NULL) {
ZVAL_LONG(&args[0], f->h);
} else {
ZVAL_STR_COPY(&args[0], f->key);
}
if (s->key == NULL) {
ZVAL_LONG(&args[1], s->h);
} else {
ZVAL_STR_COPY(&args[1], s->key);
}
BG(user_compare_fci).param_count = 2;
BG(user_compare_fci).params = args;
BG(user_compare_fci).retval = &retval;
BG(user_compare_fci).no_separation = 0;
if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
result = zval_get_long(&retval);
zval_ptr_dtor(&retval);
} else {
result = 0;
}
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
return ZEND_NORMALIZE_BOOL(result);
}