`
tolys
  • 浏览: 115606 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

编写php的extension实例

阅读更多
 

一、说明

前端时间因为客户的原因折腾了一下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的教程:
 
 
 
浏览完牛人的大作,更是信心十足,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();
}
?>

 

 

分享到:
评论

相关推荐

    编写 PHP Extension.doc

    本篇文章将详细探讨如何编写PHP扩展,包括PHP变量的存储、zval结构、引用计数等基础知识,以及HashTable数据结构和Extensions的编写流程。 ### 基础知识 #### 1. PHP 变量的存储 PHP中的所有变量都是通过一个名为...

    php_extension_writing

    理解如何在C代码中操作`HashTable`对于高效地编写PHP扩展至关重要。 ### 结论 通过深入了解`zval`结构、PHP数据处理方式、创建扩展的基本流程以及如何与数组和哈希表交互,你可以更好地掌握PHP扩展的开发。这不仅...

    PHP.rar_apache_apache php_extension_php 扩展

    2. **PHP扩展开发基础**:解释PHP扩展的结构,如zend_extension接口,以及如何使用C语言编写PHP扩展。 3. **PHP扩展编译与安装**:介绍如何编译自定义的PHP扩展,并将其添加到PHP的配置中,以便在运行时加载。 4. **...

    PHP Extension writing

    在PHP扩展中添加自定义对象涉及到创建和管理对象的实例。这通常包括定义对象结构体、初始化对象、析构对象等步骤。 1. **定义对象结构体**: 包含对象的具体属性和方法。 2. **初始化对象**: 设置对象的初始状态。 3...

    PHP Extension Writing之中文版.docx

    ### PHP Extension Writing之中文版知识点概述 #### 编写扩展 I:PHP 和 Zend 起步 ##### 引言 本文档旨在引导有兴趣为 PHP 开发扩展的开发者入门。假设读者已经熟悉 PHP 语言和 C 语言的基础知识。 ##### 扩展的...

    PHP实例开发源码—php探针 B-check.zip

    7. **PHP扩展**:探针可能检查已安装的PHP扩展,这涉及到如何使用`phpinfo()`函数和`extension_loaded()`函数。 8. **日志管理**:探针可能会生成日志文件,学习如何正确记录和分析日志数据。 9. **性能优化**:...

    PHP实例开发源码-NNABC (PHP扩展).zip

    【PHP实例开发源码-NNABC (PHP扩展)】是一个以PHP编程语言为基础的源代码集合,主要用于展示和学习PHP扩展的开发。PHP扩展是PHP核心功能的增强,它们允许开发者添加新的函数、数据类型甚至是内建类库,以满足特定的...

    PHP实例开发源码—EXT+ PHP班级通讯录.zip

    EXT在这里可能指的是PHP的Extension(扩展),它可能是指使用了某种特定的PHP扩展库来增强功能或性能。 首先,通讯录系统通常基于Web,因此我们需要了解PHP作为服务器端脚本语言如何与HTML、CSS和JavaScript等前端...

    开发PHP扩展详细教程

    编写php的extension实例的方法 所属类别:JSP 推荐指数:★★☆ 文档人气:161 本周人气:1 发布日期:2008-7-3 一、说明 前端时间因为客户的原因折腾了一下asp的扩展,在ATL的帮助下写一个asp的模块还是很容易的...

    PHP 调用 Delphi 开发的 COM 组件实例代码

    文件中,取消对`extension=php_com_dotnet.dll`的注释(对于Windows系统),然后重启PHP服务。 4. **调用COM组件**:在PHP代码中,你可以使用`com_load`函数加载注册的COM组件,然后通过实例化对象来调用其方法。...

    php5.6下的redis扩展(redis/php_redis.dll/php_igbinary.dll)windows环境

    最后,测试配置是否成功,可以通过在PHP脚本中编写简单的测试代码,例如: ```php &lt;?php $redis = new Redis(); $redis-&gt;connect('127.0.0.1', 6379); $redis-&gt;set('test_key', 'Hello, Redis!'); echo $redis-&gt;get...

    linux+php+smarty模板的例子.zip

    6. **测试**:编写`test.php`,使用Smarty实例化对象,分配变量,并渲染`test.tpl`模板。可以通过访问`aa.html`来查看结果。 标签“linux”,“php”,“smarty”进一步强调了这个示例项目涉及的技术栈。 压缩包内...

    php写扩展文档

    本文将通过实例介绍如何开发一个简单的PHP扩展——“Hello World”扩展,并逐步讲解整个过程。 #### 二、准备工作 首先,我们需要准备一个适合开发PHP扩展的环境。本文示例基于如下环境: - **操作系统**:CentOS...

    PHP实例开发源码—Flarum PHP轻论坛网站源码.zip

    **PHP实例开发源码—Flarum PHP轻论坛网站源码** Flarum是一款基于PHP开发的现代化轻论坛系统,以其简洁、快速和互动性强的特点受到开发者和社区管理员的青睐。这个压缩包文件包含了Flarum论坛的完整源码,为PHP...

    Jquery AjaxUpload实现文件上传实例 PHP版

    在这个“Jquery AjaxUpload实现文件上传实例 PHP版”中,我们将探讨如何使用jQuery的AjaxUpload插件与PHP后端进行配合,实现实时的文件上传功能。 首先,我们需要在HTML页面中创建一个用于选择文件的input元素和一...

    php使用PDO操作MySQL数据库实例

    配置文件中启用了PDO扩展(例如`extension=php_pdo.dll`)以及针对特定数据库的扩展,如PDO_MYSQL(`extension=php_pdo_mysql.dll`)。 代码示例展示了如何使用PDO进行基本的CRUD(创建Create、读取Read、更新...

    PHP调用VC编写的COM组件实例

    总结来说,本文通过实例讲解了如何在Windows环境下通过PHP调用VC编写的COM组件来完成特定功能。这种方法简化了某些复杂功能的实现,但同时也受到平台限制。开发者在实际应用中需要权衡开发便利性与平台兼容性的问题...

    EmptyExtension.zip

    PHP C++(phpcpp)是一个强大的库,它使得用C++编写PHP扩展变得相对简单。本文将深入探讨如何使用phpcpp创建一个名为“EmptyExtension”的空扩展包,并通过此过程讲解PHP扩展开发的基础知识。 首先,我们需要理解...

    phpredis-develop.zip

    5. 在代码中通过`extension=redis`加载扩展,并通过`new Redis()`创建客户端实例。 总结,PHPRedis是一个强大的工具,使得PHP开发者能够充分利用Redis的高性能特性。通过“phpredis-develop.zip”,我们可以轻松地...

Global site tag (gtag.js) - Google Analytics