如何编写PHP扩展
如何编写PHP扩展
2010年06月14日
如何编写PHP扩展
翻译:taft@wjl.cn
Ver 0.1
最后修改日期2006/1/19
WJL Studio @ wjl.cn 2006
目录 简介快速开始
内存管理
从PHP函数中返回值
完成self_concat()
实例小结
包裹第三方的扩展
编写利用资源的第一个PHP函数
全局变量
添加自定义INI指令
线程安全资源管理宏
总结
词汇表简介 PHP取得成功的一个主要原因之一是她拥 有大量的可用扩展。web开发者无论有何种需求,这种需求最有可能在PHP发 行包里找到。PHP发行包包括支持各种数据库,图形文件格式,压缩,XML技术扩展在内的许多扩展。
扩展API的引入使PHP3取得了巨大的进展,扩展API机制使PHP开发社区很容易的开发出几十 种扩展。现在,两个版本过去了,API仍然和PHP3时的非常相似。扩展主要 的思想是:尽可能的从扩展编写者那里隐藏PHP的内部机制和脚本引擎本身,仅仅需要 开发者熟悉API。
有两个理由需要自己 编写PHP扩展。第一个理由是:PHP需 要支持一项她还未支持的技术。这通常包括包裹一些现成的C函数库,以便提供PHP接口。例如,如果一个叫FooBase的数据库已推出市场,你需要建立一个PHP扩展帮助你从PHP里调用FooBase的C函数库。这个工作可能仅由一个人完成,然后被整个PHP社区共享(如果你愿意的话)。第二个不是很普遍的理由是:你需要从性能或功能的原因考虑来编写一些商 业逻辑。
如果以上的两个理由都和你没什么 关系,同时你感觉自己没有冒险精神,那么你可以跳过本章。
本章教你如何编写相 对简单的PHP扩展,使用一部分扩展API函 数。对于大多数打算开发自定义PHP扩展开发者而言,它含概了足够的资料。学习一门编程课程的最好方法之一就是动手做一些极其简单的例 子,这些例子正是本章的线索。一旦你明白了基础的东西,你就可以在互联网上通过阅读文挡、原代码或参加邮件列表新闻组讨论来丰富自己。因此,本章集中在让 你如何开始的话题。在UNIX下一个叫ext_skel的脚本被用于建立扩展的骨架,骨架信息从一个描述扩展接口的定义文件中取得。因此你需要利用UNIX来建立一个骨架。Windows开发者可以使用Windows ext_skel_win32.php代替ext_skel。
然而,本章关于用你 开发的扩展编译PHP的指导仅涉及UNIX编 译系统。本章中所有的对API的解释与UNIX和Windows下开发的扩展都有联系。
当你阅读完这章,你 能学会如何 建立一个简单的商业逻 辑扩展。 .建议个C函数库的包裹扩展,尤其是有些标准C文件操作函数比如fopen()
快 速开始
本节没有介绍关于脚 本引擎基本构造的一些知识,而是直接进入扩展的编码讲解中,因此不要担心你无法立刻获得对扩展整体把握的感觉。假设你正在开发一个网站,需要一个把字符串 重复n次的函数。下面是用PHP写 的例子:
function self_concat($string, $n)
{
$result = "";
for ($i = 0; $i 函数,而且还要传给函数很长的字符串和大值n。这意 味着在脚本里有相当巨大的字符串连接量和内存重新分配过程,以至显著地降低脚本执行速度。如果有一个函数能够更快地分配大量且足够的内存来存放结果字符 串,然后把$string重复n次,就不需要在每次循环迭代中分 配内存。
为扩展建立函数的第 一步是写一个函数定义文件,该函数定义文件定义了扩展对外提供的函数原形。该例中,定义函数只有一行函数原形self_concat() :
string self_concat(string str, int n)
函数定义文件的一般 格式是一个函数一行。你可以定义可选参数和使用大量的PHP类型,包括: bool, float, int, array等。
保存为myfunctions.def文件至PHP原代码目录树下。
该是通过扩展骨架(skeleton)构造器运行函数定义文件的时机了。该构造器脚本叫ext_skel,放在PHP原代码目录树的ext/目录下(PHP原码主目录下的README.EXT_SKEL提供了更多的信息)。假设你把函数定义保存在一个叫做myfunctions.def的文件里,而且你希望把扩展取名为myfunctions,运行下面的命令来建立扩展骨架
./ext_skel --extname=myfunctions --proto=myfunctions.def
这个命令在ext/目 录下建立了一个myfunctions/目录。你要做的第一件事情也许就是编译该骨架,以便编写和测试实际的C代码。编译扩展有两种方法:
作为一个可装载模块或者DSO(动态共享对象) 静态编译到PHP
因为第二种方法比较 容易上手,所以本章采用静态编译。如果你对编译可装载扩展模块感兴趣,可以阅读PHP原 代码根目录下的README.SELF-CONTAINED_EXTENSIONS文件。为了使扩展能够被编译,需要修改扩展目录ext/myfunctions/下的config.m4文件。扩展没有 包裹任何外部的C库,你需要添加支持--enable-myfunctions配置开关到PHP编 译系统里( with-extension 开关用于那些需要用户指定相关C库路径的扩展)。可以去掉自动生成的下面两行的注释来开启这个配置。
PHP_ARG_ENABLE(myfunctions, whether to enable myfunctions support,
[ --enable-myfunctions Include myfunctions support])
现在剩下的事情就是在PHP原代码树根目录下运行./buildconf,该命令会生成一个新的配置脚本。通过查看./configure --help输出信息,可以检查新的配置选项是否被包含到配置文件中。现在,打开你喜好的配置选项开关和--enable-myfunctions重新配置一下PHP。 最后的但不是最次要的是,用make来重新编译PHP。
ext_skel应该把两个PHP函 数添加到你的扩展骨架了:打算实现的self_concat()函数和用于检测myfunctions 是否编译到PHP的confirm_myfunctions_compiled()函数。完成PHP的 扩展开发后,可以把后者去掉。
运行这个脚本会出现类似下面的输 出:
"Congratulations! You have successfully modified ext/myfunctions
config.m4. Module myfunctions is now compiled into PHP."
另外,ext_skel脚本生成一个叫myfunctions.php的脚本,你也可以利用它来验证扩展是否被成功地编译到PHP。它会列出该扩展所支持的所有函数。
现在你学会如何编译扩展了,该是真正地研究self_concat()函数的时候了。
下面就是ext_skel脚本生成的骨架结构:
/* {{{ proto string self_concat(string str, int n) */ PHP_FUNCTION(self_concat) } char *str = NULL; int argc = ZEND_NUM_ARGS(); int str_len; long n; if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE) return; php_error(E_WARNING, "self_concat: not yet implemented"); } /* }}} */ 自动生成的PHP函数周围包含了一些注释,这些注释用于自动生成代码文档和vi、Emacs等编辑器的代码折叠。函 数自身的定义使用了宏PHP_FUNCTION(),该宏可以生成一个适合于Zend引 擎的函数原型。逻辑本身分成语义各部分,取得调用函数的参数和逻辑本身。
为了获得函数传递的参数,可以使用zend_parse_par