php内核分析(七)-扩展

简介:

这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux。

我们研究下反射这个扩展。

反射这个扩展目录是存在在:ext/reflection。其实里面的代码很简单。一个.h文件,一个 .c文件。

我们先看下.c文件中,会看到很多ZEND_METHOD

ZEND_METHOD(reflection_function, getReturnType)
{
     ...
}

对应的宏:

#define ZEND_METHOD(classname, name)     ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))
#define ZEND_NAMED_FUNCTION(name)          void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_MN(name) zim_##name
#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,就使用这个打点了。

(gdb) b reflection_type_factory

(gdb) run -f /home/xiaoju/software/php7/demo/echo.php

(gdb) s

(gdb) bt
#0  reflection_type_factory (fptr=0x7ffff6004210, closure_object=0x0, arg_info=0x7ffff6079048,
    object=0x7ffff60140d0) at /home/xiaoju/webroot/php-src/php-src-master/ext/reflection/php_reflection.c:1280
#1  0x0000000000760d23 in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER (execute_data=0x7ffff6014030)
    at /home/xiaoju/webroot/php-src/php-src-master/Zend/zend_vm_execute.h:1097
#2  0x000000000073fc88 in execute_ex (ex=<value optimized out>)
    at /home/xiaoju/webroot/php-src/php-src-master/Zend/zend_vm_execute.h:432
#3  0x000000000078b670 in zend_execute (op_array=0x7ffff60782a0, return_value=<value optimized out>)
    at /home/xiaoju/webroot/php-src/php-src-master/Zend/zend_vm_execute.h:474
#4  0x00000000006e48a3 in zend_execute_scripts (type=8, retval=0x0, file_count=3)
    at /home/xiaoju/webroot/php-src/php-src-master/Zend/zend.c:1464
#5  0x0000000000684870 in php_execute_script (primary_file=0x7fffffffe090)
    at /home/xiaoju/webroot/php-src/php-src-master/main/main.c:2541
#6  0x000000000078e9ea in do_cli (argc=3, argv=0xee1bc0)
    at /home/xiaoju/webroot/php-src/php-src-master/sapi/cli/php_cli.c:994
#7  0x000000000078f1ea in main (argc=3, argv=0xee1bc0)
    at /home/xiaoju/webroot/php-src/php-src-master/sapi/cli/php_cli.c:1387
(gdb)

好了,很清晰可以看到这个脉络:

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:

L1-21 {main}() /home/xiaoju/software/php7/demo/echo.php - 0x7fd6a127f000 + 30 ops
L3    #0     NOP
L11   #1     NOP
L16   #2     NEW                     "ReflectionMethod"                        @1
L16   #3     SEND_VAL_EX             "B"                  1
L16   #4     SEND_VAL_EX             "test"               2
L16   #5     DO_FCALL
L16   #6     ASSIGN                  $rc                  @1
L17   #7     INIT_FCALL              112                  "var_dump"
L17   #8     INIT_METHOD_CALL        $rc                  "getReturnType"
L17   #9     DO_FCALL                                                          @4
L17   #10    CAST                    @4                                        ~5
L17   #11    SEND_VAL                ~5                   1
L17   #12    INIT_METHOD_CALL        $rc                  "getReturnType"
L17   #13    DO_FCALL                                                          @6
L17   #14    SEND_VAR                @6                   2
L17   #15    DO_ICALL
L19   #16    NEW                     "ReflectionFunction"                      @8
L19   #17    SEND_VAL_EX             "getB"               1
L19   #18    DO_FCALL
L19   #19    ASSIGN                  $rc                  @8
L20   #20    INIT_FCALL              112                  "var_dump"
L20   #21    INIT_METHOD_CALL        $rc                  "getReturnType"
L20   #22    DO_FCALL                                                          @11
L20   #23    CAST                    @11                                       ~12
L20   #24    SEND_VAL                ~12                  1
L20   #25    INIT_METHOD_CALL        $rc                  "getReturnType"
L20   #26    DO_FCALL                                                          @13
L20   #27    SEND_VAR                @13                  2
L20   #28    DO_ICALL
L21   #29    RETURN                  1

可以看到这个$rc->getReturnType()相对应的opcode是在#9 DO_FCALL

好了,我们从execute_ex开始跟,可以简化成:

