File tree Expand file tree Collapse file tree 3 files changed +24
-1
lines changed Expand file tree Collapse file tree 3 files changed +24
-1
lines changed Original file line number Diff line number Diff line change @@ -79,7 +79,7 @@ for(...){
7979__ (1) 编译循环语句__ 
8080
8181``` c 
82- void  zend_compile_for (zend_ast * ast) / *  {{{  * / 
82+ void  zend_compile_for (zend_ast * ast)
8383{
8484 zend_ast * init_ast = ast->child[ 0] ;
8585 zend_ast * cond_ast = ast->child[ 1] ;
Original file line number Diff line number Diff line change 1+ # 附录2:defer推迟执行语法的实现  
2+ 
3+ 使用过Go语言的应该都知道defer这个语法,它用来推迟一个函数的执行,在函数执行返回前首先检查当前函数内是否有推迟执行的函数,如果有则执行,然后再返回。defer是一个非常有用的语法,这个功能可以很方便的在函数结束前执行一些清理工作,比如关闭打开的文件、关闭连接、释放资源、解锁等等。这样延迟一个函数有以下两个好处:
4+ 
5+  * (1) 靠近使用位置,避免漏掉清理工作,同时比放在函数结尾要清晰 
6+  * (2) 如果有多处返回的地方可以避免代码重复,比如函数中有很多处return 
7+ 
8+ 在一个函数中可以使用多个defer,其执行顺序与栈类似:后进先出,先定义的defer后执行。另外,在返回之后定义的defer将不会被执行,只有返回前定义的才会执行,通过exit退出程序的情况也不会执行任何defer。
9+ 
10+ 在PHP中并没有实现类似的语法,接下来我们介绍下如何在PHP中实现类似Go语言中defer的功能。此功能的实现需要对PHP的语法解析、抽象语法树/opcode的编译、opcode指令的执行等环节进行改造,涉及的地方比较多,但是改动点比较简单,可以很好的帮助大家完整的理解PHP编译、执行两个核心阶段的实现。整体实现思路:
11+ 
12+  * (1) 语法解析:defer本质上还是函数调用,只是将调用时机移到了函数的最后,所以编译时可以复用调用函数的规则,但是需要与普通的调用区分开,所以我们新增一个AST节点类型,其子节点为为正常函数调用编译的AST,语法我们定义为:`defer function_name()`; 
13+  * (2) AST编译为opcode:编译opcode时也复用调用函数的编译逻辑,不同的地方在于把defer放在最后编译,另外需要在编译return前新增一条opcode,用于执行return前跳转到defer开始的位置,在defer的最后也需要新增一条opcode,用于执行完defer后跳回return的位置; 
14+  * (3) 执行阶段:执行时如果发现是return前新增的opcode则跳转到defer开始的位置,同时把return的位置记录下来,执行完defer后再跳回return。 
15+ 
16+ 编译后的opcode指令如下图所示:
17+ 
18+ ![ ] ( ../img/defer.png ) 
19+ 
20+ 1、修改词法解析文件,将defer解析为token:T_DEFER
21+ 2、修改语法解析文件,支持语法:T_DEFER function_call,解析为AST:ZEND_AST_DEFER_CALL
22+ 3、编译ZEND_AST_DEFER_CALL时加入defer栈
23+ 4、编译结束时继续编译defer栈中的function_call,最后编译一条ZEND_JMP,用于跳回return/exit/die的位置
                         You can’t perform that action at this time. 
           
                  
0 commit comments