深入php内核之php in array


Posted in PHP onNovember 10, 2015

先给大家介绍php in array函数基本知识热热身。

定义和用法

in_array() 函数在数组中搜索给定的值。

语法
in_array(value,array,type)

参数 描述
value 必需。规定要在数组搜索的值。
array 必需。规定要搜索的数组。
type 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。

 说明

如果给定的值 value 存在于数组 array 中则返回 true。如果第三个参数设置为 true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。

如果没有在数组中找到参数,函数返回 false。

注释:如果 value 参数是字符串,且 type 参数设置为 true,则搜索区分大小写。

无意中看到一段代码

<?php    
$y="1800";
$x = array();
for($j=0;$j<50000;$j++){
 $x[]= "{$j}";
}
for($i=0;$i<30000;$i++){
 if(in_array($y,$x)){
  continue;
 } 
}

测试了一下

[root@dev tmp]# time php b.php
real    0m9.517s
user    0m4.486s
sys     0m0.015s

竟然需要9s

in_array是这个样子的

bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

在 haystack 中搜索 needle,如果没有设置 strict 则使用宽松的比较。

needle

待搜索的值。如果 needle 是字符串,则比较是区分大小写的。

haystack

这个数组。

strict

如果第三个参数 strict 的值为 TRUE 则 in_array() 函数还会检查 needle 的类型是否和 haystack 中的相同。

那么我看一下源代码

第一步 在ext/standard/array.c 文件中

/* }}} */              
/* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
 Checks if the given value exists in the array */   
PHP_FUNCTION(in_array)          
{                
 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); 
}                
/* }}} */             
/* {{{ proto mixed array_search(mixed needle, array haystack [, bool strict])
 Searches the array for a given value and returns the corresponding key if successful */
PHP_FUNCTION(array_search)         
{            
 php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); 
}                
/* }}} */

顺便看到了array_search,原来和in_array的内部实现基本一致

其中函数的参数 在./zend.h中

#define INTERNAL_FUNCTION_PARAM_PASSTHRU ht, return_value, return_value_ptr, this_ptr, return_value_used TSRMLS_CC

第二步 在ext/standard/array.c 文件中 查看php_search_array原型

/* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
 * 0 = return boolean
 * 1 = return key
 */  
static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
{  
 zval *value,   /* value to check for */
   *array,   /* array to check in */
   **entry,   /* pointer to array entry */
   res;    /* comparison result */
 HashPosition pos;  /* hash iterator */
 zend_bool strict = 0;  /* strict comparison or not */
 ulong num_key;
 uint str_key_len;
 char *string_key;
 int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &value, &array, &strict) == FAILURE) {
  return;
 } 
 if (strict) {
  is_equal_func = is_identical_function;
 } 
 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) {
  is_equal_func(&res, value, *entry TSRMLS_CC);
  if (Z_LVAL(res)) {
   if (behavior == 0) {
    RETURN_TRUE;
   } else {
    /* Return current key */
    switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) {
     case HASH_KEY_IS_STRING:
      RETURN_STRINGL(string_key, str_key_len - 1, 1);
      break;
     case HASH_KEY_IS_LONG:
      RETURN_LONG(num_key);
      break;
    }
   }
  }
  zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
 } 
 RETURN_FALSE;
}  
/* }}} */
/* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
 Checks if the given value exists in the array */

我们发现 strict  这个值的不同有两种比较方式,看一下两个函数的不同之处

is_identical_function 检查类型是否相同

ZEND_API int is_identical_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
{  
 Z_TYPE_P(result) = IS_BOOL;
 if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
  Z_LVAL_P(result) = 0;
  return SUCCESS;
 } 
 switch (Z_TYPE_P(op1)) {
  case IS_NULL:
   Z_LVAL_P(result) = 1;
   break;
  case IS_BOOL:
  case IS_LONG:
  case IS_RESOURCE:
   Z_LVAL_P(result) = (Z_LVAL_P(op1) == Z_LVAL_P(op2));
   break;
  case IS_DOUBLE:
   Z_LVAL_P(result) = (Z_DVAL_P(op1) == Z_DVAL_P(op2));
   break;
  case IS_STRING:
   Z_LVAL_P(result) = ((Z_STRLEN_P(op1) == Z_STRLEN_P(op2))
   && (!memcmp(Z_STRVAL_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op1))));
   break;
  case IS_ARRAY:
   Z_LVAL_P(result) = (Z_ARRVAL_P(op1) == Z_ARRVAL_P(op2)
   zend_hash_compare(Z_ARRVAL_P(op1), Z_ARRVAL_P(op2), (compare_func_t) hash_zval_identical_function, 1 TSRMLS_CC)==0);
   break;
  case IS_OBJECT:
   if (Z_OBJ_HT_P(op1) == Z_OBJ_HT_P(op2)) {
   Z_LVAL_P(result) = (Z_OBJ_HANDLE_P(op1) == Z_OBJ_HANDLE_P(op2));
   } else {
   Z_LVAL_P(result) = 0;
   }
   break;
  default:
   Z_LVAL_P(result) = 0;
   return FAILURE;
 } 
 return SUCCESS;
}  
/* }}} */

