PHP ではコア・ライブラリーや、オープンソースによる貢献によって、数え切れないほどの関数が提供されています。また PHP の拡張機能にも、バイトコード・キャッシングからシステム・コールに至るまで、あらゆるものが用意されています。一方、それらが提供していない特殊な計算が必要な場合には、SWIG (Simplified Wrapper and Interface Generator) を使用することで、独自の拡張機能を作成することができます。

Martin Streicher, Software Developer, Pixel, Byte, and Comma

author photo - martin streicherMartin Streicher はフリーランスの Ruby on Rails 開発者であり、以前は Linux Magazine の編集責任者でした。彼は Purdue University でコンピューター・サイエンスの修士号を取得しており、1986年以来 UNIX ライクなシステムのプログラミングをしてきました。彼は美術品やおもちゃを収集しています。


developerWorks 貢献著者レベル

2010年 1月 12日

PHP の拡張機能の作成は特に面倒というわけではありませんが、SWIG を利用すると、そうした作業が非常に単純になります。その大きな理由は、PHP のコードと C や C++ のコードとを混在させる上で必要な作業を SWIG が自動的に行ってくれるためです。関数の記述 (その関数の名前と正式な引数) を指定すると、SWIG は PHP と下位レベルのコードを接続するラッパーを生成してくれます。

SWIG には、いくつかの前提条件があります。最新バージョンの SWIG には PHP V5 が必要です。また、GCC (GNU Compiler Collection) などの C/C++ コンパイラー、そして PHP MDK (Module Development Kit) のコピーも必要です。具体的には、PHP のインストールに関連付けられたヘッダー・ファイルが必要です。Ubuntu Linux® を使用する場合、または Debian のバリエーションを使用する場合で、パッケージ・リポジトリーから PHP V5 をインストールした場合には、通常は APT (Advanced Packaging Tool) を使って MDK を追加します。例えば Ubuntu のカーネル 9.10 の場合には、apt-get install sudo apt-get install --install-recommends --yes php5-dev と入力します。

2009年が終わる時点での SWIG の最新リリースは V1.3.40 です (「参考文献」を参照)。SWIG を利用するには 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 ライブラリーは、Linux の mcrypt ライブラリーの C によるエントリー・ポイントに対する非常に薄いレイヤーにすぎません。ここでは、それよりもはるかに簡潔な 2 つのメソッドとして、文字列を暗号化するためのメソッドと、文字列の暗号化を解除するためのメソッドを作成しましょう。

Ubuntu や Ubuntu と類似のシステムでは、該当する mcrypt ライブラリーとヘッダー・ファイルを APT によってインストールすることができます ($ sudo apt-get install libmcrypt-dev libmcrypt4 mcrypt libmhash2)。

ソースからビルドしたい場合や、ディストリビューションに mcrypt が含まれていない場合には、mcrypt のホームページからソース・コードをダウンロードすることができます (「参考文献」を参照)。mcrypt ユーティリティーは crypt を置き換えるものですが、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() です。どちらの関数も 2 つの正式な引数 (文字列とカウント値) を取って文字列を返します。encode() は平文の文字列を暗号化し、エンコードされた文字列を返します。decode() はエンコードされた文字列の暗号化を解除し、平文の文字列を返します。文字列の長さは任意です。

このコードは DES-ECB (Data Encryption Standard-Electronic Codebook) アルゴリズムを使用しています。秘密鍵は 8 文字の任意の文字列ですが、ここでは説明用に 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 という新しいファイルに貼り付けます。secret.c を用意できたら、次はこのファイルを基にした拡張機能の API を SWIG 独自の構文を使って記述します。


SWIG ファイル

この時点で、secret.c を基に拡張機能を手動で作成することもできますが、SWIG を使用すると、ほんのわずかな擬似コードを作成するだけで、あとの面倒な作業は SWIG がしてくれるのです。リスト 4 は、この新しい拡張機能のための SWIG テンプレートである secret.i を示しています。

リスト 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 ライブラリーをビルドしてリンクを実行します。各 C ソース・ファイルをビルドするときには必ず -fpic オプションを使うようにします。-fpic オプションを使うことで、共有ライブラリーに適した、場所に依存しないコードを生成することができます。

