Preface:

One thing the world does not lack is PHP extensions. In fact, due to PHP's popularity the number of available extensions is quite dizzying.

To date, Zend Server includes 77 PHP extensions that Zend considers essential or just very helpful for most PHP developers. Still, what if the one that your code uses is not among them? Or you wish to compile one of the extensions Zend Server is shipped with in a different way (against your own libraries or to support additional, less common features)? If this scenario sounds familiar to you, you may find this tutorial useful.

Familiarize yourself with the tools:

PHP tools:

The PHP project provides several tools you should be familiar with if you aim to compile your own extensions. These are:

  • PECL (PHP Extension Community Library): PECL is a repository for PHP extensions, providing a directory of all known extensions and hosting facilities for download and development of PHP extensions. - It is also a tool supplied in the form of a small shell script with PHP code behind it to retrieve extensions from the aforementioned repository.
  • phpize: a shell script to generate a configure script for PHP extensions

Build tools:

While PHP can be built using many different tool chains, this article will focus on using the GNU tool chain. The main tools where PHP is concerned are:

  • autoconf: automatic configure script builder. This is called by the phpize script.
  • automake: a tool for generating GNU Standards-compliant Makefiles
  • libtool: Generic library support script. Libtool hides the complexity of generating special library types (such as shared libraries) behind a consistent (sort of :) ) interface.
  • GNU make: a GNU tool for controlling the generation of executables and other non-source files of a program from the program's source files
  • GCC: PHP extensions are typically written in C. Hence, in order for them to compile, you would need a C compiler. While GCC now stands for GNU compiler Collection and is no longer just a GNU C Compiler, for our purposes we only need the C part of the collection.
  • GNU's elf-binutils package: The programs in this package are used to assemble, link and manipulate binary and object files.

Getting down to business:

For this article, I've chosen two different scenarios. In one, we will compile a PECL extension called Newt, in the other a PHP extension included in the main PHP source called PSpell. Before we can compile anything, several packages need to be installed. Users of distributions with package managers (mainly Debian, Ubuntu, RHEL, CentOS and Fedora Core, though this can apply to many others as well) should install the following packages from their distribution's repository: gcc, make, autoconf, automake and libtool. Some of these tools depend on each other, for instance the libtool package depends on the gcc package, but no damage can be done from specifying all of them.

Note: Users who utilize distributions that do not have package managers (Linux from scratch anyone?), can compile these tools themselves or obtain pre-compiled binaries for them quite easily.

Additionally, if you want to compile a PHP extension from the main PHP source (as opposed to PECL) you should install a package from the Zend Server repositories called php-5.2-source-zend-server or php-5.3-source-zend-server, depending on your Zend Server's major PHP version. This package includes full PHP sources as patched, for security or optimization concerns, by the Zend development team. This ensures that you are using the exact same source code we used when building Zend Server.

Scenario 1 - compiling the Newt PECL extension

Newt is a PHP extension for RedHat's Newt (New Terminal) library, a terminal-based window and widget library for writing applications with user friendly interfaces.

Being what it is, this extension requires the existence of the Newt library development files. If you are using Debian or Ubuntu you should install a package called libnewt-dev. On RedHat based distributions the package name is newt-devel. Make sure these are installed before continuing.

NOTE: Other extensions will have other dependencies. For example, the Mcrypt extension will require the Mcrypt development package.

NOTE: Since PECL will attempt to write the extension onto /usr/local/zend/lib/php_extensions, you will have to become a super user to perform this procedure. This is only needed for the actual make install.

Assuming you have the Newt development package installed, just run:

# /usr/local/zend/bin/pecl install newt

Here is the truncated output of this command, along with explanations:

/*PECL is retrieving the package from the repository...*/ downloading newt-1.2.1.tgz ...
Starting to download newt-1.2.1.tgz (24,853 bytes)
.........done: 24,853 bytes
5 source files, building

/*The phpize script is executed...*/
running: phpize
Configuring for:
PHP Api Version: 20041225
Zend Module Api No: 20060613
Zend Extension Api No: 220060519
building in /var/tmp/pear-build-root/newt-1.2.1

/*Configure comes into play..*/
running: /tmp/pear/download/newt-1.2.1/configure
checking for grep that handles long lines and -e... /bin/grep checking for egrep... /bin/grep -E checking for a sed that does not truncate output... /bin/sed checking for gcc... gcc checking for C compiler default output file name... a.out checking whether the C compiler works... yes checking whether we are cross compiling... no checking for suffix of executables...
checking for suffix of object files... o

