下载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