php扩展开发一:hello world
下载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
复制
- 点赞
- 收藏
- 关注作者
评论(0)