摘要:這裡閱讀的php版本為PHP-7.1.0 RC3,閱讀程式碼的平台為linux。我們研究下反射這個擴展。反射這個擴充目錄是存在在:ext/reflection。其實裡面的程式碼很簡單。一個.h文件,一個 .c文件。我們先看下.c檔中,會看到很多 ...
這裡閱讀的php版本為PHP-7.1.0 RC3,閱讀程式碼的平台為linux。
我們研究下反射這個擴展。
反射這個擴充目錄是存在在: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_METD定義了一個函數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, execute_ex 根據前面的main函數分析,我們很容易能夠理解各個函數的作用。換句話說,execute_ex才是實際上呼叫opcode最終最重要的函數。
對照這個腳本的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 }這裡的handler每個opcode的op對應一個handler,例如DO_FCALL對應的handler就是ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER(和剛才的bt現顯示的堆疊一樣)
到,這個函數裡面就有一個fbc->internal_function.handler,這裡的internal_function對應的函式名稱就是zim_reflection_function_getReturnType,和我們擴充模組裡面定義的函式對應上了。可以說,這裡就進入擴充裡面了了。
以上就是php核心分析(七)-擴充的內容,更多相關內容請關注PHP中文網(www.php.cn)!

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php判断有没有小数点的方法:1、使用“strpos(数字字符串,'.')”语法,如果返回小数点在字符串中第一次出现的位置,则有小数点;2、使用“strrpos(数字字符串,'.')”语句,如果返回小数点在字符串中最后一次出现的位置,则有。

方法:1、用“str_replace(" ","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\ \;||\xc2\xa0)/","其他字符",$str)”语句。

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

查找方法:1、用strpos(),语法“strpos("字符串值","查找子串")+1”;2、用stripos(),语法“strpos("字符串值","查找子串")+1”。因为字符串是从0开始计数的,因此两个函数获取的位置需要进行加1处理。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

Dreamweaver Mac版
視覺化網頁開發工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

禪工作室 13.0.1
強大的PHP整合開發環境