Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Build PHP extensions with SWIG

Martin Streicher, Software Developer, Pixel, Byte, and Comma
author photo - martin streicher
Martin Streicher is a freelance Ruby on Rails developer and the former Editor-in-Chief of Linux Magazine. Martin holds a Master of Science degree in computer science from Purdue University, and has programmed UNIX-like systems since 1986. He collects art and toys.

Summary:  The core PHP libraries and the open source contributions to PHP provide innumerable functions. PHP extensions provide everything from byte-code caching to system calls. However, if you require specialized computation, you can create your own extension with the Simplified Wrapper and Interface Generator (SWIG).

Date:  12 Jan 2010
Level:  Intermediate
Also available in:   Japanese  Portuguese

Activity:  19445 views
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 Resources). 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 Resources). 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 Resources). 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.


Resources

Learn

Get products and technologies

Discuss

About the author

author photo - martin streicher

Martin Streicher is a freelance Ruby on Rails developer and the former Editor-in-Chief of Linux Magazine. Martin holds a Master of Science degree in computer science from Purdue University, and has programmed UNIX-like systems since 1986. He collects art and toys.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

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
author1-email=martin.streicher@gmail.com
author1-email-cc=mmccrary@us.ibm.com

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers