zoukankan      html  css  js  c++  java
  • 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,和我们扩展模块里面定义的函数对应上了。可以说,这里就进入了扩展里面了。

  • 相关阅读:
    bx值
    玲珑杯1147
    Castle
    美团 CodeM 复赛」城市网络
    上海五校赛 密码破解
    上海五校赛 零件组装
    opencv色彩空间
    opencv对图片每个像素操作
    opencv图像的读取和保存以及调用摄像头。
    numpy的logspace产生等比数列
  • 原文地址:https://www.cnblogs.com/yjf512/p/6120856.html
Copyright © 2011-2022 走看看