>  기사  >  php教程  >  PHP 커널 분석(7) - 확장

PHP 커널 분석(7) - 확장

黄舟
黄舟원래의
2016-12-19 11:28:571126검색

요약: 여기서 읽는 PHP 버전은 PHP-7.1.0 RC3이고, 코드를 읽는 플랫폼은 Linux입니다. 이러한 성찰의 확장을 연구해 봅시다. 리플렉션 확장 디렉터리는 ext/reflection에 있습니다. 실제로 내부 코드는 매우 간단합니다. .h 파일 하나와 .c 파일 하나. 먼저 .c 파일을 보시면 많이 보이실 겁니다...

여기서 읽는 php 버전은 PHP-7.1.0 RC3이고, 코드를 읽는 플랫폼은 리눅스입니다.

반영 확장에 대해 연구해 봅시다.

리플렉션 확장 디렉터리는 ext/reflection에 있습니다. 실제로 내부 코드는 매우 간단합니다. .h 파일 하나와 .c 파일 하나.

먼저 .c 파일을 살펴보겠습니다. ZEND_METHOD

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(&#39;B&#39;, &#39;test&#39;);
  17 var_dump((string)$rc->getReturnType(), $rc->getReturnType());
  18
  19 $rc = new ReflectionFunction(&#39;getB&#39;);
  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, excute_ex의 경우 쉽게 이해할 수 있습니다. 각 기능의 역할. 즉, 실제로 opcode를 호출하는 가장 중요한 함수는 Execute_ex입니다.

이 스크립트의 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    }

여기서 핸들러는 DO_FCALL과 같은 각 opcode 작업에 대한 핸들러에 해당합니다. 핸들러는 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    }

As 보시다시피 함수에는 fbc->internal_function.handler가 있습니다. 여기서 Internal_function에 해당하는 함수 이름은 zim_reflection_function_getReturnType이며 이는 확장 모듈에 정의된 함수에 해당합니다. 여기서 우리는 확장에 들어갔다고 말할 수 있습니다.

위 내용은 PHP Kernel Analysis(7) - Extension 내용입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.