当前位置: 首页 > Linux

PHP字符串小bug研究

时间:2023-04-06 20:44:03 Linux

今天翻看手册,发现一个很有意思的问题。字符串的替换规则是什么?根据手册中的描述,字符串替换需要满足的条件可以转化为字符串数组1下标的条件。对于下标小于字符串长度且不为负数的情况,与数组2一致。对于大于字符串长度的下标,下标前会补空字符。3.如果下标为负数,php什么都不做。作为实验,参见$a="a";var_dump($a);$a[20]="b";$a[0]="c";$b="";$b[-2]="e";var_dump($a,$b);Resultphp7.2PHPWarning:Illegalstringoffset:-2***inonline7Warning:Illegalstringoffset:-2in***online7string(21)"cb"string(0)""如果是负数下标,直接报错。让我们看看5.6发生了什么。php5.6string(21)"cb"array(1){[-2]=>string(1)"e"}PHPWarning:Illegalstringoffset:-2inF:\phplearn\string.phponline8Warning:Illegalstringoffset:-2inF:\phplearn\string.phponline8string(21)"cb"string(2)"22"要追溯源码,先在zend_language_parse.y中搜索expr_without_variable,通过'['找到|combined_scalar_offset{zend_do_end_variable_parse(&$1,BP_VAR_R,0TSRMLS_CC);}|组合标量{$$=$1;}combined_scalar_offset:combined_scalar_offset:combined'dim_offset']'{zend_do_begin_variable_parse(TSRMLS_C);fetch_array_dim(&$$,&$1,&$3TSRMLS_CC);}|combined_scalar_offset'['dim_offset']'{fetch_array_dim(&$$,&$1,&$3TSRMLS_CC);}|T_CONSTANT_ENCAPSED_STRING'['dim_offset']'{$1.EA=0;zend_do_begin_variable_parse(TSRMLS_C);fetch_array_dim(&$$,&$1,&$3TSRMLS_CC);}|general_constant'['dim_offset']'{zend_do_begin_variable_parse(TSRMLS_C);fetch_array_dim(&$$,&$1,&$3TSRMLS_CC);};知道fetch_array_dim为下标解析函数voidfetch_array_dim(znode*result,constznode*parent,constznode*dimTSRMLS_DC)/*{{{*/{zend_opopline;zend_llist*fetch_list_ptr;zend_stack_top(&CG(bp_stack),(void**)&fetch_list_ptr);如果(zend_is_function_or_method_call(parent)){init_op(&oplineTSRMLS_CC);opline.opcode=ZEND_SEPARATE;SET_NODE(opline.op1,parent);SET_UNUSED(opline.op2);opline.result_type=IS_VAR;opline.result.var=opline.op1.var;zend_llist_add_element(fetch_list_ptr,&opline);}init_op(&oplineTSRMLS_CC);opline.opcode=ZEND_FETCH_DIM_W;/*回补例程假定W*/opline.result_type=IS_VAR;opline.result.var=get_temporary_variable(CG(active_op_array));SET_NODE(opline.op1,parent);SET_NODE(opline.op2,dim);//这里针对key为字符串的情况会做转换if(opline.op2_type==IS_CONST&&Z_TYPE(CONSTANT(opline.op2.constant))==IS_STRING){ulongindex;int数值=0;ZEND_HANDLE_NUMERIC_EX(Z_STRVAL(CONSTANT(opline.op2.constant)),Z_STRLEN(CONSTANT(opline.op2.constant))+1,index,numeric=1);如果(数字){zval_dtor(&CONSTANT(opline.op2.constant));ZVAL_LONG(&CONSTANT(opline.op2.constant),索引);}else{CALCULATE_LITERAL_HASH(opline.op2.constant);}}GET_NODE(结果t,opline.result);zend_llist_add_element(fetch_list_ptr,&opline);}通过代码看到是通过ZEND_FETCH_DIM_W这条操作码执行的ZEND_VM_HANDLER(84,ZEND_FETCH_DIM_W,VAR|CV,CONST|TMP|VAR|UNUSED|CV){USE_OPLINE_op1,free_opLINE_op2zval**容器;SAVE_OPLINE();容器=GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);if(OP1_TYPE==IS_VAR&&UNEXPECTED(container==NULL)){zend_error_noreturn(E_ERROR,"不能将字符串偏移量用作数组");}zend_fetch_dimension_address(&EX_T(opline->result.var),container,GET_OP2_ZVAL_PTR(BP_VAR_R),OP2_TYPE,BP_VAR_WTSRMLS_CC);FREE_OP2();如果(OP1_TYPE==IS_VAR&&OP1_FREE&&READY_TO_DESTROY(free_op1.var)){EXTRACT_ZVAL_PTR(&EX_T(opline->result.var));}FREE_OP1_VAR_PTR();/*我们将通过引用分配结果*/if(UNEXPECTED(opline->extended_value!=0)){zval**retval_ptr=EX_T(opline->result.var).var.ptr_ptr;如果(retval_ptr){Z_DELREF_PP(retval_ptr);SEPARATE_ZVAL_TO_MAKE_IS_REF(retval_ptr);Z_ADDREF_PP(retval_ptr);}}CHECK_EXCEPTION();ZEND_VM_NEXT_OPCODE();为什么5.6修改空字符串中负数下标的值变成数组//这里是读取字符串下标的地方caseIS_STRING:{zvaltmp;//Keyif(type!=BP_VAR_UNSET&&Z_STRLEN_P(container)==0){gotoconvert_to_array;}if(dim==NULL){zend_error_noreturn(E_ERROR,"[]字符串不支持运算符");}if(type!=BP_VAR_UNSET){SEPARATE_ZVAL_IF_NOT_REF(container_ptr);}if(Z_TYPE_P(dim)!=IS_LONG){switch(Z_TYPE_P(dim)){/*caseIS_LONG:*/caseIS_STRING:如果(IS_LONG==is_numeric_string(Z_STRVAL_P(dim),Z_STRLEN_P(dim),NULL,NULL,-1)){break;}if(type!=BP_VAR_UNSET){zend_error(E_WARNING,"Illegalstringoffset'%s'",dim->value.str.val);}休息;caseIS_DOUBLE:caseIS_NULL:caseIS_BOOL:zend_error(E_NOTICE,"Stringoffsetcastoccurred");休息;默认值:zend_error(E_WARNING,"非法偏移类型");休息;}tmp=*昏暗;zval_copy_ctor(&tmp);convert_to_long(&tmp);昏暗=&tmp;}container=*container_ptr;结果->str_offset.str=容器;PZVAL_LOCK(容器);结果->str_offset.offset=Z_LVAL_P(dim);结果->str_offset.ptr_ptr=NULL;返回;}休息;

最新推荐
猜你喜欢