/*Next comes libtool..*/
creating libtool
appending configuration tag "CXX" to libtool
configure: creating ./config.status
config.status: creating config.h

/*The actual compilation process: calling make which will internally trigger GCC and LD...*/
running: make
/bin/sh /var/tmp/pear-build-root/newt-1.2.1/libtool --mode=compile gcc -I. -I/tmp/pear/download/newt-1.2.1 -DPHP_ATOM_INC -I/var/tmp/pear-build-root/newt-1.2.1/include
-I/var/tmp/pear-build-root/newt-1.2.1/main
-I/tmp/pear/download/newt-1.2.1 -I/usr/local/zend/include/php -I/usr/local/zend/include/php/main -I/usr/local/zend/include/php/TSRM
-I/usr/local/zend/include/php/Zend -I/usr/local/zend/include/php/ext -I/usr/local/zend/include/php/ext/date/lib
-I/usr/local/zend/include/php -DHAVE_CONFIG_H -g -O2 -c
/tmp/pear/download/newt-1.2.1/newt.c -o newt.lo mkdir .libs gcc -I. -I/tmp/pear/download/newt-1.2.1 -DPHP_ATOM_INC -I/var/tmp/pear-build-root/newt-1.2.1/include
-I/var/tmp/pear-build-root/newt-1.2.1/main
-I/tmp/pear/download/newt-1.2.1 -I/usr/local/zend/include/php -I/usr/local/zend/include/php/main -I/usr/local/zend/include/php/TSRM
-I/usr/local/zend/include/php/Zend -I/usr/local/zend/include/php/ext -I/usr/local/zend/include/php/ext/date/lib -I/usr/local/zend/include/php -DHAVE_CONFIG_H -g -O2 -c /tmp/pear/download/newt-1.2.1/newt.c -fPIC -DPIC -o .libs/newt.o /bin/sh /var/tmp/pear-build-root/newt-1.2.1/libtool --mode=compile gcc -I. -I/tmp/pear/download/newt-1.2.1 -DPHP_ATOM_INC -I/var/tmp/pear-build-root/newt-1.2.1/include
-I/var/tmp/pear-build-root/newt-1.2.1/main
-I/tmp/pear/download/newt-1.2.1 -I/usr/local/zend/include/php -I/usr/local/zend/include/php/main -I/usr/local/zend/include/php/TSRM
-I/usr/local/zend/include/php/Zend -I/usr/local/zend/include/php/ext -I/usr/local/zend/include/php/ext/date/lib
-I/usr/local/zend/include/php -DHAVE_CONFIG_H -g -O2 -c
/tmp/pear/download/newt-1.2.1/newt_vcall.c -o newt_vcall.lo gcc -I. -I/tmp/pear/download/newt-1.2.1 -DPHP_ATOM_INC -I/var/tmp/pear-build-root/newt-1.2.1/include
-I/var/tmp/pear-build-root/newt-1.2.1/main
-I/tmp/pear/download/newt-1.2.1 -I/usr/local/zend/include/php -I/usr/local/zend/include/php/main -I/usr/local/zend/include/php/TSRM
-I/usr/local/zend/include/php/Zend -I/usr/local/zend/include/php/ext -I/usr/local/zend/include/php/ext/date/lib -I/usr/local/zend/include/php -DHAVE_CONFIG_H -g -O2 -c /tmp/pear/download/newt-1.2.1/newt_vcall.c
-fPIC -DPIC -o .libs/newt_vcall.o
/bin/sh /var/tmp/pear-build-root/newt-1.2.1/libtool --mode=link gcc -DPHP_ATOM_INC -I/var/tmp/pear-build-root/newt-1.2.1/include
-I/var/tmp/pear-build-root/newt-1.2.1/main
-I/tmp/pear/download/newt-1.2.1 -I/usr/local/zend/include/php -I/usr/local/zend/include/php/main -I/usr/local/zend/include/php/TSRM
-I/usr/local/zend/include/php/Zend -I/usr/local/zend/include/php/ext -I/usr/local/zend/include/php/ext/date/lib
-I/usr/local/zend/include/php -DHAVE_CONFIG_H -g -O2 -o newt.la
-export-dynamic -avoid-version -prefer-pic -module -rpath /var/tmp/pear-build-root/newt-1.2.1/modules newt.lo newt_vcall.lo -lnewt gcc -shared .libs/newt.o .libs/newt_vcall.o -lnewt -Wl,-soname -Wl,newt.so -o .libs/newt.so creating newt.la (cd .libs && rm -f newt.la && ln -s ../newt.la newt.la) /bin/sh /var/tmp/pear-build-root/newt-1.2.1/libtool --mode=install cp ./newt.la /var/tmp/pear-build-root/newt-1.2.1/modules
cp ./.libs/newt.so /var/tmp/pear-build-root/newt-1.2.1/modules/newt.so
cp ./.libs/newt.lai /var/tmp/pear-build-root/newt-1.2.1/modules/newt.la
PATH="$PATH:/sbin" ldconfig -n /var/tmp/pear-build-root/newt-1.2.1/modules
----------------------------------------------------------------------
Libraries have been installed in:
/var/tmp/pear-build-root/newt-1.2.1/modules


