如果让你用一句话描述PHP函数array_diff_uassoc,也许你会想到,就是同时比较两个或多个函数,返回第一个函数中出现的相同键值和不会同时出现在其他函数中的数据。最近看到一个很有意思的问题,关于array_diff_uassoc的实现。看完这道题,才知道对这个函数的误解有多深。这是问题的简化版本:functioncomparekey($a,$b){return0;}$array1=['a'=>1,'b'=>2,'c'=>3,'d'=>4];$array2=['a'=>2,'d'=>4,'e'=>6];$res=array_diff_uassoc($array1,$array2,'comparekey');var_dump($资源);为什么结果是['a'=>1,'c'=>3,'d'=>4];按照正常的逻辑,array_diff_uassoc返回的是不同key不同value的数组数据。如果自定义比较函数返回0,则认为键值相同。所以正常的逻辑应该返回['a'=>1,'b'=>2,'c'=>3]你真的对吗?1.自定义函数比较两个数组的键?其实说实话,一开始我也是这么想的。直到我在自定义函数中分别输出了a和b,看到了诡异的输出内容,我才意识到比较函数并没有那么简单。为了方便看到内容,用下面的数组替换题中的数组内容functioncomparekey($a,$b){echo$a.'-'.$b;返回0;}$array1=['a'=>1,'b'=>2,'c'=>3,'d'=>4];$array2=['e'=>'2','f'=>5,'g'=>6];$res=array_diff_uassoc($array1,$array2,'comparekey');函数的输出是a-bb-cc-de-ff-ga-eb-ec-ed-e,由此可见自定义比较函数的输入不一定来自不同的数组key。也可能有相同数组的键。2、自定义函数是否只是比较key和value是否相等?当然不是,比较功能本身就比较大。但是比较键值是否相等并不是我们理解的。php会根据自定义的返回结果,在内部调整内部指针位置,所以我们看到下面的比较是a-eb-ec-ed-e3。比较键值时,真的是比较一个具有相同键名的数组元素键值吗?这也不是真的。其实是因为比较函数的数组结果影响了PHP内部数组指针位置的变化。变化方式的不同会导致最终比较的是同键名的值而不是像我们想的那样比较同键名的值。查看php源码,array_diff_uassoc最终是通过php_array_diff函数实现的。静态无效php_array_diff(void*base,size_tnmemb,size_tsiz,compare_func_tcmp,swap_func_tswp){...if(hash->nNumOfElements>1){if(behavior==DIFF_NORMAL){zend_sort((void*)lists[i],hash->nNumOfElements,sizeof(Bucket),diff_data_compare_func,(swap_func_t)zend_hash_bucket_swap);}elseif(behavior&DIFF_ASSOC){/*当DIFF_KEY*/zend_sort((void*)lists[i],hash->nNumOfElements,sizeof(Bucket),diff_key_compare_func,(swap_func_t)zend_hash_bucket_swap)时触发;}}...}可以看到diff_key_compare_func被传递给了排序函数。因此,自定义函数的返回结果会影响临时变量列表的输出。PHP在内部首先对所有输入数组进行排序。所以在自定义函数中,可以看出前面的输出内容是依次比较数组的键名。真面目当对输入数组的键名进行排序时,需要将第一个数组的键名与其他数组的键名进行比较。1)比较第一个数组的当前元素的键名是否与要比较的数组的每个元素的键名相同,直到遇到第一个或比较结束。RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));while(Z_TYPE(ptrs[0]->val)!=IS_UNDEF){for(i=1;i
