一直以来,横观国内的PHP现状,很少有专门介绍PHP内部机制的书。呵呵,我会随时记录下研究的心得,有PHP
一直以来,横观国内的PHP现状,很少有专门介绍PHP内部机制的书。呵呵,我会随时记录下研究的心得,有机会的时候,汇总成书。:)
今天这篇,我内心是想打算做为一个导论:
PHP是一个被广泛应用的脚本语言,因为它的成功,所以很多时候,我们应用PHP的时候是更不不需要考虑底层到底是怎么实现的。我相信大多数的 PHP程序 员是不会去考虑这一点的。从我接触PHP开始,到今天也就是3年,这三年里,前俩年我一直都是在”用”PHP,每次写出来一段脚本,我就会想“恩,不用担 心,PHP解释器会知道我想做什么的”,直到去年来到雅虎,接受了一个工作,是做一个PHP的Extension,从这个时候开始,我就好奇于新接触的一 大堆的新鲜事物,zend, TSRM, zval, hashtable, op_array…
于是我到处查阅资料,每次获得一篇好的文章,或者一段好的文字我就会如获珍宝,打印保存起来,细细研读。我发现,国内关于PHP内部的资料真是少的 可怜, 不知道是因为懂得的人多但是不愿意分享,还是懂得的人本来就少,所以,这条路,我走的很辛苦。于是,就会有了这篇文章。
在这篇文章中,我会从整个PHP的执行期入手,大致的介绍下各个阶段,词法分析,语法分析,op code等等,以后的文章我会再详细介绍每个阶(当然,如果你急不可耐的想知道详细,呵呵,那么可以直接联系我)。
从最初我们编写的PHP脚本->到最后脚本被执行->得到执行结果,这个过程,其实可以分为如下几个阶段(鄙视:CSDN不能上图):
首先,Zend Engine(ZE),调用词法分析器(Lex生成的,源文件在 Zend/zend_language_sanner.l), 将我们要执行的PHP源文件,去掉空格 ,注释,分割成一个一个的token。
然后,ZE会将得到的token forward给语法分析器(yacc生成, 源文件在 Zend/zend_language_parser.y),生成一个一个的op code,opcode一般会以op array的形式存在,它是PHP执行的中间语言。
最后,ZE调用zend_executor来执行op array,输出结果。
ZE是一个虚拟机,正是由于它的存在,所以才能使得我们写PHP脚本,完全不需要考虑所在的操作系统类型是什么。ZE是一个CISC(复杂指令处理器), 它支持150条指令(具体指令在 Zend/zend_vm_opcodes.h),包括从最简单的ZEND_ECHO(echo)到复杂的 ZEND_INCLUDE_OR_EVAL(include,require),所有我们编写的PHP都会最终被处理为这150条指令(op code)的序列,从而最终被执行。
那有什么办法可以看到我们的PHP脚本,最终被“翻译”成什么样的呢? 也就是说,op code张的什么样子呢? 呵呵,达到这个,我们需要重新编译PHP,修改它的compile_file和zend_execute函数。不过,在PECL中已经有这样的模块,可以 让我们直接使用了,那就是由 Derick Rethans开发的VLD (Vulcan Logic Dissassembler)模块。你只要下载这个模块,并把他载入PHP中,就可以通过简单的设置,来得到脚本翻译的结果了。具体关于这个模块的使用说 明-雅虎一下,你就知道^_^。
接下来,让我们尝试用VLD来查看一段简单的PHP脚本的中间语言。
原始代码:
<?php $i = “ This is a string “ ; //I am comments echo $i . ‘ that has been echoed to screen ‘ ; ?>
采用VLD得到的op codes:
filename :/ home / Desktop / vldOutOne . php function name: (null) number of ops: 7 line # op fetch ext operands
——————————————————————————————————————————-
2 0 FETCH_W local $ 0 , ‘ i ‘ 1 ASSIGN $ 0 , ‘ This+is+a+string ‘ 4 2 FETCH_R local $ 2 , ‘ i ‘ 3 CONCAT ~ 3 , $ 2 , ‘+that+has+been+echoed+to+screen‘ 4 ECHO ~3 6 5 RETURN 1 6 ZEND_HANDLE_EXCEPTION
我们可以看到,源文件中的注释,在op code中,已经没有了,所以不用担心注释太多会影响你的脚本执行时间(实际上,它是会影响ZE的词法处理阶段的用时而已)。
现在我们来一条一条的分析这段op codes,每一条op code 又叫做一条op_line,都由如下7个部分,在zend_compile.h中,我们可以看到如下定义:
struct _zend_op { opcode_handler_t handler ; znode result ; znode op1 ; znode op2 ; ulong extended_value ; uint lineno ; zend_uchar opcode ; } ;
其中,opcode字段指明了这操作类型,handler指明了处理器,然后有俩个操作数,和一个操作结果。
可以看出,这个很类似于很多同学大学学习编译原理时候的三元式,不同的是,这些中间代码会被Zend VM(Zend虚拟机)直接执行。
真正负责执行的函数是,zend_execute, 查看zend_execute.h:
ZEND_API extern
void
(*zend_execute)(zend_op_array
*op_array
TSRMLS_DC);
可以看出, zend_execute接受zend_op_array*作为参数。
可以看到,zend_op_array的结构和zend_function的结构很像(参看我的其他文章), 对于在全局作用域的代码,就是不包含在任何function内的op_array,它的function_name为NULL。结构中的opcodes保 存了属于这个op_array的op code数组,zend_execute会从start_op开始,逐条解释执行传入的每条op code, 从而实现我们PHP脚本想要的结果。
下一次,我将介绍PHP变量的灵魂 – zval, 你将会看到PHP是如何实现它的变量传递,类型戏法,等等。
本文由来源 21aspnet,由 javajgs_com 整理编辑,其版权均为 21aspnet 所有,文章内容系作者个人观点,不代表 Java架构师必看 对观点赞同或支持。如需转载,请注明文章来源。