开发环境:PHP-8.2
下载PHP源码并解压,注意选择和当前环境相同的版本
[root@localhost ~]# wget https://www.php.net/distributions/php-*.*.*.tar.gz
创建扩展骨架,执行完命令后会在PHP源码的ext目录下生成该扩展的目录
[root@localhost ~]# /program/php/bin/php /program/php/src/ext/ext_skel.php --ext demo_profile
Copying config scripts... done
Copying sources... done
Copying tests... done
Success. The extension is now ready to be compiled. To do so, use the
following steps:
cd /program/php/src/ext/demo_profile
phpize
./configure
make
Don't forget to run tests once the compilation is done:
make test
Thank you for using PHP!
[root@localhost ~]#
在扩展目录下有一个demo_profile.stub.php文件,里面自带了test1()和test2()两个函数原型,开发者仿照原型定义自己的函数即可
[root@localhost ~]# vim /program/php/src/ext/demo_profile/demo_profile.stub.php
<?php
/**
* @generate-class-entries
* @undocumentable
*/
function test1(): void {}
function test2(string $str = ""): string {}
// 增加intro()函数
function intro(string $name = '匿名', string $gender = '保密', int $birth = 1970): string {}
[root@localhost ~]#
说明:由于是函数原型,所以只需要声明形参和返回值即可,无需实现函数功能(函数功能是在“扩展名.c”文件里使用C语言实现)。
使用工具解析加入了自定义函数的demo_profile.stub.php文件,并根据解析内容更新demo_profile_arginfo.h文件
[root@localhost ~]# cd /program/php/src/ext/demo_profile
[root@localhost demo_profile]# /program/php/bin/php ../../build/gen_stub.php demo_profile.stub.php
[root@localhost demo_profile]# cat demo_profile_arginfo.h
…………(此处省略内容若干)…………
Saved demo_profile_arginfo.h
[root@localhost demo_profile]# cat demo_profile_arginfo.h
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: **************************************** */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_test1, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_test2, 0, 0, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, str, IS_STRING, 0, "\"\"")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_intro, 0, 0, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, name, IS_STRING, 0, "\'匿名\'")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, gender, IS_STRING, 0, "\'保密\'")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, birth, IS_LONG, 0, "1970")
ZEND_END_ARG_INFO()
ZEND_FUNCTION(test1);
ZEND_FUNCTION(test2);
ZEND_FUNCTION(intro);
static const zend_function_entry ext_functions[] = {
ZEND_FE(test1, arginfo_test1)
ZEND_FE(test2, arginfo_test2)
ZEND_FE(intro, arginfo_intro)
ZEND_FE_END
};
[root@localhost demo_profile]#
说明①:开发者可以直接手动修改demo_profile_arginfo.h的代码,但是并不建议这样做,因为demo_profile_arginfo.h可以根据demo_profile.stub.php生成,而维护*.php文件比维护*.h文件更简单,尤其是实现复杂的类,由于有很多属性和方法,如果手动修改*.h文件那么工作量会很庞大且繁琐。
说明②:解析工具需要从GitHub下载(只需下载一次),国内可能会下载失败。
在demo_profile.c文件里实现扩展功能
[root@localhost ~]# vim /program/php/src/ext/demo_profile/demo_profile.c
/* {{{ string intro( [ string $name = '匿名', string $gender = '保密', int $birth = 1970 ] ) */
PHP_FUNCTION(intro)
{
char *name = "匿名";
size_t name_len = sizeof("匿名") - 1;
char *gender = "保密";
size_t gender_len = sizeof("保密") - 1;
zend_long birth = 1970;
zend_string *retval;
/* (0, 3)表示intro()有0个必填参数,共有3个参数(不分选填和必填),也就是3个参数均为选填 */
ZEND_PARSE_PARAMETERS_START(0, 3)
Z_PARAM_OPTIONAL /* Z_PARAM_OPTIONAL是必填参数和选填参数的分界线,上面是必填,下面是选填 */
Z_PARAM_STRING(name, name_len) /* 获取name参数 */
Z_PARAM_STRING(gender, gender_len) /* 获取gender参数 */
Z_PARAM_LONG(birth) /* 获取birth参数 */
ZEND_PARSE_PARAMETERS_END();
retval = strpprintf(0, "俺叫%s(%s),出生于%ld年。", name, gender, birth);
RETURN_STR(retval);
}
/* }}}*/
[root@localhost ~]#
说明:PHP_FUNCTION(test1)和PHP_FUNCTION(test2)就是demo_profile.stub.php文件里的test1()和test2()两个函数原型的具体实现,开发者也在这里实现自己的intro()函数功能即可。
编译扩展就没什么需要特别说明的了,和编译官方扩展是一样的
[root@localhost ~]# cd /program/php/src/ext/demo_profile
[root@localhost demo_profile]# /program/php/bin/phpize
[root@localhost demo_profile]# ./configure --with-php-config=/program/php/bin/php-config
[root@localhost demo_profile]# make
[root@localhost demo_profile]# make install
启用扩展并重启PHP-FPM即可
[root@localhost ~]# vim /program/php/php.ini
extension=demo_profile
[root@localhost ~]# service php-fpm restart
重新编译命令
[root@localhost ~]# cd /program/php/src/ext/demo_profile && \
/program/php/bin/php ../../build/gen_stub.php demo_profile.stub.php && \
/program/php/bin/phpize && \
./configure --with-php-config=/program/php/bin/php-config && \
make clean && make && make install && \
service php-fpm restart
在PHP脚本中调用扩展的函数
<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);
test1();
echo 'test2() => 0个参数:' . test2() . PHP_EOL;
echo 'test2() => 1个参数:' . test2('程序猿') . PHP_EOL;
echo 'intro() => 0个参数:' . intro() . PHP_EOL;
echo 'intro() => 1个参数:' . intro('张三') . PHP_EOL;
echo 'intro() => 2个参数:' . intro('张三', '男') . PHP_EOL;
echo 'intro() => 3个参数:' . intro('张三', '男', 2003) . PHP_EOL;
// The extension demo_profile is loaded and working!
// test2() => 0个参数:Hello World
// test2() => 1个参数:Hello 程序猿
// intro() => 0个参数:俺叫匿名(保密),出生于1970年。
// intro() => 1个参数:俺叫张三(保密),出生于1970年。
// intro() => 2个参数:俺叫张三(男),出生于1970年。
// intro() => 3个参数:俺叫张三(男),出生于2003年。