设为首页 收藏本站
查看: 536|回复: 0

[经验分享] 编写php的extension实例

[复制链接]

尚未签到

发表于 2017-3-21 10:23:02 | 显示全部楼层 |阅读模式
 
一、说明
前端时间因为客户的原因折腾了一下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)
  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 */
    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);
    }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);

    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测试文件的当前目录
<?php
$realpath = dirname(__FILE__);
$key = $realpath."/user.key";
$cert = $realpath."/user.crt";
$src = "zzz";
$passwd = "12345678";

echo "The Cert file information is ";
echo icbc_getCertID($cert);
echo "<br>";
$b64sdata = icbc_sign($src,$key,$passwd);

echo "The string ".$src." encrypt by icbc api is ".$b64sdata."(base64 encoded)<br>";
echo "Now we check it weather is correct....<br>";

if(icbc_vsign("zzz",$cert,$b64sdata)){
   echo "The signtrue to ".$src." is right!!<br>";
}else{
   echo "The signtrue to ".$src." is wrong!!<br>";
   exit();
}
?>



 
 

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-352873-1-1.html 上篇帖子: PHP注释规范 下篇帖子: PHP系列之环境搭建
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表