phpのextensionインスタンスの作成



一、説明
フロントエンド時間はお客様の都合でaspの拡張を振り回し、ATLの助けでaspのモジュールを書くのは簡単です.あいにくaspを振り回したばかりのCOMは、別のお客様の問題に遭遇しました.お客様はシステムにICBCのインタフェースを統合したいと思っていますが、ICBCのインタフェースを使用するには、提供されたライブラリ関数を使用する必要があります.
 
1.sign送信データへの署名
2.getcertidユーザー証明書のバージョンを取得する
3.verifySign署名後のデータの検証
 
問題はICBCが既存のCOMコンポーネントだけを与えたことであり,Winのホストでしか使用できないことを意味する.私达の会社はlinuxのホストだけあって、*nixの上で自分でICBCのあげる静的なライブラリを呼び出す方法を考えなければなりません.私たちには2つの考えがあります
 
1.ICBCの静的ライブラリで独立した実行ファイルを作成し、PHPのシステム呼び出し関数でこの独立した実行ファイルを実行する
 
2.ICBCの静的ライブラリをPHPの拡張を行う
 
方法1は比較的簡単であるべきだが、方法2の柔軟性には及ばない.PHPの拡張を行うには、サーバーを一度コンパイルすれば、サーバー上のすべてのお客様が使用できます.
 
ASPの前科があって、PHPをするのも難しいことではないと思います.Googleを操作して検索した結果、Zendはphp extensionの作成方法のチュートリアルを書いたことがわかりました.
 
http://devzone.zend.com/article/1021-Extension-Writing-Part-I-Introduction-to-PHP-and-Zend
 
 
牛人の大作を見終わって、更に自信があって、phpの拡張は実はとても簡単で、7歩に分けて歩きます:
 
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である.m 4の中で正しいMakefileを振り回して、Zendの教程の中で言及していないため、私自身も長い間振り回して、やっとやりました.
 
二、実際の操作
1.作業環境の構築
 
phpソースパッケージを解いて、私のバージョンのphp-4.4.4、ソースパケットのextディレクトリに移動するicbcという新しいディレクトリを確立し、icbcディレクトリの下でtouchの3つのファイルconfigを作成する.m4、php_icbc.h、icbc.c
 
2.configを確立する.m 4の内容は以下の通りである.
 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

3行目はicbc拡張を有効にするかどうかを判断し、
5行目ICBCの静的ライブラリが現在のディレクトリ(phpdir/ext/icbc)の下にあるかどうかを判断する
6、7行目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.この3つの関数の実装を記述します.#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でこの3つの関数を直接呼び出すことができます
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();
}
?>