摘要:这里阅读的php版本为php-7.1.0 rc3,阅读代码的平台为linux。我们研究下反射这个扩展。反射这个扩展目录是存在在:ext/reflection。其实里面的代码很简单。一个.h文件,一个 .c文件。我们先看下.c文件中,会看到很多 ...
这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux。
我们研究下反射这个扩展。
反射这个扩展目录是存在在:ext/reflection。其实里面的代码很简单。一个.h文件,一个 .c文件。
我们先看下.c文件中,会看到很多ZEND_METHOD
立即学习“PHP免费学习笔记(深入)”;
1 ZEND_METHOD(reflection_function, getReturnType)
2 {
3 ...
4 }对应的宏:
1 #define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name)) 2 #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) 3 #define ZEND_MN(name) zim_##name 4 #define INTERNAL_FUNCTION_PARAMETERS zend_execute_data *execute_data, zval *return_value
这里的##代表的是连接,展开实际上就是:
void zim_reflection_function_getReturnType(zend_execute_data *execute_data, zval *return_value)
总而言之,我们这里是使用ZEND_METHOD定义了一个函数zim_reflection_function_getReturnType,那从执行代码是怎么调用到这里的呢?
好吧,所以我们这里是看不到扩展的调用堆栈的。那我们用gdb看下调用堆栈。
写个使用反射扩展的脚本:
1 <?php
2
3 class B
4 {
5 public function test(): B
6 {
7
8 }
9 }
10
11 function getB(): B
12 {
13
14 }
15
16 $rc = new ReflectionMethod('B', 'test');
17 var_dump((string)$rc->getReturnType(), $rc->getReturnType());
18
19 $rc = new ReflectionFunction('getB');
20 var_dump((string)$rc->getReturnType(), $rc->getReturnType());使用gdb进行打点,我们看了下getReturnType的扩展定义,里面有个在扩展代码中的函数reflection_type_factory,就使用这个打点了。
01 (gdb) b reflection_type_factory 02 03 (gdb) run -f /home/xiaoju/software/php7/demo/echo.php 04 05 (gdb) s 06 07 (gdb) bt 08 #0 reflection_type_factory (fptr=0x7ffff6004210, closure_object=0x0, arg_info=0x7ffff6079048, 09 object=0x7ffff60140d0) at /home/xiaoju/webroot/php-src/php-src-master/ext/reflection/php_reflection.c:1280 10 #1 0x0000000000760d23 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER (execute_data=0x7ffff6014030) 11 at /home/xiaoju/webroot/php-src/php-src-master/Zend/zend_vm_execute.h:1097 12 #2 0x000000000073fc88 in execute_ex (ex=<value optimized="" out="">) 13 at /home/xiaoju/webroot/php-src/php-src-master/Zend/zend_vm_execute.h:432 14 #3 0x000000000078b670 in zend_execute (op_array=0x7ffff60782a0, return_value=<value optimized="" out="">) 15 at /home/xiaoju/webroot/php-src/php-src-master/Zend/zend_vm_execute.h:474 16 #4 0x00000000006e48a3 in zend_execute_scripts (type=8, retval=0x0, file_count=3) 17 at /home/xiaoju/webroot/php-src/php-src-master/Zend/zend.c:1464 18 #5 0x0000000000684870 in php_execute_script (primary_file=0x7fffffffe090) 19 at /home/xiaoju/webroot/php-src/php-src-master/main/main.c:2541 20 #6 0x000000000078e9ea in do_cli (argc=3, argv=0xee1bc0) 21 at /home/xiaoju/webroot/php-src/php-src-master/sapi/cli/php_cli.c:994 22 #7 0x000000000078f1ea in main (argc=3, argv=0xee1bc0) 23 at /home/xiaoju/webroot/php-src/php-src-master/sapi/cli/php_cli.c:1387 24 (gdb)</value></value>
好了,很清晰可以看到这个脉络:
main->do_cli->php_execute_scripts->zend_execute->execute_ex->ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER->reflection_type_factory
对于main, do_cli, php_execute_scripts, zend_execute, execute_ex 根据前面的main函数分析,我们很容易能够理解各个函数的作用。换句话说,execute_ex才是实际上调用opcode最终最重要的函数。
对照这个脚本的opcode:
01 L1-21 {main}() /home/xiaoju/software/php7/demo/echo.php - 0x7fd6a127f000 + 30 ops
02 L3 #0 NOP
03 L11 #1 NOP
04 L16 #2 NEW "ReflectionMethod" @1
05 L16 #3 SEND_VAL_EX "B" 1
06 L16 #4 SEND_VAL_EX "test" 2
07 L16 #5 DO_FCALL
08 L16 #6 ASSIGN $rc @1
09 L17 #7 INIT_FCALL 112 "var_dump"
10 L17 #8 INIT_METHOD_CALL $rc "getReturnType"
11 L17 #9 DO_FCALL @4
12 L17 #10 CAST @4 ~5
13 L17 #11 SEND_VAL ~5 1
14 L17 #12 INIT_METHOD_CALL $rc "getReturnType"
15 L17 #13 DO_FCALL @6
16 L17 #14 SEND_VAR @6 2
17 L17 #15 DO_ICALL
18 L19 #16 NEW "ReflectionFunction" @8
19 L19 #17 SEND_VAL_EX "getB" 1
20 L19 #18 DO_FCALL
21 L19 #19 ASSIGN $rc @8
22 L20 #20 INIT_FCALL 112 "var_dump"
23 L20 #21 INIT_METHOD_CALL $rc "getReturnType"
24 L20 #22 DO_FCALL @11
25 L20 #23 CAST @11 ~12
26 L20 #24 SEND_VAL ~12 1
27 L20 #25 INIT_METHOD_CALL $rc "getReturnType"
28 L20 #26 DO_FCALL @13
29 L20 #27 SEND_VAR @13 2
30 L20 #28 DO_ICALL
31 L21 #29 RETURN 1可以看到这个$rc->getReturnType()相对应的opcode是在#9 DO_FCALL
好了,我们从execute_ex开始跟,可以简化成:
01 // 最核心的执行opcode的函数
02 ZEND_API void execute_ex(zend_execute_data *ex)
03 {
04 ...
05 while (1) {
06 int ret;
07 if (UNEXPECTED((ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) != 0)) {
08 ...
09 }
10
11 }
12 ...
13 }这里的handler每个opcode的op对应一个handler,比如 DO_FCALL对应的handler就是ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER(和刚才的bt现显示的堆栈一样)
简化下伪代码如下:
01 // DO_FCALL这个opcode对应的处理函数
02 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
03 {
04 ...
05 if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { // 如果是用户定义的函数
06 ...
07 zend_execute_ex(call);
08 ...
09 } else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) { // 如果是内部函数
10 ...
11 if (!zend_execute_internal) {
12 fbc->internal_function.handler(call, ret); // 执行这个internal_function所定义的handler函数,这个就是实际的调用方法了,命名为:zim_[class]_function_[function]
13 } else {
14 zend_execute_internal(call, ret);
15 }
16 ...
17
18 } else { /* ZEND_OVERLOADED_FUNCTION */
19 ...
20 if (UNEXPECTED(!zend_do_fcall_overloaded(fbc, call, ret))) {
21 HANDLE_EXCEPTION();
22 }
23 ...
24 }
25
26 fcall_end:
27 ...
28 ZEND_VM_SET_OPCODE(opline + 1);
29 ZEND_VM_CONTINUE(); // 下一条op
30 }可以看到,这个函数里面就有一个fbc->internal_function.handler,这里的internal_function对应的函数名就是zim_reflection_function_getReturnType,和我们扩展模块里面定义的函数对应上了。可以说,这里就进入了扩展里面了。
以上就是php内核分析(七)-扩展的内容,更多相关内容请关注PHP中文网(www.php.cn)!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号