is_equal_function 不检查类型是否相同,所以需要隐式转换

ZEND_API int is_equal_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
{  
 if (compare_function(result, op1, op2 TSRMLS_CC) == FAILURE) {
  return FAILURE;
 } 
 ZVAL_BOOL(result, (Z_LVAL_P(result) == 0));
 return SUCCESS;
}  
/* }}} */
==》compare_function

ZEND_API int compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
{   
 int ret;
 int converted = 0;
 zval op1_copy, op2_copy;
 zval *op_free;
 while (1) {
  switch (TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
   case TYPE_PAIR(IS_LONG, IS_LONG):
   ZVAL_LONG(result, Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)<Z_LVAL_P(op2)?-1:0));
   return SUCCESS;
   case TYPE_PAIR(IS_DOUBLE, IS_LONG):
   Z_DVAL_P(result) = Z_DVAL_P(op1) - (double)Z_LVAL_P(op2);
   ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
   return SUCCESS;
   case TYPE_PAIR(IS_LONG, IS_DOUBLE):
   Z_DVAL_P(result) = (double)Z_LVAL_P(op1) - Z_DVAL_P(op2);
   ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
   return SUCCESS;
   case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
   if (Z_DVAL_P(op1) == Z_DVAL_P(op2)) {
    ZVAL_LONG(result, 0);
   } else {
    Z_DVAL_P(result) = Z_DVAL_P(op1) - Z_DVAL_P(op2);
    ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
   }
   return SUCCESS;
   case TYPE_PAIR(IS_ARRAY, IS_ARRAY):
   zend_compare_arrays(result, op1, op2 TSRMLS_CC);
   return SUCCESS;
   case TYPE_PAIR(IS_NULL, IS_NULL):
   ZVAL_LONG(result, 0);
   return SUCCESS;
   case TYPE_PAIR(IS_NULL, IS_BOOL):
   ZVAL_LONG(result, Z_LVAL_P(op2) ? -1 : 0);
   return SUCCESS;
   case TYPE_PAIR(IS_BOOL, IS_NULL):
   ZVAL_LONG(result, Z_LVAL_P(op1) ? 1 : 0);
   return SUCCESS;
   case TYPE_PAIR(IS_BOOL, IS_BOOL):
   ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1) - Z_LVAL_P(op2)));
   return SUCCESS;
   case TYPE_PAIR(IS_STRING, IS_STRING):
   zendi_smart_strcmp(result, op1, op2);
   return SUCCESS;
   case TYPE_PAIR(IS_NULL, IS_STRING):
   ZVAL_LONG(result, zend_binary_strcmp("", 0, Z_STRVAL_P(op2), Z_STRLEN_P(op2)));
   return SUCCESS;
   case TYPE_PAIR(IS_STRING, IS_NULL):
   ZVAL_LONG(result, zend_binary_strcmp(Z_STRVAL_P(op1), Z_STRLEN_P(op1), "", 0));
   return SUCCESS;
   case TYPE_PAIR(IS_OBJECT, IS_NULL):
   ZVAL_LONG(result, 1);
   return SUCCESS;
   case TYPE_PAIR(IS_NULL, IS_OBJECT):
   ZVAL_LONG(result, -1);
   return SUCCESS;
   case TYPE_PAIR(IS_OBJECT, IS_OBJECT):
   /* If both are objects sharing the same comparision handler then use is */
   if (Z_OBJ_HANDLER_P(op1,compare_objects) == Z_OBJ_HANDLER_P(op2,compare_objects)) {
    if (Z_OBJ_HANDLE_P(op1) == Z_OBJ_HANDLE_P(op2)) {
     /* object handles are identical, apparently this is the same object */
     ZVAL_LONG(result, 0);
     return SUCCESS;
    }
    ZVAL_LONG(result, Z_OBJ_HT_P(op1)->compare_objects(op1, op2 TSRMLS_CC));
    return SUCCESS;
   }
   /* break missing intentionally */
   default:
   if (Z_TYPE_P(op1) == IS_OBJECT) {
    if (Z_OBJ_HT_P(op1)->get) {
     op_free = Z_OBJ_HT_P(op1)->get(op1 TSRMLS_CC);
     ret = compare_function(result, op_free, op2 TSRMLS_CC);
     zend_free_obj_get_result(op_free TSRMLS_CC);
     return ret;
    } else if (Z_TYPE_P(op2) != IS_OBJECT && Z_OBJ_HT_P(op1)->cast_object) {
     ALLOC_INIT_ZVAL(op_free);
     if (Z_OBJ_HT_P(op1)->cast_object(op1, op_free, Z_TYPE_P(op2) TSRMLS_CC) == FAILURE) {
      ZVAL_LONG(result, 1);
      zend_free_obj_get_result(op_free TSRMLS_CC);
      return SUCCESS;
     }
     ret = compare_function(result, op_free, op2 TSRMLS_CC);
     zend_free_obj_get_result(op_free TSRMLS_CC);
     return ret;
    }
   }
   if (Z_TYPE_P(op2) == IS_OBJECT) {
    if (Z_OBJ_HT_P(op2)->get) {
     op_free = Z_OBJ_HT_P(op2)->get(op2 TSRMLS_CC);
     ret = compare_function(result, op1, op_free TSRMLS_CC);
     zend_free_obj_get_result(op_free TSRMLS_CC);
     return ret;
    } else if (Z_TYPE_P(op1) != IS_OBJECT && Z_OBJ_HT_P(op2)->cast_object) {
     ALLOC_INIT_ZVAL(op_free);
     if (Z_OBJ_HT_P(op2)->cast_object(op2, op_free, Z_TYPE_P(op1) TSRMLS_CC) == FAILURE) {
      ZVAL_LONG(result, -1);
      zend_free_obj_get_result(op_free TSRMLS_CC);
      return SUCCESS;
     }
     ret = compare_function(result, op1, op_free TSRMLS_CC);
     zend_free_obj_get_result(op_free TSRMLS_CC);
     return ret;
    } else if (Z_TYPE_P(op1) == IS_OBJECT) {
     ZVAL_LONG(result, 1);
     return SUCCESS;
    }
   }
   if (!converted) {
    if (Z_TYPE_P(op1) == IS_NULL) {
     zendi_convert_to_boolean(op2, op2_copy, result);
     ZVAL_LONG(result, Z_LVAL_P(op2) ? -1 : 0);
     return SUCCESS;
    } else if (Z_TYPE_P(op2) == IS_NULL) {
     zendi_convert_to_boolean(op1, op1_copy, result);
     ZVAL_LONG(result, Z_LVAL_P(op1) ? 1 : 0);
     return SUCCESS;
    } else if (Z_TYPE_P(op1) == IS_BOOL) {
     zendi_convert_to_boolean(op2, op2_copy, result);
     ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1) - Z_LVAL_P(op2)));
     return SUCCESS;
    } else if (Z_TYPE_P(op2) == IS_BOOL) {
     zendi_convert_to_boolean(op1, op1_copy, result);
     ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1) - Z_LVAL_P(op2)));
     return SUCCESS;
    } else {
     zendi_convert_scalar_to_number(op1, op1_copy, result);
     zendi_convert_scalar_to_number(op2, op2_copy, result);
     converted = 1;
    }
   } else if (Z_TYPE_P(op1)==IS_ARRAY) {
    ZVAL_LONG(result, 1);
    return SUCCESS;
   } else if (Z_TYPE_P(op2)==IS_ARRAY) {
    ZVAL_LONG(result, -1);
    return SUCCESS;
   } else if (Z_TYPE_P(op1)==IS_OBJECT) {
    ZVAL_LONG(result, 1);
    return SUCCESS;
   } else if (Z_TYPE_P(op2)==IS_OBJECT) {
    ZVAL_LONG(result, -1);
    return SUCCESS;
   } else {
    ZVAL_LONG(result, 0);
    return FAILURE;
   }
  } 
 }  
}   
/* }}} */
PHP 相关文章推荐
php实现快速排序法函数代码
Aug 27 PHP
编写Smarty插件在模板中直接加载数据的详细介绍
Jun 26 PHP
PHP 伪静态技术原理以及突破原理实现介绍
Jul 12 PHP
PHP中VC6、VC9、TS、NTS版本的区别与用法详解
Oct 26 PHP
PHP内核探索:变量存储与类型使用说明
Jan 30 PHP
ThinkPHP3.1新特性之Action参数绑定
Jun 19 PHP
PHP实现的简单日历类
Nov 29 PHP
yii2.0之GridView自定义按钮和链接用法
Dec 15 PHP
PHP大神的十大优良习惯
Sep 14 PHP
PHP获取redis里不存在的6位随机数应用示例【设置24小时过时】
Jun 07 PHP
PHP多线程模拟实现秒杀抢单
Feb 07 PHP
使用swoole 定时器变更超时未支付订单状态的解决方案
Jul 24 PHP
谈谈你对Zend SAPIs(Zend SAPI Internals)的理解
Nov 10 #PHP
php实现可运算的验证码
Nov 10 #PHP
如何使用PHP Embed SAPI实现Opcodes查看器
Nov 10 #PHP
深入理解PHP内核(二)之SAPI探究
Nov 10 #PHP
深入理解PHP内核(一)
Nov 10 #PHP
在PHP中使用FastCGI解析漏洞及修复方案
Nov 10 #PHP
PHP中使用GD库绘制折线图 折线统计图的绘制方法
Nov 09 #PHP
You might like
JQuery入门——用bind方法绑定事件处理函数应用介绍
2013/02/05 Javascript
SinaEditor使用方法详解
2013/12/28 Javascript
js插件YprogressBar实现漂亮的进度条效果
2015/04/20 Javascript
Javascript对象Clone实例分析
2015/06/09 Javascript
JQuery+Ajax实现数据查询、排序和分页功能
2015/09/27 Javascript
jQuery表单验证简单示例
2016/10/17 Javascript
浅谈jQuery绑定事件会叠加的解决方法和心得总结
2016/10/26 Javascript
利用纯JS实现像素逐渐显示的方法示例
2017/08/14 Javascript
vue-resouce设置请求头的三种方法
2017/09/12 Javascript
微信小程序picker组件简单用法示例【附demo源码下载】
2017/12/05 Javascript
JS设计模式之观察者模式实现实时改变页面中金额数的方法
2018/02/05 Javascript
微信小程序之自定义组件的实现代码(附源码)
2018/08/02 Javascript
Vue 组件封装 并使用 NPM 发布的教程
2018/09/30 Javascript
微信小程序map组件结合高德地图API实现wx.chooseLocation功能示例
2019/01/23 Javascript
vue监听用户输入和点击功能
2019/09/27 Javascript
[06:07]DOTA2-DPC中国联赛 正赛 Ehome vs VG 选手采访
2021/03/11 DOTA
python计算书页码的统计数字问题实例
2014/09/26 Python
终端命令查看TensorFlow版本号及路径的方法
2018/06/13 Python
Python中的函数式编程:不可变的数据结构
2018/10/08 Python
在pycharm中python切换解释器失败的解决方法
2018/10/29 Python
Python实现的列表排序、反转操作示例
2019/03/13 Python
Python实现图像的垂直投影示例
2020/01/17 Python
Tensorflow 实现释放内存
2020/02/03 Python
pytorch 计算ConvTranspose1d输出特征大小方式
2020/06/23 Python
意大利体育用品网上商城:Nencini Sport
2016/08/18 全球购物
New Era英国官网:美国棒球帽品牌
2018/03/21 全球购物
自荐信要包含哪些内容
2013/11/06 职场文书
前台文员个人求职信范文
2014/01/05 职场文书
教师业务学习制度
2014/01/25 职场文书
新闻传播专业求职信
2014/07/22 职场文书
升职自我推荐信范文
2015/03/25 职场文书
家装电话营销开场白
2015/05/29 职场文书
2019年英语版感谢信(8篇)
2019/09/29 职场文书
新学期新寄语,献给新生们!
2019/11/15 职场文书
Nginx 根据URL带的参数转发的实现
2021/04/01 Servers
SpringBoot 拦截器妙用你真的了解吗
2021/07/01 Java/Android