摘要:擴展開發者可以通過實現自定義的功能,通過擴展嵌入到中。就是這個擴展注冊的函數數組。
PHP內核架構
SAPI是PHP的最上層,它是PHP的應用接口層,對于源碼目錄為sapi
main是PHP的主要代碼,主要是輸入/輸出,Web通信,以及PHP框架的初始化操作,對于源碼目錄為main
ZendVM是PHP解釋器的主要實現,即ZendVM,對于源碼目錄為Zend
截一張php-src的圖,目錄都有對應
PHP的生命周期
PHP根據不同SAPI的實現,各階段的執行情況有些差異。譬如cli模式的話,完整地經歷了這些階段,而Fastcgi模式下則在啟動時執行一次模塊初始化,然后各個請求只經歷請求初始化,執行請求腳本,請求關閉這幾個階段。
開發者可以通過C/C++實現自定義的功能,通過擴展嵌入到PHP中。
編寫擴展的步驟:
通過ext目錄下ext_skel腳本生成擴展的基本框架./ext_skel --extname=module (module is the name of your extension)
修改config.m4配置:設置編譯配置參數、設置擴展源文件
編寫擴展源代碼
生成configure:寫完后先phpize(在php的bin目錄下)運行一下
編譯&安裝: ./configure、 make、make install,然后改一下php.ini文件,添加一下.so文件
舉例操作系統:CentOS Linux release 7.3.1611
PHP版本:PHP 7.1.11
./ext_skel --extname=my_test --no-help
--no-help是略去注釋代碼(干凈點)
生成目錄my_test:
/* $Salamander$ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_my_test.h" static int le_my_test; PHP_MINIT_FUNCTION(my_test) { return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(my_test) { return SUCCESS; } PHP_RINIT_FUNCTION(my_test) { #if defined(COMPILE_DL_MY_TEST) && defined(ZTS) ZEND_TSRMLS_CACHE_UPDATE(); #endif return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(my_test) { return SUCCESS; } PHP_MINFO_FUNCTION(my_test) { php_info_print_table_start(); php_info_print_table_header(2, "my_test support", "enabled"); php_info_print_table_end(); } const zend_function_entry my_test_functions[] = { PHP_FE_END }; zend_module_entry my_test_module_entry = { STANDARD_MODULE_HEADER, "my_test", my_test_functions, PHP_MINIT(my_test), PHP_MSHUTDOWN(my_test), PHP_RINIT(my_test), PHP_RSHUTDOWN(my_test), PHP_MINFO(my_test), PHP_MY_TEST_VERSION, STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_MY_TEST #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() #endif ZEND_GET_MODULE(my_test) #endif
可以注意到這里有一些宏
PHP_MINIT_FUNCTION
PHP_MSHUTDOWN_FUNCTION
PHP_RINIT_FUNCTION
PHP_RSHUTDOWN_FUNCTION
PHP_MINFO_FUNCTION
這些是PHP提供的鉤子函數,PHP執行到不同的階段時回調各個擴展定義的鉤子函數,定義完成后,最后設置一下zend_module_entry對應的函數指針即可。
回顧之前的PHP的生命周期,也就是說(=>指對應某個階段):
PHP_MINIT_FUNCTION => 模塊初始化階段(M就是module的含義,init就是initial)
PHP_MSHUTDOWN_FUNCTION => 模塊關閉階段(M就是module的含義,后面就是shutdown)
PHP_RINIT_FUNCTION => 請求初始化(R就是request的含義,init就是initial)
PHP_RSHUTDOWN_FUNCTION => 請求關閉階段(R就是request的含義,后面就是shutdown)
PHP_MINFO_FUNCTION 指獲取模塊信息
最后,設置zend_module_entry這個結構體
zend_module_entry my_test_module_entry = { STANDARD_MODULE_HEADER, "my_test", my_test_functions, PHP_MINIT(my_test), PHP_MSHUTDOWN(my_test), PHP_RINIT(my_test), PHP_RSHUTDOWN(my_test), PHP_MINFO(my_test), PHP_MY_TEST_VERSION, STANDARD_MODULE_PROPERTIES };
獲取各個鉤子函數的指針,有對對應的宏PHP_MINIT,PHP_MSHUTDOWN,PHP_RINIT,PHP_RSHUTDOWN,PHP_MINFO
注冊函數分為兩步:
定義函數,可以通過PHP_FUNCTION()或ZEND_FUNCTION()宏來完成函數聲明
注冊函數,PHP提供了zend_function_entry,擴展只需為每個內部函數生成這樣一個結構,然后將所有函數的結構數組提供給zend_module_entry->functions即可
For Example:
PHP_FUNCTION(my_func) { // 具體實現 }
展開后
void zif_my_func(zend_execute_data *execute_data, zval *return_value) { // ... }
zend_function_entry可以通過宏PHP_FE或ZEND_FE生成(FE即function entry)。
const zend_function_entry my_test_functions[] = { PHP_FE(my_func, NULL) PHP_FE_END };
my_test_functions就是這個擴展注冊的函數數組。
最后,它設置在了zend_module_entry(第三個參數)
zend_module_entry my_test_module_entry = { STANDARD_MODULE_HEADER, "my_test", my_test_functions, PHP_MINIT(my_test), PHP_MSHUTDOWN(my_test), PHP_RINIT(my_test), PHP_RSHUTDOWN(my_test), PHP_MINFO(my_test), PHP_MY_TEST_VERSION, STANDARD_MODULE_PROPERTIES };函數參數解析
PHP提供了一個方法將zend_execute_data上的參數解析到指定變量上。
//file: Zend/zend_API.h ZEND_API int zend_parse_parameters(int num_args, const char *type_spec, ...)
num_args:參數數量,用ZEND_NUM_ARGS()可以獲取
type_spec 為參數解析規則,是一個字符串
最后一個是可變參數,指定要解析到的變量地址
舉例:
PHP_FUNCTION(my_func) { zval *arr; if(zend_parse_parameters(ZEND_NUM_ARGS(), "a", &a) == FAILURE) { RETURN_FALSE; } ... }
如果有多個變量type_spec可以變為"la",l表示整型,a表示數組(另外還有b:布爾型,s:字符串型,o:對象)
,后面則改為&a, &b
可以設置return_value,但PHP提供了設置了設置返回值的宏
#define RETURN_BOOL(b) { RETVAL_BOOL(b); return; } #define RETURN_NULL() { RETVAL_NULL(); return;} #define RETURN_LONG(l) { RETVAL_LONG(l); return; } #define RETURN_DOUBLE(d) { RETVAL_DOUBLE(d); return; } #define RETURN_STR(s) { RETVAL_STR(s); return; } #define RETURN_INTERNED_STR(s) { RETVAL_INTERNED_STR(s); return; } #define RETURN_NEW_STR(s) { RETVAL_NEW_STR(s); return; } #define RETURN_STR_COPY(s) { RETVAL_STR_COPY(s); return; } #define RETURN_STRING(s) { RETVAL_STRING(s); return; } #define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; } #define RETURN_EMPTY_STRING() { RETVAL_EMPTY_STRING(); return; } #define RETURN_RES(r) { RETVAL_RES(r); return; } #define RETURN_ARR(r) { RETVAL_ARR(r); return; } #define RETURN_OBJ(r) { RETVAL_OBJ(r); return; } #define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } #define RETURN_FALSE { RETVAL_FALSE; return; } #define RETURN_TRUE { RETVAL_TRUE; return; }寫個小例子
寫一個兩個整型變量相加的函數
/* $Salamander$ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_my_test.h" static int le_my_test; PHP_FUNCTION(my_add) { int argc = ZEND_NUM_ARGS(); zend_long a; zend_long b; if (zend_parse_parameters(argc, "ll", &a, &b) == FAILURE) RETURN_FALSE; RETURN_LONG(a + b); } PHP_MINIT_FUNCTION(my_test) { return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(my_test) { return SUCCESS; } PHP_RINIT_FUNCTION(my_test) { #if defined(COMPILE_DL_MY_TEST) && defined(ZTS) ZEND_TSRMLS_CACHE_UPDATE(); #endif return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(my_test) { return SUCCESS; } PHP_MINFO_FUNCTION(my_test) { php_info_print_table_start(); php_info_print_table_header(2, "my_test support", "enabled"); php_info_print_table_end(); } const zend_function_entry my_test_functions[] = { PHP_FE(my_add, NULL) PHP_FE_END }; zend_module_entry my_test_module_entry = { STANDARD_MODULE_HEADER, "my_test", my_test_functions, PHP_MINIT(my_test), PHP_MSHUTDOWN(my_test), PHP_RINIT(my_test), PHP_RSHUTDOWN(my_test), PHP_MINFO(my_test), PHP_MY_TEST_VERSION, STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_MY_TEST #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() #endif ZEND_GET_MODULE(my_test) #endif
config.m4中取消以下注釋(刪除dnl即可)
dnl PHP_ARG_ENABLE(my_test, whether to enable my_test support, dnl Make sure that the comment is aligned: dnl [ --enable-my_test Enable my_test support])
然后在my_test目錄下執行
phpize ./configure --with-php-config=/usr/local/php7.1/bin/php-config
php-config這個腳本是獲取PHP安裝信息的(PHP安裝路徑,PHP版本,PHP源碼的頭文件目錄,LDFLAGS,依賴的外部庫,PHP編譯參數),它在php的安裝路徑的bin目錄下,如果你不指定--with-php-config的話,將到默認的PHP的安裝路徑下搜索(安裝了多個PHP版本時最好指定一下,可能會編譯不通過)
然后
make && make install
得到
Installing shared extensions: /usr/local/php7.1/lib/php/extensions/no-debug-zts-20160303/
修改php.ini文件,加入.so
date.timezone = "Asia/Shanghai" display_errors = On error_reporting = E_ALL short_open_tag=Off upload_max_filesize = 50M post_max_size = 50M memory_limit=512M extension=my_test.so測試加載
php -m測試函數
php -r "echo my_add(1, 3);"
函數調用成功。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28162.html
摘要:圖形用戶界面擴展今天閱讀手冊的時候看到有這個圖形庫的擴展,所以咱趕新鮮用一用安裝擴展官方手冊的安裝步驟是的要求文檔寫著到上下載的,我用的是位,所以用了這個下載下來的文件目錄結構接下來放入到的目錄中,然后加入和放入目錄中,也就 圖形用戶界面(GUI) 擴展 今天閱讀PHP手冊的時候,看到PHP7有ui這個圖形庫的擴展,所以咱趕新鮮用一用 安裝擴展 官方手冊的安裝步驟是linux的: Re...
摘要:百度云盤下載地址文件目錄結構為開啟的腳本為開啟的腳本為開啟服務的腳本為開啟上述所有進程的腳本為關閉所有進程的腳本如果你更喜歡程序,可以使用簡單使用手冊把,,壓縮包解壓,并命名為,,標識一下新版本而已,以下配置文件用修改即可目錄中把或者 百度云盤下載地址 文件目錄結構 showImg(https://segmentfault.com/img/bVVFMU?w=757&h=380); ...
摘要:百度云盤下載地址文件目錄結構為開啟的腳本為開啟的腳本為開啟服務的腳本為開啟上述所有進程的腳本為關閉所有進程的腳本如果你更喜歡程序,可以使用簡單使用手冊把,,壓縮包解壓,并命名為,,標識一下新版本而已,以下配置文件用修改即可目錄中把或者 百度云盤下載地址 文件目錄結構 showImg(https://segmentfault.com/img/bVVFMU?w=757&h=380); ...
摘要:,配是通過一個類似的協議,升級版的的。在上有幫你管理進程,在似乎沒有,這是有點令人悲傷的。檢驗一下然后開啟然后配置中里文件在盤建立一個的文件夾,放入,開啟測試寫入訪問應用我的項目就用了這個東西,,歡迎 fastcgi As we all know,nginx配php是通過fastcgi(一個類似http的協議,升級版的cgi)的。在linux上有php-fpm幫你管理進程,在windo...
閱讀 3753·2021-08-11 11:16
閱讀 1621·2019-08-30 15:44
閱讀 1995·2019-08-29 18:45
閱讀 2267·2019-08-26 18:18
閱讀 996·2019-08-26 13:37
閱讀 1565·2019-08-26 11:43
閱讀 2109·2019-08-26 11:34
閱讀 372·2019-08-26 10:59