我们有如下脚本:
1 2 3 4 5
| <?php
for ($i = 0; $i < 10; $i = $i + 1) { echo $i; }
|
对应的opcode
为:
1 2 3 4 5 6 7 8
| L3 L3 L4 L3 L3 L3 L3 L6
|
首先,我们把for
语句的组成部分说一下:
1
| T_FOR '(' init_exprs ';' cond_exprs ';' loop_exprs ')' for_statement
|
对应:
1 2 3 4 5
| T_FOR => for init_exprs => $i = 0 cond_exprs => $i < 10 loop_exprs => $i = $i + 1 for_statement => echo $i;
|
因为,这些opcode
是没有经过任何优化的,所以,我们看到的opcode
顺序就是PHP
解释器去编译的顺序。
OK
,我们来从编译出来的opcode
总结出编译for
语句的一般规律。
首先是:
这对应着我们的init_exprs
。
接着是:
没有对应的代码。
接着是:
对应着我们的for_statement
。
接着是:
对应着我们的loop_exprs
。
接着是:
对应着我们的cond_exprs
。
最后是:
没有对应的代码。
我们可以得到如下图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| ┌──────────────────────────────┐ │ │ │ init_exprs │ │ │ └──────────────────────────────┘ │ │ ▼ ┌──────────────────────────────┐ │ │ ┌──│ JMP │◀─┐ │ │ │ │ │ └──────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────┐ │ │ │ │ │ │ │ for_statement │ │ │ │ │ │ │ └──────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────┐ │ │ │ │ │ │ │ loop_exprs │ │ │ │ │ │ │ └──────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────┐ │ │ │ │ │ └─▶│ cond_exprs │ │ │ │ │ └──────────────────────────────┘ │ │ │ │ │ │ │ ▼ │ ┌──────────────────────────────┐ │ │ │ │ │ JMPNZ │──┘ │ │ └──────────────────────────────┘
|
这实际上,就是通过if
和goto
来实现for
循环。
我们在写编译器的时候,就可以借鉴这个模型。