循环结构
实际应用中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。循环结构是在一定条件下反复执行某段程序的流程结构,被反复执行的程序被称为循环体。循环语句是由循环体及循环的终止条件两部分组成的。
PHP中的循环结构有4种:while、for、foreach、do while,接下来我们分析下这几个结构的具体的实现。
while循环
while循环的语法:
while(expression) { statement;//循环体 }
while的结构比较简单,由两部分组成:expression、statement,其中expression为循环判断条件,当expression为true时重复执行statement,具体的语法规则:
statement: ... | T_WHILE '(' expr ')' while_statement { $$ = zend_ast_create(ZEND_AST_WHILE, $3, $5); } ... ; while_statement: statement { $$ = $1; } | ':' inner_statement_list T_ENDWHILE ';' { $$ = $2; } ;
从while语法规则可以看出,在解析时会创建一个ZEND_AST_WHILE节点,expression、statement分别保存在两个子节点中,其AST如下:
while编译的过程也比较简单,比较特别的是while首先编译的是循环体,然后才是循环判断条件,更像是do while,编译过程大致如下:
- (1) 首先编译一条ZEND_JMP的opcode,这条opcode用来跳到循环判断条件expression的位置,由于while是先编译循环体再编译循环条件,所以此时还无法确定具体的跳转值;
- (2) 编译循环体statement;编译完成后更新步骤(1)中ZEND_JMP的跳转值;
- (3) 编译循环判断条件expression;
- (4) 编译一条ZEND_JMPNZ的opcode,这条opcode用于循环判断条件执行完以后跳到循环体的,如果循环条件成立则通过此opcode跳到循环体开始的位置,否则继续往下执行(即:跳出循环)。
具体的编译过程:
void zend_compile_while(zend_ast *ast) { zend_ast *cond_ast = ast->child[0]; zend_ast *stmt_ast = ast->child[1]; znode cond_node; uint32_t opnum_start, opnum_jmp, opnum_cond; //(1)编译ZEND_JMP opnum_jmp = zend_emit_jump(0); zend_begin_loop(ZEND_NOP, NULL); //(2)编译循环体statement,opnum_start为循环体起始位置 opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); //设置ZEND_JMP opcode的跳转值 opnum_cond = get_next_op_number(CG(active_op_array)); zend_update_jump_target(opnum_jmp, opnum_cond); //(3)编译循环条件expression zend_compile_expr(