当Zend Engine解析PHP脚本的时候,会对脚本进行词法、语法分析,然后编译成opcode来执行,类似JVM中的字节码(byte codes),只不过opcode不会像class文件那种存在磁盘,而是在内存中直到PHP的生命周期结束。
盗一张图:
opcode在PHP内核中是如何生成的可以参考 : http://www.php-internals.com/book/?p=chapt02/02-03-02-opcode
我们可以通过PHP扩展vld来查看PHP脚本的opcode,可以参考(http://blog.csdn.net/21aspnet/article/details/7002644)。%E3%80%82)
Zend Engine中编译和执行PHP脚本的关键函数是:
ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
ZEND_API void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);
VLD就是通过HOOK Zend Engine中的这两个函数来实现dump opcode,来看看它的代码:
PHP_RINIT_FUNCTION(vld){
old_compile_file = zend_compile_file;
#if (PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2)
old_compile_string = zend_compile_string;
#endif
old_execute = zend_execute;
if (VLD_G(active)) {
zend_compile_file = vld_compile_file;
#if (PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2)
zend_compile_string = vld_compile_string;
#endif
if (!VLD_G(execute)) {
zend_execute = vld_execute;
}
}
}
在vld_compile_file中完成HOOK:
static zend_op_array *vld_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC){
...
op_array = old_compile_file (file_handle, type TSRMLS_CC);
...
return op_array;
}
获取opcode后将其格式化输出:
op_array = old_compile_file (file_handle, type TSRMLS_CC);
if (op_array) {
vld_dump_oparray (op_array TSRMLS_CC); //格式化输出函数
}
其实APC、Opcache等opcode优化扩展都是用这种方式来实现的。
下面我们用vld生成一段opcode看看。PHP脚本如下:
<?php
function hello($who) {
return sprintf("Hello, %s!", $who);
}
echo hello('World');
执行vld,输出: