原创

php扩展开发一:hello world

温馨提示:
本文最后更新于 2019年03月16日,已超过 1,857 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

下载php源码

首先,我们需要下载php的源码:https://github.com/php/php-src/releases

本人选择的是php7.3

wget https://github.com/php/php-src/archive/php-7.3.3.tar.gz

自行选择版本,下载完之后解压,并进入php目录 ext/

tar -zvxf php-7.3.3.tar.gz
cd php-src-php-7.3.3/ext

在这个目录中,有个"ext_skel.php"的文件,我们运行:

 php ext_skel.php --ext tioncico

将输出:

[root@localhost ext]# php ext_skel.php --ext tioncico
Copying config scripts... done
Copying sources... done
Copying tests... done

Success. The extension is now ready to be compiled into PHP. To do so, use the
following steps:

cd /path/to/php-src
./buildconf
./configure --enable-tioncico
make

Don't forget to run tests once the compilation is done:
make test TESTS=ext/tioncico/tests

Thank you for using PHP!

在这个时候,将会生成一个和扩展名一样的文件夹

准备工作结束了

扩展目录

进入扩展文件夹

cd tioncico

里面有以下几个文件:

tests  //文件夹,用于扩展安装之后的测试
config.m4  //配置编译的执行命令
config.w32  //win32编译
php_tioncico.h //扩展的头文件  
tioncico.c //扩展c文件

安装扩展

我们直接通过phpize生成./configure文件:(多版本情况需要注意phpize版本)

phpize

然后根据正常的安装扩展进行安装:

./configure --with-php-config=/www/server/php/73/bin/php-config
make
make install

在编译完成之后,在php.ini中加入一行配置:

extension = tioncico.so

php -m查看扩展是否安装:

php -m

进入tests文件夹,运行测试文件:

cd tests
php 001.phpt 
php 002.phpt 
php 003.phpt

即可看到自定义扩展的输出(本人是php多版本,所以用的是php73版本运行)

[root@localhost tests]# php73 001.phpt 
--TEST--
Check if tioncico is loaded
--SKIPIF--
--FILE--
The extension "tioncico" is available--EXPECT--
The extension "tioncico" is available
[root@localhost tests]# php73 002.phpt 
--TEST--
tioncico_test1() Basic test
--SKIPIF--
--FILE--
The extension tioncico is loaded and working!
NULL
--EXPECT--
The extension tioncico is loaded and working!
NULL
[root@localhost tests]# php73 003.phpt 
--TEST--
tioncico_test2() Basic test
--SKIPIF--
--FILE--
string(11) "Hello World"
string(9) "Hello PHP"
--EXPECT--
string(11) "Hello World"
string(9) "Hello PHP"

config.m4

网上的教程都说到,运行hello world扩展需要去掉

PHP_ARG_ENABLE(tioncico, whether to enable tioncico support,

dnl Make sure that the comment is aligned:

[ --enable-tioncico Enable tioncico support], no)

这三行的dnl注释,但是本人运行php73生成扩展时默认就去掉了上下2行注释,中间那行并不需要

PHP_ARG_ENABLE和PHP_ARG_WITH代表了2种编译模式
PHP_ARG_WITH 在你的扩展用到外部依赖的时候需要使用
否则使用PHP_ARG_ENABLE

之后我或许会继续学习下去补充说明

php_tioncico.h

该文件为扩展头文件,用于定义声明php扩展

extern zend_module_entry tioncico_module_entry;

该行代码引用了php源码的模块必要信息结构体

struct _zend_module_entry {
   unsigned short size;
   unsigned int zend_api;
   unsigned char zend_debug;
   unsigned char zts;
   const struct _zend_ini_entry *ini_entry;
   const struct _zend_module_dep *deps;
   const char *name;//扩展名
   const struct _zend_function_entry *functions;//php函数结构体的指针
   int (*module_startup_func)(INIT_FUNC_ARGS);//模块初始化时被调用的函数指针。用来放一些初始化步骤。初始化过程中出现故障返回FAILURE,成功返回SUCCESS。声明一个初始化函数使用ZEND_MINIT
   int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);//模块被关闭时调用的函数指针,同来用来做一次性的析构步骤。如释放资源。成功返回SUCESS,失败返回FAILURE,未使用返回NULL。声明使用ZEND_MSHUTDOWN
   int (*request_startup_func)(INIT_FUNC_ARGS);//每处理一次请求前调用此函数。成功SUCESS,失败FAILURE,未使用返回NULL。声明使用ZEND_RINIT。从WEB来解释,就是每次请求调用此函数。
   int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);//每处理一次请求结束后调用此函数。成功SUCESS,失败FAILURE,未使用返回NULL。声明使用ZEND_RSHUTDOWN。
   void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);//当调用phpinfo()时打印出的关于此扩展的信息。这个信息就是由此函数来输出的。声明使用ZEND_MINFO
   const char *version;//版本号
   size_t globals_size;
#ifdef ZTS
   ts_rsrc_id* globals_id_ptr;
#else
   void* globals_ptr;
#endif
   void (\*globals_ctor)(void \*global);
   void (\*globals_dtor)(void \*global);
   int (*post_deactivate_func)(void);
   int module_started;
   unsigned char type;
   void *handle;
   int module_number;
   const char *build_id;
};

tioncico.c

该文件为扩展的主文件,里面写我们需要实现的扩展逻辑

里面默认已经写好了例子以及入口

首先我们需要看下zend_module_entry tioncico_module_entry

/* {{{ tioncico_module_entry
 * 模块信息结构体
 */
zend_module_entry tioncico_module_entry = {
   STANDARD_MODULE_HEADER,//标准模块头,填充了扩展名上面的所有参数
   "tioncico",                /* 扩展名
   tioncico_functions,          /* 函数定义
   NULL,                    /* PHP_MINIT - Module initialization */
   NULL,                    /* PHP_MSHUTDOWN - Module shutdown */
   PHP_RINIT(tioncico),         /* PHP_RINIT - Request initialization */
   NULL,                    /* PHP_RSHUTDOWN - Request shutdown */
   PHP_MINFO(tioncico),         /* PHP_MINFO - Module info */
   PHP_TIONCICO_VERSION,     /* Version 在tioncico.h中已经定义了版本号*/
   STANDARD_MODULE_PROPERTIES //标准模块属性充了版本号下面的所有参数
};
/* }}} */

通过上面的函数定义结构体,我们需要声明自己的函数:

/* {{{ void tioncico_test1()
 */
PHP_FUNCTION(tioncico_test1)
{
   ZEND_PARSE_PARAMETERS_NONE();//暂时不确定详细意思,大概为没有参数的情况需要调用这个

   php_printf("The extension %s is loaded and working!\r\n", "tioncico");//直接输出字符串,相当于php echo
}

我们也可以声明一个带参数的函数:

/* {{{ string tioncico_test2( [ string $var ] )
 */
PHP_FUNCTION(tioncico_test2)
{
   char *var = "World";//定义一个world字符串变量
   size_t var_len = sizeof("World") - 1;//定义长度
   zend_string *retval;//定义zend_string类型的变量

   ZEND_PARSE_PARAMETERS_START(0, 1)//设置参数数量限制,前面的代表着最少传0个参数,后面的代表了最多传1个
      Z_PARAM_OPTIONAL //可选参数 ,不强制传参
      Z_PARAM_STRING(var, var_len)//如果有传值,则把值赋值给字符串变量var
   ZEND_PARSE_PARAMETERS_END();//设置参数结束

   retval = strpprintf(0, "Hello %s", var);//格式化字符串

   RETURN_STR(retval);//返回值
}
/* }}}*/

但这样是不够的,我们还需要定义一个函数的参数声明:

/* {{{ arginfo
 */
ZEND_BEGIN_ARG_INFO(arginfo_tioncico_test1, 0)//函数名,参数是否为引用类型,0代表不是
ZEND_END_ARG_INFO()//结束函数参数信息声明

ZEND_BEGIN_ARG_INFO(arginfo_tioncico_test2, 0)//函数名,参数是否为引用类型,0代表不是
   ZEND_ARG_INFO(0, str)//声明一个普通参数str
ZEND_END_ARG_INFO()//结束函数参数信息声明
/* }}} */

然后,将函数注册进模块

/* {{{ tioncico_functions[]
 */
static const zend_function_entry tioncico_functions[] = {
   PHP_FE(tioncico_test1,    arginfo_tioncico_test1)
   PHP_FE(tioncico_test2,    arginfo_tioncico_test2)
   PHP_FE_END
};

这时候,一个扩展的2个函数已经编写完成了

重新编译运行,调用函数即可:

[root@localhost tioncico]# php73 tests/002.phpt 
--TEST--
tioncico_test1() Basic test
--SKIPIF--
--FILE--
tioncico is 666! //修改过的输出
NULL
--EXPECT--
The extension tioncico is loaded and working!
NULL
正文到此结束
本文目录