葡萄全部视频:https://segmentfault.com/a/11...原视频地址:http://replay.xesv5.com/ll/24...流程回顾上节课我们整理整理了$a=1的流程。我们了解了op1、op2、result、opcode的生成过程。下面我们来回顾一下整个过程。staticzend_op_array*zend_compile(inttype){zend_op_array*op_array=NULL;zend_booloriginal_in_compilation=CG(in_compilation);CG(in_compilation)=1;CG(ast)=NULL;CG(ast_arena)=zend_arena_create(1024*32);/首先会分配内存if(!zendparse()){//zendparse(即yyparse)(zend_language_parse.y)==>通过parser调用lexer,生成抽象语法树ast_list,保存到CG(ast);yyparse是通过bison编译zend_language_parser.y生成intlast_lineno=CG(zend_lineno);zend_file_contextoriginal_file_context;zend_oparray_contextoriginal_oparray_context;zend_op_array*original_active_op_array=CG(active_op_array);op_array=emalloc(sizeof(zend_op_array));init_op_array(op_array,类型,INITIAL_OP_ARRAY_SIZE);//初始化oparrayCG(active_op_array)=op_array;如果(zend_ast_process){zend_ast_process(CG(ast));}zend_file_context_begin(&original_file_context);zend_oparray_context_begin(&original_oparray_context);zend_compile_top_stmt(CG(ast));//编译ast生成oparrayCG(zend_lineno)=last_lineno;zend_emit_final_return(类型==ZEND_USER_FUNCTION);//return1会在PHP中加入并在这里处理op_array->line_start=1;op_array->line_end=last_lineno;pass_two(op_array);//针对handler的处理zend_oparray_context_end(&original_oparray_context);zend_file_context_end(&original_file_context);CG(active_op_array)=original_active_op_array;}zend_ast_destroy(CG(ast));zend_arena_destroy(CG(ast_arena));CG(in_compilation)=original_in_compilation;returnop_array;}大致流程是:词法分析->语法分析->编译ast生成op_array->处理return1->对return1链接前的handler做上面的处理,我们在文章中已经提到了。不明白的请看前面的文章。接下来,我们的gdb程序转到链接返回1。代码:fn_flags&ZEND_ACC_RETURN_REFERENCE)!=0;如果(CG(active_op_array)->fn_flags&ZEND_ACC_HAS_RETURN_TYPE&&!(CG(active_op_array)->fn_flags&ZEND_ACC_GENERATOR)){zend_emit_return_type_check(NULL,CG(active_op_array)->arg_info-1,1);(&zn.u.constant,1);//这一步在gdb过程中会到达,给zn.u.constant赋1}else{ZVAL_NULL(&zn.u.constant);}ret=zend_emit_op(NULL,returns_reference?ZEND_RETURN_BY_REF:ZEND_RETURN,&zn,NULL);//这里的字面值会增加一个新的元素1ret->extended_value=-1;}staticzend_op*zend_emit_op(znode*result,zend_uchar操作码,znode*op1,znode*op2)/*{{{*/{zend_op*opline=get_next_op(CG(active_op_array));opline->操作码=操作码;如果(op1==NULL){SET_UNUSED(opline->op1);}else{SET_NODE(opline->op1,op1);}if(op2==NULL){SET_UNUSED(opline->op2);}else{SET_NODE(opline->op2,op2);}zend_check_live_ranges(opline);如果(结果){zend_make_var_result(结果,opline);}returnopline;}#defineSET_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);\//增加元素}else{\target=(src)->u.op;\}\}while(0)我们发现gdb进程在这个函数中添加了1likeliteralselement,我们打印操作码:我们发现添加了一条新的指令,在代码中是return1。此时我们发现有3条指令,2个变量,3个字面量。$a和$b的位置已经存在,字面值也是如此。我们发现处理程序仍然是一个空指针。接下来,让我们看看处理程序的生成。pass_two设置处理程序。让我们继续并转到pass_two函数。在这个函数中,进一步处理了opline指令集。主要工作是设置指令处理程序。源码如下:ZEND_APIintpass_two(zend_op_array*op_array){/**代码省略**/while(opline
