요약: 여기서 읽는 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('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, 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)를 참고해주세요!