实现一个PHP的C扩展 [入门篇]
2019-07-19 本文已影响3人
laotoutou
最近想尝试写PHP的C扩展,在《PHP7底层设计与源码实现》书中有一个PHP扩展的例子,这里动手实现了一下。
准备
Linux
PHP7
PHP7源码包 点击下载
预览
Linux查看文本行数可以使用wc -l
查看,现在需要将这一功能实现为PHP的自带功能。
假如有这样一个文本,中间有空行,要分析该文本行数,有些时候需要让函数输出3,有些时候让函数输出7(包括空行),可以通过配置php.ini文件保存这一配置。
然后在PHP中这样使用就好了:
开始
- 首先使用PHP7源码包中的ext_skel工具生成扩展的基本框架,进入源码包目录执行
./ext_skel --extname=wcl
,之后会在ext_skel同目录下生成一个wcl文件夹,其中wcl.c是扩展的主要源文件,php_wcl.h是头文件。 -
编辑config.m4,去掉PHP_ARG_ENABLE和[--enable-wcl]的注释dnl,我这边生成的config.m4默认是下面的样子,就不用改了。
- 编辑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
这里是执行phpinfo()函数的输出
*/
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函数*/
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
};
/* }}} */
计数功能主要在wcl函数中实现,无法打开文件返回false,然后按文件内容判断为换行符,计数器+1,最后返回文件行数。
- 之前说过对于前面的test.txt(包含多个空行),有时我们需要让函数不对空行计数,有时又需要,因此需要加载php.ini文件,将对应配置放到配置文件中由扩展加载。
编辑php_wcl.h文件:
/*
Declare any global variables you may need between the BEGIN
and END macros here:
*/
ZEND_BEGIN_MODULE_GLOBALS(wcl)
// filter_blank变量表示是否过滤空行,声明扩展内的全局变量
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变量赋默认值0
STD_PHP_INI_ENTRY("wcl.filter_blank", "0", PHP_INI_ALL, OnUpdateBool, filter_blank, zend_wcl_globals, wcl_globals)
PHP_INI_END()
/* }}} */
wcl函数做如下修改,使用WCL_G函数获取php.ini中对应配置项的值
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;
}
- 由于增加了配置项,现在要在扩展启动和销毁时对配置项做相应操作:
/* {{{ 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;
}
- 编译生成动态链接.so文件
phpize命令需要安装php-dev
phpize
./configure
sudo make
sudo make install
- 配置php.ini
执行上述过程不报错那么扩展对应的目录下会生成wcl.so文件,编辑php.ini
extension=wcl.so
[Wcl]
wcl.filter_blank = 1
到这里wcl扩展就完成了,可以重启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
关于PHP请求以及PHP扩展的生命周期参见https://www.cnblogs.com/beatzeus/archive/2016/11/16/6071902.html