$ 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`

最初の 2 つのコマンドは C ソースをビルドします。3 番目のコマンドは PHP 拡張機能をビルドします。-lmcrypt オプションは拡張機能の中の呼び出しを mcrypt ライブラリーのエントリー・ポイントによって解決します。4 番目のコマンドは PHP が新しい PHP 拡張機能をロードできるように、新しい PHP 拡張機能を適切なディレクトリーに配置します。

PHP コードを作成する前の最後のステップとして、この拡張機能をロードします。適切な php.ini ファイル (Apache 版またはコマンドライン版の PHP の php.ini ファイル) を開き、extension=secret.so という 1 行を追加します。

どの php.ini ファイルを編集するのかわからない場合には、PHP そのものに対して問い合わせを行います。下記の 3 行のプログラムを作成し、このプログラムをブラウザーまたは対話型のインタープリターを使って実行します。

<?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 を使用して、例えば E メールで簡単に送信できる印刷可能な文字列に変換しています。5 行目は Base64 エンコーディングをデコードして元の文字列を生成し、6 行目で秘密のメッセージの暗号化を解除して元のテキストにしています。

このコードを coder.php に保存したとし、また mcrypt ライブラリーをシステムの /usr/local/lib にインストールしたとすると、このサンプル・コードを下記のように PHP の CLI コマンドを使って実行することができます。

$ LD_LIBRARY_PATH=/usr/local/lib php ./code.php 
Double secret probation

SWIG を活用する

SWIG は既存のコードを再利用するための優れた手段です。C や C++ のライブラリーを SWIG でラップすることによって、その結果を皆さんの次期 Web アプリケーションやシステム・アプリケーションに統合することができます。さらに良いことに、SWIG を使うと、他のスクリプト言語用のラッパーも、まったく同じ .i ファイルから生成することができます。一度拡張機能を作成すれば、その機能を PHP、Perl、Python、Ruby で共有することができ、また他の開発者とも共有することができます。

参考文献

学ぶために

  • SWIG 拡張機能生成プログラムについて、SWIG のサイトから学んでください。
  • SWIG のドキュメントを調べ、また SWIG に関する記事やチュートリアルを調べることで、より容易に SWIG を使えるようになります。このプロジェクトには、いくつかのウィキも用意されています。
  • PHP.net は PHP 開発者のための中心的なリソースです。
  • Recommended PHP reading list」を調べてみてください。
  • developerWorks には他にも PHP に関する資料が豊富に用意されています。
  • developerWorks を Twitter でフォローしてください。
  • IBM developerWorks の PHP project resources を利用して PHP のスキルを磨いてください。
  • developerWorks podcasts ではソフトウェア開発者のための興味深いインタビューや議論を聞くことができます。
  • PHP でデータベースを使うのであれば、Zend Core for IBM を調べてみてください。Zend Core for IBM は、IBM DB2 V9 をサポートする PHP の開発環境および本番環境として、シームレスでそのまま使用することができ、インストールも容易に行えます。
  • My developerWorks コミュニティーは広範な話題を網羅した全般的なコミュニティーとして成功している一例です。
  • developerWorks の Technical events and webcasts で最新情報を入手してください。
  • IBM オープンソース開発者にとって関心のある、世界中で今後開催される会議や業界展示会、ウェブキャスト、その他のイベントについて調べてみてください。
  • developerWorks の Open source ゾーンをご覧ください。オープンソース技術を使った開発や、IBM 製品でオープンソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。また最も人気のあった記事やチュートリアルもご覧ください。
  • IBM とオープンソース技術、そして製品機能を調べ、学ぶために、無料の developerWorks On demand demos をご覧ください。

製品や技術を入手するために

  • SWIG プロジェクトのサイトから SWIG をダウンロードしてください。
  • mcrypt ライブラリーのソース・コードをダウンロードしてください。
  • 皆さんの次期オープンソース開発プロジェクトを IBM ソフトウェアの試用版を使って革新してください。ダウンロード、あるいは DVD で入手することができます。
  • IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=467960
ArticleTitle=SWIG を使って PHP 拡張機能を作成する
publish-date=01122010