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 |
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.
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.
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
|
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.
Learn
-
Learn more about the SWIG
extension generator from the SWIG site.
-
Check out
the SWIG documentation and find articles and tutorials to make SWIG
even easier. The project also maintains several
wikis.
-
PHP.net is the central resource for PHP developers.
-
Check out the "Recommended PHP reading list."
-
Browse all the PHP content on developerWorks.
-
Follow developerWorks on Twitter.
-
Expand your PHP skills by checking out IBM developerWorks' PHP project resources.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Using a database with PHP? Check out the Zend Core for
IBM, a seamless, out-of-the-box, easy-to-install PHP development and production environment that supports IBM DB2 V9.
-
The My developerWorks community is an example of a successful general community that covers a wide variety of topics.
-
Stay current with developerWorks' Technical events and webcasts.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products, as well as our most popular articles and tutorials.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
Get products and technologies
-
Download SWIG
from its project site.
-
Download the
mcryptlibrary source code. -
Innovate your next open source development project with IBM trial software, available for download or on DVD.
- Download
IBM product evaluation versions
or explore
the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from
DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.
-
Participate in the developerWorks PHP Forum: Developing PHP applications with IBM Information Management products (DB2, IDS).

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.




