实现一个PHP的C扩展 [入门篇]

最近想尝试写PHP的C扩展,在《PHP7底层设计与源码实现》书中有一个PHP扩展的例子,这里动手实现了一下。

准备

Linux
PHP7
PHP7源码包 点击下载

预览

Linux查看文本行数可以使用wc -l查看,现在需要将这一功能实现为PHP的自带功能。


假如有这样一个文本,中间有空行,要分析该文本行数,有些时候需要让函数输出3,有些时候让函数输出7(包括空行),可以通过配置php.ini文件保存这一配置。
然后在PHP中这样使用就好了:

开始
  1. 首先使用PHP7源码包中的ext_skel工具生成扩展的基本框架,进入源码包目录执行./ext_skel --extname=wcl,之后会在ext_skel同目录下生成一个wcl文件夹,其中wcl.c是扩展的主要源文件,php_wcl.h是头文件。
  2. 编辑config.m4,去掉PHP_ARG_ENABLE和[--enable-wcl]的注释dnl,我这边生成的config.m4默认是下面的样子,就不用改了。


  3. 编辑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,最后返回文件行数。

  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)
  1. 编辑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;
    }
  1. 由于增加了配置项,现在要在扩展启动和销毁时对配置项做相应操作:
/* {{{ 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;
}
  1. 编译生成动态链接.so文件
    phpize命令需要安装php-dev
phpize
./configure
sudo make
sudo make install
  1. 配置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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容