PHP扩展开发入门
PHP扩展可以在编译PHP时一起编译,也可以单独编译为共享库,共享库的像是需要将库的名称加入到php.ini配置中去,在pho_module_startup()阶段PHP会根据php.ini的配置将对应的扩展共享库加载到PHP中。
# 开发工具
PHP提供了几个用于简化扩展开发的脚本工具:
- ext_skel:初始化php扩展项目
- phpsize:生成configure配置文件
# ext_skel
ext_skel这个脚本位于php源码的ext目录下,它的作用是用来生成扩展的基本骨架,帮助开发者快速生成一个规范的扩展结构。
用法如下(以新建say扩展为例):
gitlib@devops:~/php-7.1.31/ext$ ./ext_skel --extname=say
执行完之后在ext目录下新生成一个扩展目录,比如
gitlib@devops:~/php-7.1.31/ext/say$ ls -al
total 44
drwxr-xr-x 3 zhoufei root 4096 Oct 6 21:46 .
drwxr-xr-x 76 zhoufei root 4096 Oct 6 21:46 ..
-rw-r--r-- 1 zhoufei root 1984 Oct 6 21:46 config.m4
-rw-r--r-- 1 zhoufei root 331 Oct 6 21:46 config.w32
-rw-r--r-- 1 zhoufei root 4 Oct 6 21:46 CREDITS
-rw-r--r-- 1 zhoufei root 0 Oct 6 21:46 EXPERIMENTAL
-rw-r--r-- 1 zhoufei root 392 Oct 6 21:46 .gitignore
-rw-r--r-- 1 zhoufei root 2267 Oct 6 21:46 php_say.h
-rw-r--r-- 1 zhoufei root 5023 Oct 6 21:46 say.c
-rw-r--r-- 1 zhoufei root 493 Oct 6 21:46 say.php
drwxr-xr-x 2 zhoufei root 4096 Oct 6 21:46 tests
2
3
4
5
6
7
8
9
10
11
12
13
主要文件说明如下:
- config.m4:autoconf规则的编译配置文件
- include:依赖库的include头文件
- say.c:扩展源码
- php_say.c: 扩展源码头文件
这个脚本主要生成来编译需要的配置及扩展的基本结构,初步生成的这个扩展可以成功编译、安装和使用,实际开发中我们可以用这个脚本生成一个基本结构,然后根据具体的需要完善。
# phpize
phpsize是扩展编译必用的一个脚本,主要是操作复杂的autoconf/automake/autoheader/autolocal等系列命令,用于生成扩展的configure文件。
示例:
gitlib@devops:~/php-7.1.31/ext/say$ /usr/local/php7/bin/phpize
Configuring for:
PHP Api Version: 20180731
Zend Module Api No: 20180731
Zend Extension Api No: 320180731
2
3
4
5
# 实践案例
示例中,我们将实现如下功能:
<?php
echo say();
?>
2
3
输出内容:
$ php ./test.php
$ hello word
2
在扩展中实现一个say方法,调用say方法后,输出 hello word。
# 第一步:生成代码
PHP为我们提供了生成基本代码的工具 ext_skel。这个工具在PHP源代码的./ext目录下。
$ cd php_src/ext/
$ ./ext_skel --extname=say
2
extname参数的值就是扩展名称,执行ext_skel命令后,这样在当前目录下会生成一个与扩展名一样的目录。
# 第二步,修改config.m4配置文件
$ cd ./say
$ vim ./config.m4
2
config.m4的作用就是配合phpize工具生成configure文件。configure文件是用于环境检测的。检测扩展编译运行所需的环境是否满足,现在开始修改config.m4文件。
打开config.m4文件后,你会发现这样一段文字:
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(say, for say support,
dnl Make sure that the comment is aligned:
dnl [ --with-say Include say support])
dnl Otherwise use enable:
dnl PHP_ARG_ENABLE(say, whether to enable say support,
dnl Make sure that the comment is aligned:
dnl [ --enable-say Enable say support])
2
3
4
5
6
7
8
9
10
11
其中,dnl 是注释符号。上面的代码说,如果你所编写的扩展如果依赖其它的扩展或者lib库,需要去掉PHP_ARG_WITH相关代码的注释。否则,去掉 PHP_ARG_ENABLE 相关代码段的注释。我们编写的扩展不需要依赖其他的扩展和lib库。因此,我们去掉PHP_ARG_ENABLE前面的注释。去掉注释后的代码如下:
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(say, for say support,
dnl Make sure that the comment is aligned:
dnl [ --with-say Include say support])
dnl Otherwise use enable:
PHP_ARG_ENABLE(say, whether to enable say support,
Make sure that the comment is aligned:
[ --enable-say Enable say support])
2
3
4
5
6
7
8
9
10
11
# 第三步,代码实现
修改say.c文件。实现say方法。
找到PHP_FUNCTION(confirm_say_compiled),在其上面增加如下代码:
PHP_FUNCTION(say)
{
zend_string *strg;
strg = strpprintf(0, "hello word");
RETURN_STR(strg);
}
2
3
4
5
6
找到PHP_FE(confirm_say_compiled, 在上面增加如下代码:
PHP_FE(say, NULL)
修改后的代码如下:
const zend_function_entry say_functions[] = {
PHP_FE(say, NULL) /* For testing, remove later. */
PHP_FE(confirm_say_compiled, NULL) /* For testing, remove later. */
PHP_FE_END /* Must be the last line in say_functions[] */
};
/* }}} */
2
3
4
5
6
7
# 第四步,编译安装
编译扩展的步骤如下:
$ phpize
$ ./configure
$ make && make install
2
3
修改php.ini文件,增加如下代码:
[say]
extension = say.so
2
然后执行,php -m 命令,在输出的内容中,会看到say字样。
# 第五步,调用测试
写一个脚本,调用say方法,看输出的内容是否符合预期。
# 总结
总结一下编写扩展的步骤:
- **初始化:**通过ext目录下的ext_skel脚本生成扩展的基本扩展ext_skel -extname;
- **修改config.m4配置:**设置编译配置参数、设置扩展的源代码、依赖库/函数检查等;
- **编写扩展要实现的功能:**按照PHP扩展的格式及PHP提供的api编写功能;
- **生成configure:**扩展编写完成后执行phpsize脚本生成configure及其他配置文件;
- 编译&安装:./configure、make、make install,然后将扩展的.so路径添加到php.ini中;