本文基于 PHP8.0.1
测试脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12
| <?php
namespace Bar;
function Foo1(string $arg1) { }
function foo2(string $arg2) { }
Foo1('aa'); foo2('bb');
|
流程如下:
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| +----------------------------------------+ | | | zend_compile_top_stmt | | | +----------------------------------------+ | | | ast->kind == ZEND_AST_FUNC_DECL | | v +----------------------------------------+ | zend_compile_func_decl | | | | start compile function | +----------------------------------------+ | | | v +----------------------------------------+ | | | init_op_array | | | +----------------------------------------+ | | v +----------------------------------------------+ | | | | | zend_begin_func_decl | | | | 1. convert function from unqualified_name to | | namespace_name (it means Foo1 and function | | op_array to Bar\Foo1) | | 2. insert bar\foo1 to CG(function_table) | | | | | +----------------------------------------------+ | | | v +----------------------------------------+ | | | zend_compile_params | | | +----------------------------------------+ | | | | v +----------------------------------------+ | | | zend_compile_stmt stmt_ast | | | +----------------------------------------+ | | | v +----------------------------------------+ | | | zend_compile_stmt stmt_ast | | | +----------------------------------------+ | | | v +----------------------------------------+ | zend_emit_final_return(0) | | | | add return null | +----------------------------------------+ | | | v +----------------------------------------+ | | | pass_two | | | +----------------------------------------+
|
对应的主函数常量存储的内容如下:
1 2 3 4 5 6 7 8 9
| 0: Bar\Foo1 1: bar\foo1 2: foo1 3: aa 4: Bar\foo2 5: bar\foo2 6: foo2 7: bb 8: 1
|
说明,在常量表里面,既存了函数原来的名字,也存了函数的全小写名字。
opline
如果要用到函数的名字,那么偏移量存的是函数原来的名字。但是,在查找函数的时候,需要用小写的名字,因为CG(function_table)
里面存的是全小写的名字(目的是为了让PHP脚本的函数不区分大小写)。所以,如果opline
需要通过函数名字来查找zend_function
,那么要对opline
引用的zval *literal
偏移量+1
。
那如果使用了命名参数,那么常量区还会存储参数的名字,例如:
1 2 3 4 5 6 7 8 9 10 11 12
| <?php
namespace Bar;
function Foo1(string $arg1) { }
function foo2(string $arg2) { }
Foo1(arg1: 'aa'); foo2(arg2: 'bb');
|
对应的主函数常量存储的内容如下:
1 2 3 4 5 6 7 8 9 10 11
| 0: Bar\Foo1 1: bar\foo1 2: foo1 3: aa 4: arg1 5: Bar\foo2 6: bar\foo2 7: foo2 8: bb 9: arg2 10: 1
|