最近想嘗試寫PHP的C擴(kuò)展惶翻,在《PHP7底層設(shè)計與源碼實現(xiàn)》書中有一個PHP擴(kuò)展的例子丁溅,這里動手實現(xiàn)了一下鹦蠕。
準(zhǔn)備
Linux
PHP7
PHP7源碼包 點擊下載
預(yù)覽
Linux查看文本行數(shù)可以使用wc -l
查看佑力,現(xiàn)在需要將這一功能實現(xiàn)為PHP的自帶功能筋量。
假如有這樣一個文本,中間有空行拯欧,要分析該文本行數(shù)详囤,有些時候需要讓函數(shù)輸出3,有些時候讓函數(shù)輸出7(包括空行)镐作,可以通過配置php.ini文件保存這一配置藏姐。
然后在PHP中這樣使用就好了:
開始
- 首先使用PHP7源碼包中的ext_skel工具生成擴(kuò)展的基本框架,進(jìn)入源碼包目錄執(zhí)行
./ext_skel --extname=wcl
该贾,之后會在ext_skel同目錄下生成一個wcl文件夾羔杨,其中wcl.c是擴(kuò)展的主要源文件,php_wcl.h是頭文件杨蛋。 -
編輯config.m4兜材,去掉PHP_ARG_ENABLE和[--enable-wcl]的注釋dnl,我這邊生成的config.m4默認(rèn)是下面的樣子逞力,就不用改了曙寡。
- 編輯wcl.c文件:
/* Every user-visible function in PHP should document itself in the source */
/* {{{ proto string confirm_wcl_compiled(string arg)
Return a string to confirm that the module is compiled in */
PHP_FUNCTION(wcl)
{
size_t arg_len, len;
int argc = ZEND_NUM_ARGS();
char *arg = NULL;
if (zend_parse_parameters(argc, "s", &arg, &arg_len) == FAILURE) {
return;
}
// zend_string *strg;
// strg = strpprintf(0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "wcl", arg);
// RETURN_STR(strg);
FILE *fp;
if ((fp = fopen(arg, "r")) == NULL) {
RETURN_FALSE;
}
char ch, pre = '\n';
zend_long lcount = 0;
while ((ch = fgetc(fp)) != EOF) {
if (ch == '\n') {
lcount++;
}
pre = ch;
}
fclose(fp);
RETURN_LONG(lcount);
}
/* }}} */
/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(wcl)
{
#if defined(COMPILE_DL_WCL) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
這里是執(zhí)行phpinfo()函數(shù)的輸出
*/
PHP_MINFO_FUNCTION(wcl)
{
php_info_print_table_start();
php_info_print_table_header(2, "wcl support", "enabled");
php_info_print_table_end();
/* Remove comments if you have entries in php.ini
DISPLAY_INI_ENTRIES();
*/
}
/* }}} */
/* {{{ wcl_functions[]
*
* Every user visible function must have an entry in wcl_functions[].
*/
const zend_function_entry wcl_functions[] = {
PHP_FE(wcl, NULL) /* 注冊上面定義的wcl函數(shù)*/
PHP_FE_END /* Must be the last line in wcl_functions[] */
};
/* }}} */
/* {{{ wcl_module_entry
*/
zend_module_entry wcl_module_entry = {
STANDARD_MODULE_HEADER,
"wcl",
wcl_functions,
PHP_MINIT(wcl),
PHP_MSHUTDOWN(wcl),
PHP_RINIT(wcl), /* Replace with NULL if there's nothing to do at request start */
PHP_RSHUTDOWN(wcl), /* Replace with NULL if there's nothing to do at request end */
PHP_MINFO(wcl),
PHP_WCL_VERSION,
STANDARD_MODULE_PROPERTIES
};
/* }}} */
計數(shù)功能主要在wcl函數(shù)中實現(xiàn),無法打開文件返回false寇荧,然后按文件內(nèi)容判斷為換行符管嬉,計數(shù)器+1怯伊,最后返回文件行數(shù)筛婉。
- 之前說過對于前面的test.txt(包含多個空行),有時我們需要讓函數(shù)不對空行計數(shù)镀琉,有時又需要,因此需要加載php.ini文件蕊唐,將對應(yīng)配置放到配置文件中由擴(kuò)展加載屋摔。
編輯php_wcl.h文件:
/*
Declare any global variables you may need between the BEGIN
and END macros here:
*/
ZEND_BEGIN_MODULE_GLOBALS(wcl)
// filter_blank變量表示是否過濾空行,聲明擴(kuò)展內(nèi)的全局變量
zend_long filter_blank;
// char *global_string;
ZEND_END_MODULE_GLOBALS(wcl)
- 編輯wcl.c文件
添加配置項:
/* If you declare any globals in php_wcl.h uncomment this:*/
ZEND_DECLARE_MODULE_GLOBALS(wcl)
// Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
// filter_blank變量賦默認(rèn)值0
STD_PHP_INI_ENTRY("wcl.filter_blank", "0", PHP_INI_ALL, OnUpdateBool, filter_blank, zend_wcl_globals, wcl_globals)
PHP_INI_END()
/* }}} */
wcl函數(shù)做如下修改替梨,使用WCL_G函數(shù)獲取php.ini中對應(yīng)配置項的值
char ch, pre = '\n';
zend_long lcount = 0;
while ((ch = fgetc(fp)) != EOF) {
if (ch == '\n') {
// filter_blank in php.ini
if (WCL_G(filter_blank) && pre == ch) {
continue;
}
lcount++;
}
pre = ch;
}
- 由于增加了配置項凡壤,現(xiàn)在要在擴(kuò)展啟動和銷毀時對配置項做相應(yīng)操作:
/* {{{ PHP_MINIT_FUNCTION
配置項的注冊在該階段完成
*/
PHP_MINIT_FUNCTION(wcl)
{
/* If you have INI entries, uncomment these lines */
REGISTER_INI_ENTRIES();
return SUCCESS;
}
/* }}} */
PHP_MSHUTDOWN_FUNCTION(wcl)
{
/* uncomment this line if you have INI entries*/
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
- 編譯生成動態(tài)鏈接.so文件
phpize命令需要安裝php-dev
phpize
./configure
sudo make
sudo make install
- 配置php.ini
執(zhí)行上述過程不報錯那么擴(kuò)展對應(yīng)的目錄下會生成wcl.so文件,編輯php.ini
extension=wcl.so
[Wcl]
wcl.filter_blank = 1
到這里wcl擴(kuò)展就完成了耙替,可以重啟fpm然后直接使用wcl(filename)來測試輸出。
再多bb兩句曹体,php啟動常見兩種方式:cli和fpm俗扇,如果配置的是fpm的php.ini文件,那么修改php.ini后需要重啟fpm箕别,由此可見php.ini是在fpm啟動時候加載的铜幽,參見 https://www.php.net/manual/zh/configuration.file.php
關(guān)于PHP請求以及PHP擴(kuò)展的生命周期參見https://www.cnblogs.com/beatzeus/archive/2016/11/16/6071902.html