Build complete.
Don't forget to run 'make test'.

/*And.. we're done, all that's left is PECL to put the newly built Newt extension into place...*/
running: make INSTALL_ROOT="/var/tmp/pear-build-root/install-newt-1.2.1"
install
Installing shared extensions:
/var/tmp/pear-build-root/install-newt-1.2.1//usr/local/zend/lib/php_extensions/
running: find "/var/tmp/pear-build-root/install-newt-1.2.1" | xargs ls -dils
574096 4 drwxr-xr-x 3 root root 4096 Mar 30 20:45
/var/tmp/pear-build-root/install-newt-1.2.1
574119 4 drwxr-xr-x 3 root root 4096 Mar 30 20:45
/var/tmp/pear-build-root/install-newt-1.2.1/usr
574120 4 drwxr-xr-x 3 root root 4096 Mar 30 20:45
/var/tmp/pear-build-root/install-newt-1.2.1/usr/local
574121 4 drwxr-xr-x 3 root root 4096 Mar 30 20:45
/var/tmp/pear-build-root/install-newt-1.2.1/usr/local/zend
574122 4 drwxr-xr-x 3 root root 4096 Mar 30 20:45
/var/tmp/pear-build-root/install-newt-1.2.1/usr/local/zend/lib
574123 4 drwxr-xr-x 2 root root 4096 Mar 30 20:45
/var/tmp/pear-build-root/install-newt-1.2.1/usr/local/zend/lib/php_extensions
574118 244 -rwxr-xr-x 1 root root 241717 Mar 30 20:45 /var/tmp/pear-build-root/install-newt-1.2.1/usr/local/zend/lib/php_extensions/newt.so

Build process completed successfully
Installing '/usr/local/zend/lib/php_extensions/newt.so'
install ok: channel://pear.php.net/newt-1.2.1

That's it! Now all that's left is to load the extension by inserting the "extension=newt.so" directive either in php.ini or in a separate file under the scan dir. If you're using the DEB and RPM flavors of Zend Server, the best practice is to place a file called newt.ini under /usr/local/zend/etc/conf.d.

Scenario 2 - compiling the Pspell extension

As mentioned before, you will need to install php-source-zend-[ce|pe] package for this procedure. Also, since this extension relies on the portable spell-checking interface (pspell) library, you will need to install its devel package. Debian and Ubuntu users should install the libpspell-dev package, on RedHat based distributions, the package name is aspell-devel.

CD onto the extension's source dir (in our example, the PHP version is 5.2.9 as it is the current stable version Zend Server is shipped with):

$ cd /usr/local/zend/share/php-source/php-5.2.9/ext/pspell

Run phpize:

$ /usr/local/zend/bin/phpize

Output should be similar to this:

/Configuring for:
PHP Api Version: 20041225
Zend Module Api No: 20060613
Zend Extension Api No: 220060519/

Run the configure script, generated by phpize:

$ ./configure --with-php-config=/usr/local/zend/bin/php-config

Run make:

$ make

Finally, become a super user [root] and run:

# make install

The output should be:

/Installing shared extensions:     /usr/local/zend/lib/php_extensions/

Then, just like in the first example, insert the "extension=pspell.so" directive either in php.ini or in a separate file under the scan dir.

Troubleshooting:

As is shown in the example, the configure script outputs messages as it goes along and many times you will be able to understand the problem just by looking at it, however, sometimes, the error doesn't necessarily reflect the real issue so it is always a good idea to review the config.log. This is a very generic statement but no other statement can be made as there are many different extensions and issues one may come across so attempting to list them all will be somewhat futile.