美文网首页PHP经验分享PHP那些事儿
实现一个PHP的C扩展 [入门篇]

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

作者: laotoutou | 来源:发表于2019-07-19 16:25 被阅读3次

最近想尝试写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

相关文章

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

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

  • php7.4扩展

    php 的底层是由 c/c++ 实现的,所以 php 本身就是一层皮,而我们也可以通过开发一些扩展来增强 php。...

  • 2018.01.03 周三--【技术文章】《PHP扩展及核心》

    一、主要内容: 1️⃣php扩展的概念和底层实现 2️⃣编写一个php扩展的步骤 3️⃣php底层,Zend 引擎...

  • 用 FFI 写一个普通PHP程序员 “看不懂” 的 “PHP”

    PHP 7.4 新加入了一个扩展 PHP FFI,这个扩展可以很方便的让PHP调用C语言写的各种优秀的库。 什么...

  • Phalcon入门(一)--安装

    介绍 Phalcon是一个使用c扩展写的PHP框架, 使用c扩展意味着在运行速度上要优于直接使用php写的框架 安...

  • PHP扩展开发

    为什么要用到php扩展?因为php扩展使用C语言编写,而C语言是静待编译的,所以执行效率要高于php很多,这里我来...

  • PHP扩展开发

    PHP程序员大概都知道可以用C/C++来扩展PHP。现在网络上关于PHP扩展开发的资料也已经很多了,假如你恰巧有C...

  • php-swoole安装

    PHP版本 7.2 / swoole版本 4.5 swoole是php的一个c扩展程序,能使php跑协程异步 首先...

  • php扩展开发(二)类开发

    php扩展开发(二)类开发 类开发 实现目标,后面我们将根据这个结构进行扩展的开发 这里使用server.c作为扩...

  • PHP C 扩展安装的两种方式

    PHP 扩展有两种。一种是 PHP 编写的扩展,通过 composer 安装。一种是通过 C 编写,通过 PHP...

网友评论

    本文标题:实现一个PHP的C扩展 [入门篇]

    本文链接:https://www.haomeiwen.com/subject/jtlrlctx.html