0.php代码
<?php $a='abc'; $b='def'; $c='ghi';
$d='jkl'; $a.=$b.$c.$d;
1.BNF范式(语法规则)
expr_without_variable: | variable T_CONCAT_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_CONCAT, &$$, &$1, &$3 TSRMLS_CC); } | expr '.' expr { zend_do_binary_op(ZEND_CONCAT, &$$, &$1, &$3 TSRMLS_CC); }
2.生成opcode
void zend_do_binary_assign_op(zend_uchar op, znode *result, const znode *op1, const znode *op2 TSRMLS_DC) /* {{{ */ { int last_op_number = get_next_op_number(CG(active_op_array)); zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); if (last_op_number > 0) { //这一段不知道什么意思 zend_op *last_op = &CG(active_op_array)->opcodes[last_op_number-1]; switch (last_op->opcode) { case ZEND_FETCH_OBJ_RW: last_op->opcode = op; last_op->extended_value = ZEND_ASSIGN_OBJ; zend_do_op_data(opline, op2 TSRMLS_CC); SET_UNUSED(opline->result); GET_NODE(result, last_op->result); return; case ZEND_FETCH_DIM_RW: last_op->opcode = op; last_op->extended_value = ZEND_ASSIGN_DIM; zend_do_op_data(opline, op2 TSRMLS_CC); opline->op2.var = get_temporary_variable(CG(active_op_array)); opline->op2_type = IS_VAR; SET_UNUSED(opline->result); GET_NODE(result,last_op->result); return; default: break; } } opline->opcode = op; SET_NODE(opline->op1, op1); SET_NODE(opline->op2, op2); opline->result_type = IS_VAR; opline->result.var = get_temporary_variable(CG(active_op_array)); GET_NODE(result, opline->result); }
#define SET_NODE(target, src) do { target ## _type = (src)->op_type; if ((src)->op_type == IS_CONST) { target.constant = zend_add_literal(CG(active_op_array), &(src)->u.constant TSRMLS_CC); } else { target = (src)->u.op; } } while (0)
3.执行opcode
static int ZEND_FASTCALL ZEND_ASSIGN_CONCAT_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { return zend_binary_assign_op_helper_SPEC_CV_TMP(concat_function, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } static int ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_CV_TMP(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op2, free_op_data2, free_op_data1; zval **var_ptr; zval *value; SAVE_OPLINE(); switch (opline->extended_value) { case ZEND_ASSIGN_OBJ: ..........break; case ZEND_ASSIGN_DIM: { ......... } break; default: value = _get_zval_ptr_tmp(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_RW(EX_CVs(), opline->op1.var TSRMLS_CC); /* do nothing */ break; } if (UNEXPECTED(var_ptr == NULL)) { zend_error_noreturn(E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); } if (UNEXPECTED(*var_ptr == &EG(error_zval))) { ........... ZEND_VM_NEXT_OPCODE(); } SEPARATE_ZVAL_IF_NOT_REF(var_ptr); if (UNEXPECTED(Z_TYPE_PP(var_ptr) == IS_OBJECT) && Z_OBJ_HANDLER_PP(var_ptr, get) && Z_OBJ_HANDLER_PP(var_ptr, set)) { /* proxy object */ //对象的操作 } else { binary_op(*var_ptr, *var_ptr, value TSRMLS_CC);//故意设置第一个参数与第二个参数一样 } ........... zval_dtor(free_op2.var); ........... ZEND_VM_NEXT_OPCODE(); }
typedef int (*binary_op_type)(zval *, zval *, zval * TSRMLS_DC);
执行$a.$b时,执行concat_function,第一个参数为一个临时变量(类型为zval), 执行函数后,这个临时变量即保存了concat后的字符串的首地址,然后这个临时变量会放到yyvsp这个数组里,当再移进.$c后,归约成exp.exp,取出这个临时变量作为函数concat_function的第二个参数,$c作为第三个参数 , 执行concat_funcion,将第一个参数的值放回yyvsp
static int ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE SAVE_OPLINE(); concat_function(&EX_T(opline->result.var).tmp_var, opline->op1.zv, _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op2.var TSRMLS_CC) TSRMLS_CC); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); }
ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */ { zval op1_copy, op2_copy; int use_copy1 = 0, use_copy2 = 0; if (Z_TYPE_P(op1) != IS_STRING) { zend_make_printable_zval(op1, &op1_copy, &use_copy1); } if (Z_TYPE_P(op2) != IS_STRING) { zend_make_printable_zval(op2, &op2_copy, &use_copy2); } if (use_copy1) { /* We have created a converted copy of op1. Therefore, op1 won't become the result so * we have to free it. */ if (result == op1) { zval_dtor(op1); } op1 = &op1_copy; } if (use_copy2) { op2 = &op2_copy; } if (result==op1 && !IS_INTERNED(Z_STRVAL_P(op1))) { /* special case, perform operations on result */ uint res_len = Z_STRLEN_P(op1) + Z_STRLEN_P(op2); if (Z_STRLEN_P(result) < 0 || (int) (Z_STRLEN_P(op1) + Z_STRLEN_P(op2)) < 0) { efree(Z_STRVAL_P(result)); ZVAL_EMPTY_STRING(result); zend_error(E_ERROR, "String size overflow"); } Z_STRVAL_P(result) = erealloc(Z_STRVAL_P(result), res_len+1);//realloc在紧接原来内存后面,开僻一块内存,如果没有足够内存,重新找一块内存 memcpy(Z_STRVAL_P(result)+Z_STRLEN_P(result), Z_STRVAL_P(op2), Z_STRLEN_P(op2)); Z_STRVAL_P(result)[res_len]=0; Z_STRLEN_P(result) = res_len; } else { int length = Z_STRLEN_P(op1) + Z_STRLEN_P(op2); char *buf = (char *) emalloc(length + 1);//重新malloc分配内存 memcpy(buf, Z_STRVAL_P(op1), Z_STRLEN_P(op1)); memcpy(buf + Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2)); buf[length] = 0; ZVAL_STRINGL(result, buf, length, 0); } if (use_copy1) { zval_dtor(op1); } if (use_copy2) { zval_dtor(op2); } return SUCCESS; }