什么是opcode

当Zend Engine解析PHP脚本的时候,会对脚本进行词法、语法分析,然后编译成opcode来执行,类似JVM中的字节码(byte codes),只不过opcode不会像class文件那种存在磁盘,而是在内存中直到PHP的生命周期结束。

盗一张图:

Untitled

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优化扩展都是用这种方式来实现的。

读懂opcode

下面我们用vld生成一段opcode看看。PHP脚本如下:

<?php
function hello($who) {
    return sprintf("Hello, %s!", $who);
}

echo hello('World');

执行vld,输出:

Powered by Fruition