grape
全部视频:https://segmentfault.com/a/11…
引入
在我们平常写 PHP 代码总是会用到 while 语句,那么我们有没有去考虑过 while 语句是怎么实现的呢?
我们来看下面的这段代码:
<?php
$a = 1;
while($a){}
我们知道这段代码是个死循环,毋庸置疑,但是在 PHP 中,它是如何做到的呢?请看下文。
分析
首先,我们自己分析一下这个 while 语句,如果让我们来设计,我们会如何设计呢?笔者写出脑补几点:
- PHP 是 C 语言编写的,直接去沿用 C 的语法,while 套用。
- 在源码设计中加入 goto 语句。
- for 循环嵌套 if 语句
- 等等(大家可以头脑风暴下)
接下来我们看一下 PHP 源码中是如何实现的。
俗话说,实践是检验真理的唯一道路,那么我们就去 gdb 一下,调试上边的代码,用事实说话。
接下来是 gdb 的过程,因为是 ast 树的构建过程,之前文章已经详细解释过,此处不再赘述,文章传送门:2019-05-07 发布【PHP 源码学习】2019-03-21 AST,gdb 过程如图 1 所示:
图 1
此时我们可以知道 AST 就是长的图二的样子,如图 2:
图 2
ast 树建立好了,但是他执行了什么指令,我们还是得去看看他的 opcode 是什么,继续 gdb 代码,执行到 zend_file_context_end 之后,此时 opcode 已经执行完成,我们打印下 gdb 结果,如图 3 所示:
图 3
ps:在图三中我们可以看到有四条指令,对应下分别为:ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER ,ZEND_JMP_SPEC_HANDLER,ZEND_JMPNZ_SPEC_CV_HANDLER,ZEND_RETURN_SPEC_CONST_HANDLER,由此我们可以发现 while 语句的实现由 ZEND_JMP_SPEC_HANDLER,ZEND_JMPNZ_SPEC_CV_HANDLER 来实现,学过汇编的同学应该知道,jmp 是跳转的意思,jumpnz 则是不为 0 跳转。由此我们可以画出 while 的执行流程,如图 4 所示:
图 4
至此我们可以得到 while 的执行流程了,当然如果想知道这些是如何进行运作的,建议大家去看一看 pass_two 这个函数,里边有如何设置 opcode 的。
结论
while 语句并不是嵌套 C 语言的 while 语句,也不是我们之前的种种猜测,他的核心是通过 jump,jumpnz 来进行控制的。
ps: 设计者可真是个天才。:手动滑稽
延伸
如果说明白了 while 语句,那么 do_while 又是怎么一回事呢?大家可以自行思考一下