// 最核心的执行opcode的函数
ZEND_API void execute_ex(zend_execute_data *ex)
{
     ...
     while (1) {
          int ret;
          if (UNEXPECTED((ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) != 0)) {
              ...
          }

     }
    ...
}

这里的handler每个opcode的op对应一个handler,比如 DO_FCALL对应的handler就是ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER(和刚才的bt现显示的堆栈一样)

简化下伪代码如下:

// DO_FCALL这个opcode对应的处理函数
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
     ...
     if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {  // 如果是用户定义的函数
          ...
        zend_execute_ex(call);
        ...
     } else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) { // 如果是内部函数
          ...
          if (!zend_execute_internal) {
               fbc->internal_function.handler(call, ret);  // 执行这个internal_function所定义的handler函数,这个就是实际的调用方法了,命名为:zim_[class]_function_[function]
          } else {
               zend_execute_internal(call, ret);
          }
        ...

     } else { /* ZEND_OVERLOADED_FUNCTION */
          ...
          if (UNEXPECTED(!zend_do_fcall_overloaded(fbc, call, ret))) {
               HANDLE_EXCEPTION();
          }
        ...
     }

fcall_end:
     ...
     ZEND_VM_SET_OPCODE(opline + 1);
     ZEND_VM_CONTINUE(); // 下一条op
}

可以看到,这个函数里面就有一个fbc->internal_function.handler,这里的internal_function对应的函数名就是zim_reflection_function_getReturnType,和我们扩展模块里面定义的函数对应上了。可以说,这里就进入了扩展里面了。


本文转自轩脉刃博客园博客,原文链接:http://www.cnblogs.com/yjf512/p/6120856.html,如需转载请自行联系原作者

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
1月前
|
缓存 自然语言处理 PHP
深入PHP内核:理解OPcode和执行生命周期
在PHP的执行过程中,源代码经过词法分析、语法分析等一系列复杂的步骤后,最终会编译成一种叫做“操作码”(OPcode)的中间代码。本文将深入探讨PHP的操作码(OPcode)是什么,它是如何生成的,以及它在整个PHP脚本执行周期中扮演的角色。我们还将了解不同的OPcode优化技术,以及它们如何影响PHP应用程序的性能。通过本文,读者将对PHP的内部工作原理有更深刻的理解,为进一步的性能调优打下坚实的基础。
|
1月前
|
数据采集 前端开发 PHP
深入PHP内核:探索高性能异步编程
【2月更文挑战第24天】在现代Web开发中,提供快速响应和高并发处理能力是至关重要的。传统的同步阻塞模型已无法满足现代互联网服务的需求。本文将深入探讨PHP语言中的异步编程概念,介绍其原理、实现方式以及如何通过异步编程提升应用性能。我们将剖析PHP的多线程扩展,如pthreads,以及事件驱动编程模型,重点关注它们在处理大量并发连接时的优势和潜在问题。此外,文中还将展示如何使用Swoole等高级框架来构建高效的异步应用。最后,我们将讨论异步编程在实际应用中的挑战与未来发展趋势。
16 1
|
2月前
|
运维 Unix Linux
Linux系统 PHP安装expect扩展详解
Linux系统 PHP安装expect扩展详解
39 5
|
15天前
|
NoSQL Linux PHP
php添加redis扩展 linux和windos图文详解 l
php添加redis扩展 linux和windos图文详解 l
3 0
|
1月前
|
数据采集 存储 JavaScript
PHP爬虫技术:利用simple_html_dom库分析汽车之家电动车参数
本文旨在介绍如何利用PHP中的simple_html_dom库结合爬虫代理IP技术来高效采集和分析汽车之家网站的电动车参数。通过实际示例和详细说明,读者将了解如何实现数据分析和爬虫技术的结合应用,从而更好地理解和应用相关技术。
PHP爬虫技术:利用simple_html_dom库分析汽车之家电动车参数
|
3月前
|
PHP Windows
php扩展com_dndnet(PHP与.NET框架进行交互)
php扩展com_dndnet(PHP与.NET框架进行交互)
php扩展com_dndnet(PHP与.NET框架进行交互)
|
4月前
|
NoSQL Linux PHP
PHP 扩展开发初探
PHP 扩展开发初探
49 0