1 前言
- 主要目的让大家了解一下怎么做PHP语言的扩展,仍然以上次叙述的Perl的扩展的例子——获得IP来源地址为例。
2 前提条件
- 假设你已经拥有了 LAMP (Linux+Apache+Mysql+PHP的缩写),并假设其安装路径分别是:
1 |
Apache:/usr/local/apache<br />Mysql:/usr/local/mysql<br />PHP:/usr/local/php<br /> |
- 目前所叙述的实例可能与 mysql 没有太多的关系,所以无关紧要,但是 PHP 是必须安装的;
- 另外,需要 PHP 的源码,如果你没有,可以去 http://www.php.net 获取 PHP 源码,其源码路径位:
1 |
PHPSrc:/usr/local/php/src<br /> |
- 末了说句,本实例完全在 Linux 64 位平台下演示,若有出入,可以随时联系笔者。
3 流程及步骤
- 其实制作PHP扩展非常简单, ext_skel 命令的帮助已说明得非常详细,如下所示:
1 |
[Cnangel@localhost ~]$cd /usr/local/php/src/ext<br />[Cnangel@localhost ext]$ext_skel --help<br />./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]]<br /> [--skel=dir] [--full-xml] [--no-help]<br /><br /> --extname=module module is the name of your extension<br /> --proto=file file contains prototypes of functions to create<br /> --stubs=file generate only function stubs in file<br /> --xml generate xml documentation to be added to phpdoc-cvs<br /> --skel=dir path to the skeleton directory<br /> --full-xml generate xml documentation for a self-contained extension<br /> (not yet implemented)<br /> --no-help don't try to be nice and create comments in the code<br /> and helper functions to test if the module compiled<br /> |
- 尝试着创建一个扩展 getaddress :
1 |
[Cnangel@localhost ext]$ext_skel --extname=getaddress<br />Creating directory getaddress<br />Creating basic files: config.m4 config.w32 .cvsignore getaddress.c php_getaddress.h CREDITS EXPERIMENTAL tests/001.phpt getaddress.php [done].<br /><br />To use your new extension, you will have to execute the following steps:<br /><br />1. $ cd ..<br />2. $ vi ext/getaddress/config.m4<br />3. $ ./buildconf<br />4. $ ./configure --[with|enable]-getaddress<br />5. $ make<br />6. $ ./php -f ext/getaddress/getaddress.php<br />7. $ vi ext/getaddress/getaddress.c<br />8. $ make<br /><br />Repeat steps 3-6 until you are satisfied with ext/getaddress/config.m4 and<br />step 6 confirms that your module is compiled into PHP. Then, start writing<br />code and repeat the last two steps as often as necessary.<br /> |
- 上面的后续的帮助步骤已经说得比较清楚,一共 8 个步骤;
4 详细过程描述
4.1 建立扩展目录
- 方便的 ext_skel 命令能帮助建立一个扩展模型 getaddress ,系统会自动建立一个 getaddress 目录,其文件会相应的生成在 getaddress 目录内;
4.2 描述及编辑config.m4
- 用 ext_skel 建立的目录里面一般有 config.m4 这个文件,这里面有一些基础的宏定义:
- dnl 是注释;
- PHP_ARG_WITH 或者 PHP_ARG_ENABLE 指定了PHP扩展模块的工作方式;
- PHP_REQUIRE_CXX 用于指定这个扩展用到了C++;
- PHP_ADD_INCLUDE 指定PHP扩展模块用到的头文件目录;
- PHP_CHECK_LIBRARY 指定PHP扩展模块PHP_ADD_LIBRARY_WITH_PATH定义以及库连接错误信息等;
- PHP_SUBST 用于说明这个扩展编译成动态链接库的形式;
- PHP_NEW_EXTENSION 用于指定有哪些源文件应该被编译,文件和文件之间用空格隔开;
- 这里指定PHP扩展模块的工作方式为 PHP_ARG_ENABLE ,需要修改config.m4文件为:
1 |
[Cnangel@localhost getaddress]vim config.m4<br /> |
找到里面有类似几行:
1 |
dnl PHP_ARG_WITH(getaddress, for getaddress support,<br /> dnl Make sure that the comment is aligned:<br /> dnl [ --with-getaddress Include getaddress support])<br /><br /> dnl Otherwise use enable:<br /><br /> dnl PHP_ARG_ENABLE(getaddress, whether to enable getaddress support,<br /> dnl Make sure that the comment is aligned:<br /> dnl [ --enable-getaddress Enable getaddress support])<br /> |
修改成:
1 |
dnl PHP_ARG_WITH(getaddress, for getaddress support,<br /> dnl Make sure that the comment is aligned:<br /> dnl [ --with-getaddress Include getaddress support])<br /><br /> dnl Otherwise use enable:<br /><br /> PHP_ARG_ENABLE(getaddress, whether to enable getaddress support,<br /> Make sure that the comment is aligned:<br /> [ --enable-getaddress Enable getaddress support])<br /> |
- 除了修改 config.m4 外,还需要修改的文件有 getaddress.c 、 php_getaddress.h 两个文件,下面会说到该文件的修改。
4.3 创建configure文件
- 源码修改:进入源代码根目录,使用工具 buildconf 创建 configure 文件,其命令如下:
1 |
[Cnangel@localhost getaddress]cd /usr/local/php/src<br />[Cnangel@localhost src]./buildconf<br /> |
- 扩展修改:进入扩展目录,使用工具 phpize 创建 configure 文件,其目录如下:
1 |
[Cnangel@localhost getaddress]/usr/local/php/bin/phpize<br />Configuring for:<br />PHP Api Version: 20041225<br />Zend Module Api No: 20060613<br />Zend Extension Api No: 220060519<br /> |
4.4 创建Makefile文件
- 上个步骤中创建了 configure 文件,记住,一般在类unix系统中, configure 文件是一个可执行文件,用来创建编译过程中所用到的make命令所需要的Makefile文件,其创建过程如下:
1 |
./configure --enable-getaddress --with-apxs=/usr/local/apache/bin/apxs --with-php-config=/usr/local/php/bin/php-config<br /> |
- 注意,如果你写的扩展与apache有关,则需要关联apxs,产生apache的modules。
4.5 编译过程
- 编译过程是一个调试过程,出现错误了需要检查 config.m4 、 getaddress.c 、 php_getaddress.h 这几个文件是否编写正确,编译的过程十分简单,命令如下:
1 |
make<br /> |
4.6 安装扩展模块
- 安装扩展模块一般有两种安装,一种是直接:
1 |
make install<br /> |
- 结果会安装到: /usr/local/php/lib/php/extensions/no-debug-non-zts-20060613/ 目录,然后在 php.ini 里面通过 extension_dir 将该目录设置为php的扩展目录,并在php.ini中打开这个扩展:
1 |
extension=getaddress.so<br /> |
- 另外一种是直接复制。一般经过编译过程这个步骤后,会在 getaddress 目录下生成一个目录: modules ,该目录下面有一个已经编译好的.so文件,如果是静态编译,可能是.a或.la;把该文件复制到一个目录下,比如: /usr/loca/php/ext/ ,然后直接调用函数 dl 来调用其 api。
4.7 运行测试
- getaddress 目录下有一个 getaddress.php 文件专门用来测试你的扩展模块是否正确运行,该文件即可以作为 CGI 来运行又可以当作脚本执行,命令如下:
1 |
[Cnangel@localhost getaddress]php -f getaddress.php<br />Functions available in the test extension:<br />confirm_getaddress_compiled<br /><br />Congratulations! You have successfully modified ext/getaddress/config.m4. Module getaddress is now compiled into PHP.<br /> |
4.8 增添PHP扩展模块函数
- 上述讲了这么多,这节才是最主要的。PHP扩展模块主要有三个方面的作用:
- 增加PHP基础函数没有的功能或更加 OOP(Object Oriented Programming) 的思想供工程调用;
- PHP的扩展一般是汇编、C/C++等编译性语言缩写,二进制运行消耗时间比解释性语言实现同样的功能少得多;
- 由于PHP扩展一般是二进制的,所以一般来说不开源,很方便的保护了版权,能够进行商业运用。
- 默认的 getaddress.c 中, zend_function_entry 是导出函数列表, zend_module_entry 描述了模块的信息。
- 编写代码应该注意:
- 如果是C++开发,记住把getaddress.c的后缀改为cpp,并用extern "C"来把c相关代码包起来;
5 Bugs
- 在64位机器上和32位机器上会出现很大的差异,主要在QQWry.c文件;
- 多个引用php函数会出现corp dump情况,具体原因还未查清;
6 实例代码
- 注意,修复一处Bug:将
1 |
Z_STRVAL_P(file) == NULL<br /> |
代替
1 |
Z_STRLEN_P(file) == 0<br /> |
7 总结
- 由于作者对C指针运用不太熟练,所以在操作指针时,往往会出现corp dump,这方面有待加强;
- 就PHP扩展来说,涵盖的面比较广,需要丰富的知识面做铺垫才能做好一个优秀的扩展。