好好学习,天天向上,自学学习网欢迎您!
当前位置:首页 > > 考试 > > 计算机类 > 内容页

php内核分析之扩展

2021-04-22 12:08:29计算机类访问手机版284

  php内核分析之扩展

  以下是小编精心为大家整理的关于php内核扩展方面的分析,欢迎阅读参考!更多内容请关注应届毕业生网!

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

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

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

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

  ZEND_METHODreflection_function, getReturnType

  ...

  对应的宏:

  #define ZEND_METHODclassname, name ZEND_NAMED_FUNCTIONZEND_MNclassname##_##name

  #define ZEND_NAMED_FUNCTIONname void nameINTERNAL_FUNCTION_PARAMETERS

  #define ZEND_MNname zim_##name

  #define INTERNAL_FUNCTION_PARAMETERS zend_execute_data *execute_data, zval *return_value

  这里的##代表的是连接,展开实际上就是:

  void zim_reflection_function_getReturnTypezend_execute_data *execute_data, zval *return_value

  总而言之,我们这里是使用ZEND_METHOD定义了一个函数zim_reflection_function_getReturnType,那从执行代码是怎么调用到这里的呢?

  好吧,所以我们这里是看不到扩展的.调用堆栈的。那我们用gdb看下调用堆栈。

  写个使用反射扩展的脚本:

  1

  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_dumpstring$rc->getReturnType, $rc->getReturnType;

  18

  19 $rc = new ReflectionFunction'getB';

  20 var_dumpstring$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=

  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=

  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_exzend_execute_data *ex

  ...

  while 1

  int ret;

  if UNEXPECTEDret = opcode_handler_tOPLINE->handlerZEND_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_HANDLERZEND_OPCODE_HANDLER_ARGS

  ...

  if EXPECTEDfbc->type == ZEND_USER_FUNCTION // 如果是用户定义的函数

  ...

  zend_execute_excall;

  ...

  else if EXPECTEDfbc->type < ZEND_USER_FUNCTION // 如果是内部函数

  ...

  if !zend_execute_internal

  fbc->internal_function.handlercall, ret; // 执行这个internal_function所定义的handler函数,这个就是实际的调用方法了,命名为:zim_[class]_function_[function]

  else

  zend_execute_internalcall, ret;

  ...

  else /* ZEND_OVERLOADED_FUNCTION */

  ...

  if UNEXPECTED!zend_do_fcall_overloadedfbc, call, ret

  HANDLE_EXCEPTION;

  ...

  fcall_end:

  ...

  ZEND_VM_SET_OPCODEopline + 1;

  ZEND_VM_CONTINUE; // 下一条op

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

TAG标签: