您的位置:首页 > 编程语言 > C语言/C++

用C++扩展PHP

2007-03-08 08:30 489 查看
[align=left]用C++扩展PHP [/align]
[align=left] [/align]
[align=left]第1节. 开始之前 [/align]
[align=left]文章所描述的主要是在UNIX的PHP环境上的。[/align]
[align=left]一些说明... [/align]
[align=left]$PHP_HOME 是指你的PHP源代码的位置,如:你解开的PHP源代码包所放的位置。[/align]
[align=left]我们用来做例子的模块叫做php5cpp. [/align]
[align=left] [/align]
[align=left] 第2节.安装 [/align]
[align=left]在你用C++编写PHP扩展前,你先要搭建一个基本的扩展模块的架构。在UNIX下,你可以运行一个在 $PHP_HOME/ext 下叫做ext_skel 的shell脚本。先切换到 $PHP_HOME/ext 目录和执行那个shell脚本,并用 --extname 参数为你的扩展模块命名。 [/align]
[align=left]jay@monty ~ $ cd setup/php/php-5.x.x/ext[/align]
[align=left]jay@month ext $ ./ext_skel --extname php5cpp[/align]
[align=left]这样,在$PHP_HOME/ext/php5cpp下,我们已经有了一个基本的PHP扩展模块架构。唯一的问题是,它是为C搭建的,而不是为C++。 [/align]
[align=left] [/align]
[align=left]第3节.修改config.m4 [/align]
[align=left]现在我们要修改那个扩展模块的config.m4 文件以支持C++。 [/align]
[align=left]你不需要做太多的改动,要做的只是告诉编译PHP的系统,你的模块是使用C++的,而且需要连接C++标准库。下边是一个删去自动生成的注释后,php5cpp 扩展模块的config.m4文件的例子: [/align]
[align=left](红色部分是新加的,第四句中原来的.c文件改为.cpp)[/align]
[align=left]PHP_ARG_ENABLE(php5cpp, for php5cpp support,[/align]
[align=left][ --enable-php5cpp Enable php5cpp support])[/align]
[align=left] [/align]
[align=left]if test "$PHP_php5cpp" != "no" ; then[/align]
[align=left] PHP_REQUIRE_CXX()[/align]
[align=left]PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", PHP5CPP_SHARED_LIBADD) [/align]
[align=left] PHP_SUBST(PHP5CPP_SHARED_LIBADD) [/align]
[align=left] PHP_NEW_EXTENSION(php5cpp, php5cpp.cpp, $ext_shared)[/align]
[align=left]fi[/align]
[align=left]注意其中的PHP_REQUIRE_CXX(),和php5cpp.c 已经变成了 php5cpp.cpp 了。 [/align]
[align=left] [/align]
[align=left] 第4节.编写代码 [/align]
[align=left]修改完config.m4 后,你可以编写代码了。记住把php5cpp.c 修改成C++文件的名字。根据前边 config.m4的修改,在这里我们把它改成 php5cpp.cpp. [/align]
[align=left]现在你可以开始编写你的代码了。但是你如果现在编译这个扩展代码的话,可能会生一个so,并且不会产生任何编译错误,但是并不能在PHP中使用。如果你把它静态编译进PHP,则会产生连接错误。这是因为C和C++的变量空间的不一致引起的 (PHP是使用C来编写,你的扩展使用C++来编写) 。 [/align]
[align=left]修改的方法就是,你要告诉你的扩展模块,将把一些PHP API函数当成C函数来对待(他们是用C来写的),而不是当成C++。 [/align]
[align=left]你需要把一些代码用 BEGIN/END_EXTERN_C()包起来。你的php5cpp.cpp 可能要写成像下边的样子: [/align]
[align=left]extern "C" {[/align]
[align=left]#include "php.h"[/align]
[align=left]#include "php_ini.h"[/align]
[align=left]#include "ext/standard/info.h"[/align]
[align=left]} //这里不用把自动生成的那个头文件包含在extern里面[/align]
[align=left] [/align]
[align=left]#ifdef COMPILE_DL_PHP5CPP[/align]
[align=left]BEGIN_EXTERN_C()[/align]
[align=left]ZEND_GET_MODULE(php5cpp)[/align]
[align=left]END_EXTERN_C()[/align]
[align=left]#endif[/align]
[align=left] [/align]
[align=left]一般地,我们是用 BEGIN/END_EXTERN_C() 来包起头文件的那些内容,如对 ZEND_GET_MODULE(php5cpp)那样。但是在引用声明 BEGIN/END_EXTERN_C() 的 zend.h文件前,可以通过使用extern "C" 来达到相同的作用。 [/align]
[align=left] [/align]
[align=left] 第5节.编译扩展模块 [/align]
[align=left]到你的模块的目录下,运行phpize 命令(假设你已经安装了PEAR, [/align]
[align=left]这里要装AUTOCONF,我运行的时候因为没安装这个所以phpize运行失败.[/align]
[align=left]我用ports下自带的autoconf259安装,安装完后运行[/align]
[align=left]ln -s /usr/local/bin/autoconf253 /usr/local/bin/autoconf[/align]
[align=left]ln -s /usr/local/bin/autoheader253 /usr/local/bin/autoheader[/align]
[align=left]不然运行phpize的时候不认[/align]
[align=left]),它会为你的模块创建一个 configure 脚本。[/align]
[align=left]然后运行[/align]
[align=left]./Configure[/align]
[align=left]make [/align]
[align=left]make install[/align]
[align=left]a.如果你想让你的模块自动加载,你要修改php.ini 以加载正确的文件。如:加上类似的一行:extension=php5cpp.so 。 [/align]
[align=left](这里奇怪的是我自己指定的extention目录,将编译好的SO加入到里面,PHP加载不进去,使用系统默认的LIB目录才行)[/align]
[align=left]b.如果是在PHP代码里动态加载,可以模仿以下代码(取自php5cpp.php)[/align]
[align=left]<?php[/align]
[align=left]$br = (php_sapi_name() == "cli")? "":"<br>";[/align]
[align=left]if(!extension_loaded('php5cpp')) {[/align]
[align=left] dl('php5cpp.' . PHP_SHLIB_SUFFIX);[/align]
[align=left]}[/align]
[align=left]$module = 'php5cpp';[/align]
[align=left]$functions = get_extension_funcs($module);[/align]
[align=left]echo "Functions available in the test extension:$br/n";[/align]
[align=left]foreach($functions as $func) {[/align]
[align=left] echo $func."$br/n";[/align]
[align=left]}[/align]
[align=left]echo "$br/n";[/align]
[align=left]$function = 'confirm_' . $module . '_compiled';[/align]
[align=left]if (extension_loaded($module)) {[/align]
[align=left] $str = $function($module);[/align]
[align=left]} else {[/align]
[align=left] $str = "Module $module is not compiled into PHP";[/align]
[align=left]}[/align]
[align=left]echo "$str/n";[/align]
[align=left]?>[/align]
现在你的PHP扩展模块已经编译好了。试着运行一下在模块目录下自动生成的php5cpp.php ,看看是不是一切正常?:)

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/app ext/testext/testext.php来测试模块是否正常. 不过这之前要先把testext.so复制到某个特定的目录下,这个目录不同的系统不一样,你可以根据运行/sapi/cli/app ext/testext/testext.php得到的错误信息来知道这个目录.
4. 如果测试没有问题,就可以安装到正式的PHP里去了,通过修改php.ini文件然后重起Apache就可以.(我看不重启也行)
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之类的命令调试的.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: