文件名称:开发PHP扩展详细教程
文件大小:911KB
文件格式:RAR
更新时间:2012-11-18 09:59:22
PHP 扩展 开发
如果用PHP不能再满足你的需求,最好的办法就是开发PHP扩展。这有一些好处:
1、增加自己的特殊功能。
2、保护自己的专利代码。
这是几年前的一篇英文文章,现在已被翻译成中文版的。
作者应该是hshq_cn。
链接是:http://bbs3.chinaunix.net/thread-1028798-1-1.html。
现我将此转变为PDF文件,仅有兴趣者参阅。同时非常感谢
原作者及hshq_cn,给我们带来的这么好的资料。里面还有一个幻灯片的,也是很有帮助的文档。另外,再提供一篇相关的文章(http://www.programbbs.com/doc/4083.htm):
编写php的extension实例的方法
所属类别:JSP
推荐指数:★★☆
文档人气:161
本周人气:1
发布日期:2008-7-3
一、说明
前端时间因为客户的原因折腾了一下asp的扩展,在ATL的帮助下写一个asp的模块还是很容易的。不巧的时刚刚折腾完asp的COM就碰到另一个客户的问题。客户想给系统集成ICBC的接口,但是用ICBC的接口需要用他们的提供的库函数去
1. sign对发送的数据进行签名
2. getcertid获取用户证书的版本
3. verifySign对签名后的数据进行验证
问题是ICBC只给了现成的COM组件,意味在只能在Win的主机上使用。俺们公司只有linux的主机,在*nix上就要自己想办法调用ICBC给的静态库了。对此我们有两个想法
1.用ICBC的静态库做一个独立的执行文件,用PHP的系统调用函数来执行这个独立的执行文件
2.将ICBC的静态库做出一个PHP的扩展
方法一应该比较简单,但是远不如方法二的灵活。搞成PHP的扩展,只要服务器编译一次,服务器上的所有客户都可以用的。
有ASP的前科,俺觉得搞个PHP的也不是什么难事。操起google搜了一通,结果发现Zend已经写了一个如何编写php extension的教程:
http://devzone.zend.com/article/1021-Extension-Writing-Part-I-Introduction-to-PHP-and-Zend
浏览完牛人的大作,更是信心十足,php的扩展其实很简单,分七步走:
1. 制作编译配置文件:config.m4
2. 执行phpize生成扩展的框架
3. 在生成的php_xxx.h中声明自己写的函数
4. 在xxx.c中实现自己的函数
5. 编译自己的扩展
6. 将生成的xxx.so拷贝到php.ini中指定的extensions_dir
7. 在php.ini中打开xxx.so的调用
此问题问题唯一搞的地方就是在config.m4中折腾出正确的Makefile,因为Zend的教程中没有提到,俺自己也折腾了好久,才搞出来。
二、实际操作
1.建立工作环境
将php源码包解开,我的版本的php-4.4.4,转到源码包中的ext目录建立一个新的目录叫icbc,然后在icbc目录下touch三个文件config.m4、php_icbc.h、icbc.c
2.建立config.m4 内容如下:
PHP_ARG_ENABLE(icbc, whether to enable ICBC support,
[ --enable-icbc Enable ICBC support])
if test \"$PHP_ICBC\" = \"yes\"; then
AC_DEFINE(HAVE_ICBC, 1, [Whether you have ICBC])
if test -f ./libicbcapi.a; then
PHP_ADD_LIBRARY_WITH_PATH(icbcapi, ./, ICBCAPI_SHARED_LIBADD)
PHP_SUBST(ICBCAPI_SHARED_LIBADD)
AC_MSG_RESULT(checking for libicbcapi.a is OK)
else
AC_MSG_RESULT(libicbcapi.a not found)
AC_MSG_ERROR(Please make sure the libicbcapi.a is in current directory) [Page]
fi
PHP_NEW_EXTENSION(icbc, icbc.c, $ext_shared)
fi
第三行判断是否要启用icbc扩展,
第五行判断ICBC的静态库是否在当前目录(phpdir/ext/icbc)下
第六、七行将ICBC的静态库加入到编译环境中
3.在php_icbc.h中声明我们要导出的函数icbc_sign、icbc_vsign、icbc_getCertID
#ifndef PHP_ICBC_H
#define PHP_ICBC_H
extern zend_module_entry icbc_module_entry;
#define phpext_icbc_ptr &icbc_module_entry
#ifdef PHP_WIN32
#define PHP_ICBC_API __declspec(dllexport)
#else
#define PHP_ICBC_API
#endif
#ifdef ZTS
#include \"TSRM.h\"
#endif
PHP_MINIT_FUNCTION(icbc);
PHP_MSHUTDOWN_FUNCTION(icbc);
PHP_RINIT_FUNCTION(icbc);
PHP_RSHUTDOWN_FUNCTION(icbc);
PHP_MINFO_FUNCTION(icbc);
/*Modify youself here*/
PHP_FUNCTION(icbc_sign);
PHP_FUNCTION(icbc_vsign);
PHP_FUNCTION(icbc_getCertID);
/****End of Self control section***/
#ifdef ZTS
#define ICBC_G(v) TSRMG(icbc_globals_id, zend_icbc_globals *, v)
#else
#define ICBC_G(v) (icbc_globals.v)
#endif
#endif /* PHP_ICBC_H */
涉及到我们也就
PHP_FUNCTION(icbc_sign);
PHP_FUNCTION(icbc_vsign);
PHP_FUNCTION(icbc_getCertID);
其他的都是PHP各个状态的入口函数声明
4.编写这三个函数的实现:
#ifdef HAVE_CONFIG_H
#include \"config.h\"
#endif
#include \"php.h\"
#include \"php_ini.h\"
#include \"ext/standard/info.h\"
#include \"php_icbc.h\"
#include \"icbcapi.h\"
static int le_icbc;
zend_function_entry icbc_functions[] = {
PHP_FE(icbc_sign,NULL)
PHP_FE(icbc_vsign,NULL)
PHP_FE(icbc_getCertID,NULL)
{NULL, NULL, NULL} /* Must be the last line in icbc_functions[] */
};
zend_module_entry icbc_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
\"icbc\",
icbc_functions,
PHP_MINIT(icbc),
PHP_MSHUTDOWN(icbc),
PHP_RINIT(icbc), /* Replace with NULL if there’s nothing to do at request start */
PHP_RSHUTDOWN(icbc), /* Replace with NULL if there’s nothing to do at request end */ [Page]
PHP_MINFO(icbc),
#if ZEND_MODULE_API_NO >= 20010901
\"0.1\", /* Replace with version number for your extension */
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_ICBC
ZEND_GET_MODULE(icbc)
#endif
PHP_MINIT_FUNCTION(icbc)
{
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(icbc)
{
return SUCCESS;
}
PHP_RINIT_FUNCTION(icbc)
{
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(icbc)
{
return SUCCESS;
}
PHP_MINFO_FUNCTION(icbc)
{
php_info_print_table_start();
php_info_print_table_header(2, \"icbc support\", \"enabled\");
php_info_print_table_end();
}
PHP_FUNCTION(icbc_sign)
{
char* src;
int srclen;
char* pkey;
int keylen;
char* keypass;
int keypasslen;
char* signedbuf;
int signedbuflen;
FILE* fp;
char key[2000];
int rcc;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,\"sss\",&src,&srclen,&pkey,&keylen,&keypass,&keypasslen) == FAILURE){
return;
}
fp = fopen(pkey,\"rb\");
if(fp == NULL)
{
return;
}
fseek(fp,2,SEEK_SET);
fread((void*)key,609,1,fp);
fclose(fp);
if(rcc = sign(src,srclen,key,607,keypass,&signedbuf,&signedbuflen) >= 0){
base64enc(signedbuf,signedbuflen,&signedbuf,&signedbuflen);
src = estrndup(signedbuf,signedbuflen);
if(signedbuf != NULL) infosec_free(signedbuf);
RETURN_STRING(src,1); [Page]
}else{
RETURN_LONG(rcc);
}
}PHP_FUNCTION(icbc_vsign)
{
char* src;
int srclen;
char* cert;
int certlen;
char* vsignedbuf;
int vsignedbuflen;
FILE* fp;
char vcert[2000];
int rcc;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,\"sss\",&src,&srclen,&cert,&certlen,&vsignedbuf,&vsignedbuflen) == FAILURE){
return;
}
fp = fopen(cert,\"rb\");
if(fp == NULL)
{
return;
}
fread((void*)vcert,1525,1,fp);
fclose(fp);
base64dec(vsignedbuf,vsignedbuflen,&vsignedbuf,&vsignedbuflen);
if(rcc = verifySign(src,srclen,vcert,1525,vsignedbuf,vsignedbuflen) >= 0){
if(vsignedbuf != NULL) infosec_free(vsignedbuf);
RETURN_TRUE;
}else{
if(vsignedbuf != NULL) infosec_free(vsignedbuf);
RETURN_LONG(rcc);
}
}
PHP_FUNCTION(icbc_getCertID)
{
char* arg;
char* certid;
int arg_len,certidlen;
FILE* fp;
char cert[2000];
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,\"s\", &arg,&arg_len) == FAILURE){
return;
}
fp = fopen(arg,\"rb\");
if(fp == NULL)
{
return;
}
fread((void*)cert,1525,1,fp);
fclose(fp); [Page]
if(!getCertID(cert,1525,&certid,&certidlen))
{
arg = estrndup(certid,certidlen);
if(certid != NULL) infosec_free(certid);
RETURN_STRING(arg,1);
}else{
return;
}
}
先在zend_function_entry icbc_functions[]数组中放入我们的要实现的函数名,然后是一堆php各个状态入口函数,详情请看Zend的教程。最后是在PHP_FUNCTION宏定义中放我们声明函数的具体实现。具体实现时难点也就是参数的传入和结果传出,还好PHP已经为我们做了很好的抽象。在Zend的教程中也有详尽的说明,俺就不啰嗦了。关键代码照搬icbc的test.c就行了。
5.编译安装我们的库
先将ICBC的头文件考到当前目录,重命名为icbcapi.php,将静态库也cp过来,重命名为*nix的标准形式libicbcapi.a,然后运行
phpize
生成configure,运行
./configure --enable-icbc
生成Makefile,这里有一个很搞的地方,在生成的Makefile中最后一句中指定ICBC静态库的地方错了,正确的应该是(红色标记地方):
$(LIBTOOL) --mode=link $(CC) $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) -o $@ -export-dynamic -avoid-version -prefer-pic -module -rpath $(phplibdir) $(EXTRA_LDFLAGS) $(shared_objects_icbc) $(ICBCAPI_SHARED_LIBADD)
改好Makefile后就可以执行
make
如果一切顺利的话会在modules中得到我们的icbc.so,将我们的icbc.so拷贝到/usr/local/lib/php/extensions目录下,然后在php.ini中确认extensions_dir的值是/usr/local/lib/php/extensions,然后加入这句话
extension=icbc.so
重启apache后,就可以在php中直接调用这三个函数了
6.测试程序,要将测试的证书和key文件放到php测试文件的当前目录
\";
$b64sdata = icbc_sign($src,$key,$passwd);
echo \"The string \".$src.\" encrypt by icbc api is \".$b64sdata.\"(base64 encoded)
\";
echo \"Now we check it weather is correct....
\";
if(icbc_vsign(\"zzz\",$cert,$b64sdata)){
echo \"The signtrue to \".$src.\" is right!!
\"; [Page]
}else{
echo \"The signtrue to \".$src.\" is wrong!!
\";
exit();
}
?>
【文件预览】:
PHP_Extensions_Writing.pdf
PHP_Extension_Writing.pdf