作者:Martin Streicher, 軟體開發人員, Pixel, Byte, and Comma
儘管編寫一個 PHP 擴充並不是很困難,而 SWIG 的確進一步簡化了這項任務,這主要是因為它自動化了結合 PHP 與 C 或 C++ 所需的工作。若給定對一個函數的描繪 — 函數的名稱及其形參 — SWIG 就會產生一個封裝程式來將 PHP 與低層代碼串連起來。
SWIG 需要一些前提條件。SWIG 的一些最新版本需要 PHP 的版本是 V5。此外,還需要有一個 C/C++ 編譯器,比如 GNU Compiler Collection (GCC),以及 PHP Module Development Kit (MDK)。特別是您還要有與 PHP 安裝相關的標頭檔。如果您使用的是 Ubuntu Linux 或一個 Debian 的變體並且已經從一個包存放庫安裝了 PHP V5,那麼一般而言您就可以使用 Advanced Packaging Tool (APT) 添加 MDK 了。例如,在 Ubuntu 核心 9.10 上,鍵入 apt-get install sudo apt-get install --install-recommends --yes php5-dev。
截止到 2009 年底,SWIG 的最新版本是 V1.3.40(參見 參考資料)。下載 tarball (一個由 gzip 壓縮了的 TAR 檔案),將它解壓縮,然後針對您的系統配置這些代碼,構建並安裝這個軟體。(要想找到所有的配置選項,運行 ./configure --help)。清單 1 提供了下載、解壓縮和安裝 SWIG 所需的命令。
清單 1. 下載、解壓縮和安裝 SWIG
$ wget http://prdownloads.sourceforge.net/swig/swig-1.3.40.tar.gz
$ tar xzf swig-1.3.40.tar.gz
$ cd swig-1.3.40
$ ./configure
$ make
$ sudo make install
$ which swig
/usr/local/bin/swig
構建一個擴充
讓我們構建一個擴充來用 Linux mcrypt 庫加密和解密訊息。PHP 提供了一個 mcrypt 庫,但它不過是對此庫的 C 版本稍作修飾後的結果。現在,讓我們構建兩個更為簡潔的方法:一個用來加密字串,另一個用來解密字串。
在 Ubuntu 或與其相似的系統上,您可以用 APT 安裝恰當的 mcrypt 庫和標頭檔:$ sudo apt-get install libmcrypt-dev libmcrypt4 mcrypt libmhash2。
若您寧願從頭開始構建,或者您的分布版內不包括 mcrypt,那麼可以從它的首頁上下載原始碼(參見 參考資料)。替代了 crypt 的 mcrypt 公用程式也依賴於 libmhash,因此必須在編譯 mcrypt 之前構建 libmhash。清單 2 給出了構建 libmhash 所需的代碼。
清單 2. 構建 libmhash
$ # libmhash
$ wget http://sourceforge.net/projects/mhash/files/mhash/0.9.9.9//
mhash-0.9.9.9.tar.bz2/download
$ tar xfj mhash-0.9.9.9.tar.bz2
$ cd mhash-0.9.9.9
$ ./configure
$ make
$ sudo make install
# libmcrypt
$ wget ftp://mcrypt.hellug.gr/pub/crypto/mcrypt/libmcrypt//
libmcrypt-2.5.7.tar.gz
$ tar xfz libmcrypt-2.5.7.tar.gz
$ cd libmcrypt-2.5.7
$ ./configure
$ make
$ sudo make install
$ # mcrypt
$ wget wget http://sourceforge.net/projects/mcrypt/files/MCrypt/2.6.8//
mcrypt-2.6.8.tar.gz/download
$ tar xfz mcrypt-2.6.8.tar.gz
$ cd mcrypt-2.6.8
$ ./configure
$ make
$ sudo make install
接下來,建立此擴充的 C 代碼。代碼中最有趣的函數是位於 清單 3 底部的 encode() 和 decode()。二者均具有兩個形參 — 一個字串和一個計數 — 並且均返回字串。前者加密一個純文字的字串,返回其編碼;後者解密一個加密了的字串並返回純文字。字串可以是任意長度。
上述代碼使用了 Data Encryption Standard-Electronic Codebook (DES-ECB) 演算法。秘密密匙可以是八個字元的任一字元串,並可顯示為 12345678(只用於示範目的)。如果您要與其他方交換加密了的訊息,需要獲得交換方的密鑰或建立一個新的密鑰並共用它。(密碼編譯演算法獨立於架構和語言,不過,寄件者和接收者均必須知道秘密密匙。)
清單 3. PHP 擴充的 C 代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mcrypt.h>
char *encode( char *string, int length );
char *decode( char *string, int length );
MCRYPT start() {
MCRYPT td = mcrypt_module_open( "des", NULL, "ecb", NULL );
if ( td == MCRYPT_FAILED ) {
return( MCRYPT_FAILED );
}
if ( mcrypt_enc_self_test( td ) != 0 ) {
return( MCRYPT_FAILED );
}
int i;
char *IV;
int iv_size = mcrypt_enc_get_iv_size( td );
if ( iv_size != 0 ) {
IV = calloc( 1, iv_size );
for ( i = 0; i < iv_size; i++ ) {
IV[ i ] = rand();
}
}
int keysize = mcrypt_enc_get_key_size( td );
char *key = calloc( 1, keysize );
memcpy(key, "12345678", keysize);
i = mcrypt_generic_init ( td, key, keysize, IV );
if ( i < 0 ) {
mcrypt_perror( i );
exit(1);
}
return( td );
}
void end( MCRYPT td ) {
mcrypt_generic_deinit( td );
mcrypt_module_close( td );
}
#define B64_DEF_LINE_SIZE 72
#define B64_MIN_LINE_SIZE 4
/*
** encode 3 8-bit binary bytes as 4 '6-bit' characters
*/
void encodeblock( unsigned char in[3], unsigned char out[4], int len ) {
static const char
cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
out[0] = cb64[ in[0] >> 2 ];
out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
out[2] = (unsigned char) (len > 1 ? cb64[
((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
}
char *base64encode( char *input, int size ) {
int i, x, len;
unsigned char in[3], out[4];
char *target = calloc( 1, ( ( size + 2 ) / 3 ) * 4 + 1 );
char *t = target;
for ( x = 0; x < size; ) {
len = 0;
for( i = 0; i < 3; i++ ) {
if ( x < size ) {
len++;
in[i] = input[x++];
}
else {
in[i] = 0;
}
}
if( len ) {
encodeblock( in, out, len );
for( i = 0; i < 4; i++ ) {
*t++ = out[i];
}
}
}
return( target );
}
char *encode( char *string, int length ) {
MCRYPT td = start();
int blocksize = mcrypt_enc_get_block_size( td );
int cryptsize = ( ( length + blocksize - 1 ) / blocksize ) * blocksize;
char *target = calloc( 1, cryptsize );
memcpy( target, string, length );
if ( mcrypt_generic( td, target, cryptsize ) != 0 ) {
fprintf( stderr, "Code failing" );
}
end( td );
char* result = base64encode( target, cryptsize );
free( target );
return result;
}
char *decode( char *string, int length ) {
MCRYPT td = start();
int blocksize = mcrypt_enc_get_block_size( td );
char *block_buffer = calloc( 1, blocksize );
int decryptlength = (length + blocksize - 1) / blocksize * blocksize;
char *target = calloc( 1, decryptlength );
memcpy(target, string, length);
mdecrypt_generic( td, target, decryptlength );
end( td );
free(block_buffer);
return( target );
}
將清單 3 內的代碼複製並粘貼到一個名為 secret.c 的新檔案。下一個任務是使用 SWIG 自身的文法描述此擴充的 API。
--------------------------------------------------------------------------------
回頁首
SWIG 檔案
在目前的開發階段,可以在 secret.c 基礎上手動構建一個擴充。但 SWIG 可以幫您完成這一艱苦工作 — 並且只需採用少量的虛擬碼。清單 4 所示的 secret.i 就是這個新擴充的 SWIG 模板。
清單 4. secret.i
%module secret
%{
extern char *encode( char *string, int length );
extern char *decode( char *string, int length );
%}
extern char *encode( char *string, int length );
extern char *decode( char *string, int length );
對 SWIG 文法和選項的全面解讀超出了本文的討論範圍。完整的文檔,可以在網上找到(參見 參考資料)。簡單來講,SWIG 檔案一般在第 1 行聲明擴充的名稱。檔案的其他部分聲明進入點。就是這些內容。編譯需要幾個步驟:第一步是產生代碼的封裝程式:$ swig -php secret.i。
SWIG 將 secret.i 轉變為 secret_wrap.c。接下來的幾個步驟是構建和連結這些封裝程式碼、這個擴充以及 mcrypt 庫。請務必用 -fpic 選項構建每個 C 源檔案,因該選項可以產生獨立於位置的代碼,這非常適合於共用庫。
$ cc -fpic -c secret.c
$ gcc `php-config --includes` -fpic -c secret_wrap.c
$ gcc -shared *.o -o secret.so -lmcrypt
$ sudo cp secret.so `php-config --extension-dir`
前兩個命令構建 C 原始碼。第三個命令構建 PHP 擴充。-lmcrypt 選項解析此擴充內進入點在 mcrypt 庫內的那些調用。第四個命令會將這個新的 PHP 擴充放入合適的目錄以便它可被 PHP 載入。
在開始編寫 PHP 代碼之前,最後一步是載入這個擴充。開啟適當的 php.ini 檔案 — 或者針對 Apache,或者針對 PHP 的命令列變體 — 並添加一行代碼:extension=secret.so。
如果不確定該編輯哪個 php.ini 檔案,可以查詢 PHP 本身。建立如下所示的這個共三行代碼的程式並使用瀏覽器或互動解譯器運行它:
<?php
phpinfo();
?>
尋找以 Loaded Configuration File 開頭的一行代碼。例如,在本文使用的測試平台上,此程式產生了輸出 Loaded Configuration File => /etc/php5/cli/php.ini。因此,要編輯的檔案是 /etc/php5/cli/php.ini。
--------------------------------------------------------------------------------
回頁首
編寫 PHP 代碼
有了這個出色的新擴充後,就可以開始編寫 PHP 了。清單 5 顯示了 code.php。
清單 5. code.php
<?php
include("secret.php");
$string = "Double secret probation";
$base64encode = secret::encode($string, strlen($string));
$base64decode = base64_decode($base64encode);
$decode = secret::decode( $base64decode, strlen($base64decode));
echo $decode . "/n";
?>
行 1 載入此擴充。行 4 編碼字串 Double secret probation 並使用 Base64 將加密了的這個字串轉變為可列印的字元,以便於用電子郵件等程式傳輸。行 5 對 Base 64 編碼進行解碼以產生原始字元,行 6 將加密訊息解密成原始文本。
假設將這些代碼儲存在 coder.php 內,並在系統的 /usr/local/lib 下安裝了 mcrypt 庫,用 PHP CLI 命令就可以運行這些範例程式碼了:
$ LD_LIBRARY_PATH=/usr/local/lib php ./code.php
Double secret probation
--------------------------------------------------------------------------------
回頁首
結束語
SWIG 是重用現有代碼的一種極好的方式。用 SWIG 封裝 C 或 C++ 庫,並將結果集成到您的下一個 Web 或系統應用程式。而更妙的是,SWIG 還可以從相同的 .i 檔案產生面向其他指令碼語言的封裝程式。您只需編寫一次擴充,之後,就可以與 PHP、Perl、Python、Ruby 和其他開發人員共用它了。
參考資料
學習
在 SWIG 網站瞭解有關 SWIG 擴充產生器的更多資訊。
查閱 SWIG 文檔,獲得可以使 SWIG 變得更易使用的文章和教程。此項目還維護著幾個 wikis。
PHP.net 是 PHP 開發人員資源中心。
查看 “推薦 PHP 讀物列表”。
瀏覽 developerWorks 上的所有 PHP 內容。
查看 IBM developerWorks 的 PHP 項目資源,擴充 PHP 技能。
要收聽面向軟體開發人員的有趣訪談和討論,請訪問 developerWorks podcasts。
在 PHP 中使用資料庫?試試 Zend Core for IBM,這是一個無縫、即開即用、易於安裝的 PHP 開發和生產環境,支援 IBM DB2 V9。
My developerWorks 社區是一個成功社區的典範,涵蓋了各種內容的主題。
隨時關注 developerWorks 技術活動和網路廣播。
查閱最近將在全球舉辦的面向 IBM 開放源碼開發人員的研討會、交易展覽、網路廣播和其他 活動。
訪問 developerWorks Open source 專區 獲得豐富的 how-to 資訊、工具和項目更新以及 最受歡迎的文章和教程,協助您用開放源碼技術進行開發,並將它們與 IBM 產品結合使用。
查看免費的 developerWorks 示範中心,觀看並瞭解 IBM 及開源技術和產品功能。
獲得產品和技術
從這個項目網站 下載 SWIG。
下載 mcrypt 庫的原始碼。
使用 IBM 產品評估試用版軟體 改進您的下一個開源開發項目,這些軟體可以通過下載獲得。
下載 IBM 產品評估試用版軟體 或 IBM SOA Sandbox for Reuse,並使用來自 DB2、Lotus、Rational、Tivoli 和 WebSphere 的應用程式開發工具和中介軟體產品。
討論
參與 developerWorks blogs 並加入 developerWorks 社區。
參與 developerWorks PHP 論壇:用 IBM 資訊管理產品(DB2、IDS)開發 PHP 應用程式。
來源:http://www.ibm.com/developerworks/cn/opensource/os-php-swig/index.html
出處:http://blog.csdn.net/heiyeshuwu/archive/2010/03/02/5338344.aspx