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扩展为例):

1
gitlib@devops:~/php-7.1.31/ext$ ./ext_skel --extname=say

执行完之后在ext目录下新生成一个扩展目录,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
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

主要文件说明如下:

  • config.m4:autoconf规则的编译配置文件
  • include:依赖库的include头文件
  • say.c:扩展源码
  • php_say.c: 扩展源码头文件

这个脚本主要生成来编译需要的配置及扩展的基本结构,初步生成的这个扩展可以成功编译、安装和使用,实际开发中我们可以用这个脚本生成一个基本结构,然后根据具体的需要完善。

phpize

phpsize是扩展编译必用的一个脚本,主要是操作复杂的autoconf/automake/autoheader/autolocal等系列命令,用于生成扩展的configure文件。

示例:

1
2
3
4
5
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

实践案例

示例中,我们将实现如下功能:

1
2
3
<?php
echo say();
?>

输出内容:

1
2
$ php ./test.php
$ hello word

在扩展中实现一个say方法,调用say方法后,输出 hello word。

第一步:生成代码

PHP为我们提供了生成基本代码的工具 ext_skel。这个工具在PHP源代码的./ext目录下。

1
2
$ cd php_src/ext/
$ ./ext_skel --extname=say

extname参数的值就是扩展名称,执行ext_skel命令后,这样在当前目录下会生成一个与扩展名一样的目录。

第二步,修改config.m4配置文件

1
2
$ cd ./say
$ vim ./config.m4

config.m4的作用就是配合phpize工具生成configure文件。configure文件是用于环境检测的。检测扩展编译运行所需的环境是否满足,现在开始修改config.m4文件。

打开config.m4文件后,你会发现这样一段文字:

1
2
3
4
5
6
7
8
9
10
11
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])

其中,dnl 是注释符号。上面的代码说,如果你所编写的扩展如果依赖其它的扩展或者lib库,需要去掉PHP_ARG_WITH相关代码的注释。否则,去掉 PHP_ARG_ENABLE 相关代码段的注释。我们编写的扩展不需要依赖其他的扩展和lib库。因此,我们去掉PHP_ARG_ENABLE前面的注释。去掉注释后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
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])

第三步,代码实现

修改say.c文件。实现say方法。

找到PHP_FUNCTION(confirm_say_compiled),在其上面增加如下代码:

1
2
3
4
5
6
PHP_FUNCTION(say)
{
zend_string *strg;
strg = strpprintf(0, "hello word");
RETURN_STR(strg);
}

找到PHP_FE(confirm_say_compiled, 在上面增加如下代码:

1
PHP_FE(say, NULL)

修改后的代码如下:

1
2
3
4
5
6
7
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[] */
};

/* }}} */

第四步,编译安装

编译扩展的步骤如下:

1
2
3
$ phpize
$ ./configure
$ make && make install

修改php.ini文件,增加如下代码:

1
2
[say]
extension = say.so

然后执行,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中;
有用就打赏一下作者吧!