php內核分析之擴展
以下是本站小編精心為大家整理的關於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
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/
(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/ - 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_ler(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_ler,這裏的internal_function對應的函數名就是zim_reflection_function_getReturnType,和我們擴展模塊裏面定義的函數對應上了。可以説,這裏就進入了擴展裏面了。
-
tp搜索時首頁分頁和搜索頁保持條件分頁的方法
在做搜索查詢時突然發現在首頁用的`分頁代碼在搜索頁使用時出現錯誤,首頁分頁代碼(代碼中標註start與end部分為分頁代碼),下面是由本站小編為大家整理的Thinkphp搜索時首頁分頁和搜索頁保持條件分頁的方法,喜歡的可以收藏一下!瞭解更多詳情資訊,請關注應屆畢業生...
-
零基礎學習PHP應該怎麼學
在網上可以看到很多學習php語言的方法,鮮有適合零基礎自學的,當然你要有其他主流語言的從業經驗就另説了,比如你從事java開發3年,python3年之類的,並且英語已經過了6級,閲讀英文書籍和文檔沒什麼壓力,可以按照那些方式學一下。但是隻是感興趣想要學習php語言,不如看看...
-
PHP工程師應該掌握的知識結構
作為一名合格的PHP工程師的知識結構是怎樣的?下面是本站小編精心為大家整理的PHP工程師應該掌握的知識結構,希望對大家有幫助,更多內容請關注應屆畢業生網!一x方面Linux常用命令1.文件處理命令2.權限管理命令3.幫助命令4.文件搜索命令5.壓縮解壓命令6.命令使用技...
-
PHP開發環境安裝的方法
PHP是一種HTML內嵌式的語言,是一種在服務器端執行的嵌入HTML文檔的腳本語言,語言的風格有類似於C語言,被廣泛地運用。以下是小編為大家搜索整理的PHP開發環境安裝的方法,歡迎閲讀!更多精彩內容請及時關注我們應屆畢業生考試網!一、PHP簡介PHP於1994年由RasmusLerdo...