环境介绍:
centos7、php7.4
实现功能:写个简单类,包含name属性和一个say方法;
打开PHP安装的源码包,进入/usr/local/resource/php-7.4.24/ext
执行命令:php ext_skel.php --ext hello
结果如下:
此时在./ext目录下会多出个hello目录,进入其中
打开配置文件config.m4
这个文件很重要,PHP系统如何构建扩展就根据它来的,配合phpize工具生成configure文件
dnl相当于注释吧,本次扩展不依赖其他库,所以打开PHP_ARG_ENABLE就行(代码里说明
如果所编写的扩展如果依赖其它的扩展或者lib库,就开启with)
第一个参数是扩展名,第二个参数用于configure 脚本执行时输出的,AS_HELP_STRING用
于--help时显示信息
最底下,PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
第二个参数为源文件,多个源文件用空格隔开,shared参数设置为$ext_shared吧
(应该是:)),交给configure处理。。。
其他一些M4宏方法,有兴趣的可以去细细查下,不多解释,7.4版本下,以上都是默认开启的
打开源文件hello.c
内置了test1和test2方法,咱不管它
(1)先摁个指针:zend_class_entry *hello_ce;
(2)在hello_module_entry结构体内,修改第4个参数为 PHP_MINIT(hello);
(3)模块初始化:
PHP_MINIT_FUNCTION(hello)
{
zend_class_entry ce;
//hello为类名(切记要有双引号),hello_methods是类的行为集合
INIT_CLASS_ENTRY(ce, "hello", hello_methods);
//zend_register_internal_class返回一个指针
hello_ce = zend_register_internal_class(&ce);
//定义name属性
zend_declare_property_null(hello_ce, "name", sizeof("name") - 1, ZEND_ACC_PUBLIC);
return SUCCESS;
}
(4)行为集合,方法结构体:
//say为行为名称,arginfo_hello_say为参数验证
const zend_function_entry hello_methods[] = {
PHP_ME(hello, say, arginfo_hello_say, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
(5)参数验证,定义arginfo结构体:
· ZEND_BEGIN_ARG_INFO_EX(arginfo_hello_say, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, other)
ZEND_END_ARG_INFO()
生成zend_arg_info结构的数组比较繁琐,内核已经提供了相应的宏来处理此问题。
头部:ZEND_BEGIN_ARG_INFO(name, pass_rest_by_reference)
ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)
前两个参数统一:name结构体名称;pass_rest_by_reference(1|0)为1时表示所有参数都要以引入的形式传递 ;return_reference(1|0)1时返回值需要以引入的形式
返回;required_num_args必传参数的个数
数据:ZEND_ARG_INFO(0, name)
第一个参数(1|0)为1表示必须以引入的方式传递参数,此处单独配置会覆盖头部pass_rest_by_reference;第二个为参数名称
尾部:ZEND_END_ARG_INFO()
(6)行为方法体具体实现:
PHP_FUNCTION(hello, say)
{
zend_string *name, *other, *var;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(name)
Z_PARAM_OPTIONAL
Z_PARAM_STR(other)
ZEND_PARSE_PARAMETERS_END();
var = strpprintf(0, "hello %s", ZSTR_VAL(name));
if(ZSTR_LEN(other) != 0){
var = strpprintf(0, "hello %s from %s", ZSTR_VAL(name), ZSTR_VAL(other));
}
RETURN_STR(var);
}
ps:在PHP7之前一直使用zend_parse_parameters函数获取参数。这个函数的作用,就是把传入的参数转换为PHP内核中相应的类型,方便在PHP扩展中使用。比如官网例子:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) {
return;
}
ZEND_NUM_ARGS() TSRMLS_CC:规定传入参数的个数
al|zb: 格式化字符串,指定传入参数与PHP内核类型的转换关系 a:zval, l:long,| 表示后面的参数可选 ,z: 表示参数是多种类型,要把传入的参数转换为zval类型,b:布尔
FAST ZPP
在PHP7中新提供的方式。是为了提高参数解析的性能。对应经常使用的方法,建议使用FAST ZPP方式。
以ZEND_PARSE_PARAMETERS_START(1, 2)开头。
第一个参数表示必传的参数个数,第二个参数表示最多传入的参数个数。
以ZEND_PARSE_PARAMETERS_END();结束。
中间是传入参数的解析。
值得注意的是,一般FAST ZPP的宏方法与zend_parse_parameters的specifier是一一对应的。如:
Z_PARAM_ARRAY 对应 a
Z_PARAM_LONG 对应 l
Z_PARAM_OPTIONAL 对应 |
FAST ZPP相应的宏方法可以查看官方网站 https://wiki.php.net/rfc/fast_zpp#proposal
(7)完整代码(注意顺序):
zend_class_entry *hello_ce;
PHP_METHOD(hello, say)
{
zend_string *name, *other, *var;
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(name)
Z_PARAM_OPTIONAL
Z_PARAM_STR(other)
ZEND_PARSE_PARAMETERS_END();
var = strpprintf(0, "hello %s", ZSTR_VAL(name));
if(ZSTR_LEN(other) != 0){
var = strpprintf(0, "hello %s from %s", ZSTR_VAL(name), ZSTR_VAL(other));
}
RETURN_STR(var);
}
ZEND_BEGIN_ARG_INFO_EX(arginfo_hello_say, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, other)
ZEND_END_ARG_INFO()
const zend_function_entry hello_methods[] = {
PHP_ME(hello, say, arginfo_hello_say, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
PHP_MINIT_FUNCTION(hello)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "hello", hello_methods);
hello_ce = zend_register_internal_class(&ce);
zend_declare_property_null(hello_ce, "name", sizeof("name") - 1, ZEND_ACC_PUBLIC);
return SUCCESS;
}
编译,安装 ,/usr/local/resource/php-7.4.24/ext/hello目录下
/opt/php/bin/phpize(根据安装的PHP位置)
./configure --enable-hello --with-php-config=/opt/php/bin/php-config
make && make install
打开php.ini,添加extension=hello.so
php -m查看是否加载hello扩展
测试
来源地址:https://blog.csdn.net/jz_Orange/article/details/126838021