Contents


Build PHP extensions with SWIG

Comments

Although writing a PHP extension isn't particularly onerous, SWIG simplifies the task further, largely by automating the work required to intertwine PHP and C or C++ code. Given a description of your function — its name and its formal arguments — SWIG generates a wrapper to bridge from PHP to low-level code.

SWIG has a few prerequisites. The most recent versions require PHP V5. You must also have a C/C++ compiler, such as the GNU Compiler Collection (GCC), and a copy of the PHP Module Development Kit (MDK). Specifically, you must have the header files associated with your PHP installation. If you use Ubuntu Linux® or a variation of Debian and you installed PHP V5 from a package repository, you can typically add the MDK using the Advanced Packaging Tool (APT). For example, on Ubuntu kernel 9.10, type apt-get install sudo apt-get install --install-recommends --yes php5-dev.

As of the end of 2009, the most recent release of SWIG is V1.3.40 (see Related topics). Download the tarball (a gzip-compressed TAR file), unpack it, configure the code for your system, and build and install the software. (To find all the configuration options, run ./configure --help). Listing 1 provides the commands for downloading, unpacking, and installing SWIG.

Listing 1. Download, unpack, and install 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

Building an extension

Let's build an extension to encrypt and decrypt messages with the Linux mcrypt library. PHP provides an mcrypt library, but it's little more than a very thin veneer over the library's C entry points. Here, let's build two far more succinct methods: one to encrypt a string and another to decrypt a string.

On Ubuntu and its analogs, you can install the pertinent mcrypt libraries and header files with APT: $ sudo apt-get install libmcrypt-dev libmcrypt4 mcrypt libmhash2.

If you prefer to build from scratch, or if your distribution does not include mcrypt, you can download the source code from its home page (see Related topics). The mcrypt utility, which replaces crypt, also depends on libmhash, which you must build prior to compiling mcrypt. Listing 2 shows the code for building libmhash.

Listing 2. Building 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

Next, create the C code for the extension. The interesting functions in the code are encode() and decode(), at the bottom of Listing 3. Both have two formal arguments — a string and a count — and both functions return a string. The former encrypts a plain-text string, returning its encoding; the latter decrypts an encoded string and returns plain text. Strings can be of any length.

The code uses the Data Encryption Standard-Electronic Codebook (DES-ECB) algorithm. The secret key can be any string of eight characters and is shown as 12345678 for demonstration purposes only. If you are exchanging encrypted messages with another party, obtain the party's key or create a new key and share it. (The encryption algorithm is architecture and language-independent. However, both the sender and the receiver must know the secret key.

Listing 3. The C code for the PHP extension
#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 );
}

Copy and paste the code in Listing 3 to a new file named secret.c. Your next task is to describe the API of the extension using SWIG's own syntax.

The SWIG file

At this point in development, you could handcraft an extension built upon secret.c. SWIG, though, does the hard work for you — with a minuscule amount of pseudo-code. Listing 4 shows secret.i, a SWIG template for the new extension.

Listing 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 );

A complete almanac of SWIG syntax and options is beyond the scope of this article. You can find complete documentation online (see Related topics). Briefly, the SWIG file declares the name of the extension in line 1. The rest of the file declares the entry points. And that's it. Compilation requires a few steps: The first step generates the wrapper for your code: $ swig -php secret.i.

SWIG transforms secret.i to secret_wrap.c. The next few steps build and link the wrapper code, your extension, and the mcrypt library. Be sure to build each C source file with the -fpic option, which generates position-independent code suitable for a shared library.

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

The first two commands build the C source. The third command builds the PHP extension. The -lmcrypt option resolves the calls in the extension with entry points in the mcrypt library. The fourth command places the new PHP extension in the proper directory so it can be loaded by PHP.

The final step before writing PHP code is to load the extension. Open the appropriate php.ini file — either for Apache or for the command-line variant of PHP — and add one line: extension=secret.so.

If you are unsure which php.ini file to edit, you can query PHP itself. Create this three-line program and run it using the browser or the interactive interpreter:

<?php
  phpinfo();
?>

Look for a line that begins Loaded Configuration File. For example, on the test platform used to create this article, the program generated the output Loaded Configuration File => /etc/php5/cli/php.ini. Hence, the file to edit is /etc/php5/cli/php.ini.

Writing the PHP code

With a shiny new extension in hand, you can now write PHP. Listing 5 shows code.php.

Listing 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";
?>

Line 1 loads the extension. Line 4 encodes the string Double secret probation and uses Base64 to turn the encrypted string into printable characters that can easily be transmitted over e-mail, say. Line 5 unravels the Base64 encoding to yield raw characters, and line 6 decrypts the secret message to the original text.

Assuming that you save the code in coder.php and have installed the mcrypt library on your system in /usr/local/lib, you can run the example code with the PHP CLI command:

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

Take a big SWIG

SWIG is a great way to reuse existing code. Wrap up your C or C++ libraries with SWIG, and integrate the result into your next Web or system application. Better yet, SWIG can generate wrappers for other scripting languages, too, from the exact same .i file. Write your extension once, and share it with PHP, Perl, Python, Ruby, and other developers.


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=460607
ArticleTitle=Build PHP extensions with SWIG
publish-date=01122010