Jason
1. 必须的工具
2. 主要步骤
2.1. 创建
2.2. 编译
2.3. 调试
3. 总结
1. 必须的工具
如下工具是必须的:
1.
gcc. 我使用的是3.4.3
2.
autoconf. 我使用的是2.13-5. 奇怪的是2.59是不能用的,如果你的系统里安装的是这个版本,要先卸载,然后安装2.13
3.
PHP4的源代码。我使用的是PHP-4.4.4。在PHP的网站上可以下载到
2. 主要步骤
创建PHP的扩展很麻烦。熟悉其它脚本语言的人会觉得开发PHP的扩展真是罗嗦到了极点。不过给脚本写扩展的原理都是一样的:
导出一个特定名字的函数,然后通过这个函数返回关于这个扩展的全部信息,其中重要的有:
1.
名字,描述,版本信息
2.
模块所提供的功能,这个一般都是一个函数名和函数指针成对的数组
3.
通过一些函数实现脚本和扩展之间的数据交换.这方面各个语言都不相同,TCL是直接传递字符串,Python提供了PyArg_ParseTuple,Py_BuildValue等函数,Lua则通过一个堆栈来交换数据.
PHP提供了一系列的工具和宏来封装这个过程,可惜的是在我看来这反而让它更加复杂。
2.1. 创建
1.
将PHP-4 的源代码解压缩。下文将这个目录称为PHPSRC.
2.
进入PHPSRC/ext目录,执行./ext_skel扩展名。假设我们的扩展名字叫testext,那就是输入./ext_skeltestext. 这个程序将会为你在ext目录下创建如下内容:
1.
testext目录
2.
在testext目录下又包括:
1.
testext.c - 这是扩展模块的源文件
2.
php_testext.h - 头文件
3.
config.m4 - 用于autoconf的配置文件,autoconf会通过这个文件的内容决定应该如何编译链接这个模块。
4.
testext.php - 自动产生的测试脚本
5.
tests 目录 - 忽略
3.
然后我们必须简单的理解一下config.m4这个文件。
m4 is a macro processor, in the sense that it copiesits input to the output, expanding macros as it goes. Macros are either builtinor user-defined, and can take any number of arguments. Besides just doing macroexpansion, m4 has builtin functions for including
named files, running shellcommands, doing integer arithmetic, manipulating text in various ways,performing recursion, etc.... m4 can be used either as a front-end to acompiler, or as a macro processor in its own right.
The m4 macro processor is widely available on allUNIXes, and has been standardized by POSIX. Usually, only a small percentage ofusers are aware of its existence. However, those who find it often becomecommitted users. The popularity of GNU Autoconf, which
requires GNU m4 forgenerating configure scripts, is an incentive for many to install it, whilethese people will not themselves program in m4. GNU m4 is mostly compatiblewith the System V, Release 3 version, except for some minor differences. SeeCompatibility,
for more details.
Some people find m4 to be fairly addictive. Theyfirst use m4 for simple problems, then take bigger and bigger challenges,learning how to write complex sets of m4 macros along the way. Once reallyaddicted, users pursue writing of sophisticated m4 applications
even to solvesimple problems, devoting more time debugging their m4 scripts than doing realwork. Beware that m4 may be dangerous for the health of compulsive programmers.
以上内容来自http://www.gnu.org,所以我就不多说了. 总之PHP使用了这种技术来帮助我们构建扩展.
只记录一下对我们有用的东西:
1.
dnl是注释.好怪
2.
PHP_ARG_WITH或者PHP_ARG_ENABLE指定了PHP模块的工作方式,任选一种,我选择的是WITH
3.
PHP_REQUIRE_CXX()用于指定这个扩展用到了C++
4.
PHP_SUBST(SYSFILE_SHARED_LIBADD)用于说明这个扩展编译成动态链接库的形式(SYSFILE=ModuleName,漏了这句也不行,在测试的时候,可知道,这句使Makefile中多了一行:SYSFILE_SHARED_LIBADD=-l stdc++,没有这句编译虽然通过,但是在php加载**.so文件的时候,找不到扩展函数)
5.
PHP_ADD_LIBRARY(stdc++,"",SYSFILE_SHARED_LIBADD)用于将标准C++库链接进入扩展
6.
PHP_NEW_EXTENSION用于指定有哪些源文件应该被编译,文件和文件之间用空格隔开.
ext_skel默认生成的模块框架是针对C的,我们要使用C++,那以上的3,4,5,三个个宏就是必须的.另外还要把testext.c改名成testext.cpp,所以
PHP_NEW_EXTENSION原本包括的testext.c也要修改.
在使用C++之后要注意一个小问题,那就是php_testext.h这个文件可能被PHP的其他部分所引用到,而引用者很可能是一个.c文件,所以不能在php_testext.h里包含任何C++所独有的东西.比如标准模版库,类,或者bool类型等等.
4.
默认生成的testext.c(现在改名为testext.cpp)了,已经包含了一个测试性质的导出函数,通过那个例子就可以大概明白怎么添加自己的函数了. zend_function_entry是导出函数列表,zend_module_entry描述了模块的信息. 不过因为是C++了,还是有几点要修改的:
1.
#include "php.h"#include "php_ini.h" #include "ext/standard/info.h"
要用extern "C" 修饰.
2.
ZEND_GET_MODULE也要用extern "C"修饰
3.
zend_module_entry就是用于描述模块信息的类型,其实就是一个结构,其中第二项不知道是什么含意.在Windows上,这好像是一个描述信息,可以是一个长长的字符串,但是在Linux上似乎只能是跟模块名相同.
5.
之后的工作就是写自己的代码了.根据设计,提供对外的接口函数,然后写C/C++代码来实现.
2.2. 编译
1.
代码写好以后,到PHPSRC目录,运行./buildconf --force,除非m4文件有错或者autoconf安装有问题,否则这个步骤不会有什么错误,时间也不长. 通过删除configure文件,可以强迫buildconf重新生成一遍.
2.
运行./configure --disable-all--with-testext=shared
--disable-all是为了减少配置和下一步编译的时间,因为我们只需要自己的模块
--with-testext=shared指明了讲这个模块编译成动态链接库,而不是集成到PHP内部
3.
运行make
这个过程时间比较长,如果你的代码有问题,就会出现编译或者链接错误,根据提示自己修改就是了. 如果没什么问题,就会在PHPSRC/modules下面生成扩展文件testext.so,同时也会生成PHPSRC/sapi/cli/php. 可以运行./sapi/cli/appext/testext/testext.php来测试模块是否正常. 不过这之前要先把testext.so复制到某个特定的目录下,这个目录不同的系统不一样,你可以根据运行/sapi/cli/appext/testext/testext.php得到的错误信息来知道这个目录.
4.
如果测试没有问题,就可以安装到正式的PHP里去了,通过修改php.ini文件然后重起Apache就可以.
2.3. 调试
用gdb可以调试PHP的扩展.首先要写一个php脚本用来测试你想测试的功能, 把它放到PHPSRC目录下,比如叫test.php. 然后按如下步骤进行:
1.
gdb
2.
file ./sapi/cli/php
3.
用b命令下断点.因为是C++程序,所以扩展的导出函数名并不是我们输入的名字,可以用nm命令查看扩展的所有导出函数,找到编译器最终分配的名字.这个名字是肯定包含了你原本输入的名字的,所以很好找.
gdb会提示说这个符号在当前程序(php)里找不到,问你要不要在以后的shared library里找,回答Yes
4.
run test.php,之后就会停在断点上了.用n,s,display之类的命令调试的.
3. 总结
一句话,PHP的模块扩展比谁都麻烦,规则太多.其实我希望它提供一个文档,提供我在第二节开头描述的那些信息就够了. 不过也许是我学艺不精吧,对本来挺正常的事深恶痛绝