当前位置: 首页 > 后端技术 > PHP

PHP源码分析等于运算符(==)

时间:2023-03-30 00:06:32 PHP

PHP中不同类型的变量弱类型时,语言本身有特殊的规则。本文从比较规则入手,从源码的角度进行讲解。PHP-version:5.3官方规则参考:PHP.net规则解释通过词法分析/语法分析/使用vld查看opcode,发现在PHP源码中,比较运算符实现的核心方法是compare_function,位于Zend/zend_operators.c+1376确定核心方法源码位置词法分析如上图,通过Zend/zend_language_scanner.l+1201词法分析规则,得到==对应的Token为:T_IS_EQUAL语法分析搜索中得到的Token值上一步在Zend/zend_language_parser.y中获取语法解析中调用生成opcode的方法是:zend_do_binary_op。同时我们可以看到==对应的opcode是ZEND_IS_EQUAL来确定zend执行时的函数opcode。函数opcode对应的调用函数在Zend/zend_vm_execute.h文件中实现。以下函数列表:methods以ZEND_IS_EQUAL_SPEC开头,名字后缀受两个操作数的zval类型影响==。具体类型可以通过vld查看,如:猜测是左操作数的类型+右操作数的类型。以上图为例,左操作数的类型为IS_CONST,右操作数的类型为IS_CV,那么对应的处理方法应该是:ZEND_IS_EQUAL_SPEC_CONST_CV_HANDLER(注:这种确认handler的方法只是经验法则,且源码验证还在等待中)以上推测是否正确并不影响我们接下来的判断,因为我们发现这些方法都调用了同一个核心方法compare_function,比较规则就在这个方法中。通过查看该方法的源码,比较规则一目了然。compare_function方法源代码如下:ZEND_APIintcompare_function(zval*result,zval*op1,zval*op2TSRMLS_DC)/*{{{*/{intret;转换后的整数=0;zvalop1_copy,op2_copy;zval*op_free;while(1){switch(TYPE_PAIR(Z_TYPE_P(op1),Z_TYPE_P(op2))){caseTYPE_PAIR(IS_LONG,IS_LONG):ZVAL_LONG(result,Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)compare_objects(op1,op2TSRMLS_CC));返回成功;}/*故意中断丢失*/default:if(Z_TYPE_P(op1)==IS_OBJECT){if(Z_OBJ_HT_P(op1)->get){op_free=Z_OBJ_HT_P(op1)->get(op1TSRMLS_CC);ret=compare_function(result,op_free,op2TSRMLS_CC);zend_free_obj_get_result(op_freeTSRMLS_CC);返还;}elseif(Z_TYPE_P(op2)!=IS_OBJECT&&Z_OBJ_HT_P(op1)->cast_object){ALLOC_INIT_ZVAL(op_free);如果(Z_OBJ_HT_P(op1)->cast_object(op1,op_free,Z_TYPE_P(op2)TSRMLS_CC)==FAILURE){ZVAL_LONG(结果,1);zend_free_obj_get_result(op_freeTSRMLS_CC);返回成功;}ret=compare_function(result,op_free,op2TSRMLS_CC);zend_free_obj_get_result(op_freeTSRMLS_CC);返还;}}if(Z_TYPE_P(op2)==IS_OBJECT){if(Z_OBJ_HT_P(op2)->get){op_free=Z_OBJ_HT_P(op2)->get(op2TSRMLS_CC);ret=compare_function(result,op1,op_freeTSRMLS_CC);zend_free_obj_get_result(op_freeTSRMLS_CC);返还;}elseif(Z_TYPE_P(op1)!=IS_OBJECT&&Z_OBJ_HT_P(op2)->cast_object){ALLOC_INIT_ZVAL(op_free);如果(Z_OBJ_HT_P(操作2)->cast_object(op2,op_free,Z_TYPE_P(op1)TSRMLS_CC)==FAILURE){ZVAL_LONG(result,-1);zend_free_obj_get_result(op_freeTSRMLS_CC);返回成功;}ret=compare_function(result,op1,op_freeTSRMLS_CC);zend_free_obj_get_result(op_freeTSRMLS_CC);返还;}elseif(Z_TYPE_P(op1)==IS_OBJECT){ZVAL_LONG(result,1);返回成功;}}if(!converted){if(Z_TYPE_P(op1)==IS_NULL){zendi_convert_to_boolean(op2,op2_copy,result);ZVAL_LONG(结果,Z_LVAL_P(op2)?-1:0);返回成功;}elseif(Z_TYPE_P(op2)==IS_NULL){zendi_convert_to_boolean(op1,op1_copy,结果);ZVAL_LONG(结果,Z_LVAL_P(op1)?1:0);返回成功;}elseif(Z_TYPE_P(op1)==IS_BOOL){zendi_convert_to_boolean(op2,op2_copy,result);ZVAL_LONG(结果,ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1)-Z_LVAL_P(op2)));返回成功;}elseif(Z_TYPE_P(op2)==IS_BOOL){zendi_convert_to_boolean(op1,op1_copy,result);ZVAL_LONG(结果,ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1)-Z_LVAL_P(op2)));返回成功;}else{zendi_convert_scalar_to_number(op1,op1_copy,result);zendi_convert_scalar_to_number(op2,op2_copy,结果);转换=1;}}否则如果(Z_TYPE_P(op1)==IS_ARRAY){ZVAL_LONG(结果,1);返回成功;}elseif(Z_TYPE_P(op2)==IS_ARRAY){ZVAL_LONG(result,-1);返回成功;}elseif(Z_TYPE_P(op1)==IS_OBJECT){ZVAL_LONG(result,1);返回成功;}elseif(Z_TYPE_P(op2)==IS_OBJECT){ZVAL_LONG(result,-1);返回成功;}else{ZVAL_LONG(结果,0);返回失败;}}}}操作数类型示例null结果:将NULL转换为“”,进行数字或词汇比较源码描述:数组结果:将字符串和资源转换为数字,按照普通数学进行比较源码描述:其他参见compare_function源码类型比较规则