尽管编写一个 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 所需的命令。
$ 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
所需的代码。
$ # 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
(只用于演示目的)。如果您要与其他方交换加密了的消息,需要获得交换方的密钥或创建一个新的密钥并共享它。(加密算法独立于架构和语言,不过,发送者和接收者均必须知道秘密密匙。)
#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。
在目前的开发阶段,可以在 secret.c 基础上手动构建一个扩展。但 SWIG 可以帮您完成这一艰苦工作 — 并且只需采用少量的伪代码。清单 4 所示的 secret.i 就是这个新扩展的 SWIG 模板。
%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 了。清单 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 对 Base64 编码进行解码以生成原始字符,行 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