This article describes the steps to create a custom PHP extension DLL for the Windows platform. The Zend API documentation that comes with PHP 5 on Windows (see php_manual_en.chm) does a good job explaining how to write extension methods, parse method parameters, and return values. But there is not currently a good step-by-step tutorial on how to get your first extension project up and running on Windows. The aim of this article is to fill that gap.

Prerequisites

  • Visual Studio 2005
    You can alternately use the free Visual C++ Express Edition or the VC++ 8 compiler in the Windows SDK v6.0 if you’re a makefile master. Visual Studio 2003 (VC++ 7) will probably work just fine, but some of the project configuration steps will be different than what is explained here
  • A web server
    For this article I used Sambar Server 7.0. Any HTTP server that can run the PHP 5.x ISAPI extension (php5isapi.dll) will do
  • PHP 5 binaries, installed and configured for your server
    I used PHP 5.2.4. Using the windows installer package (.msi file) makes configuration easier
  • PHP 5 source code
    DONT PANIC! You do not need to build PHP from source, just get the source that matches your binary version. You will need an extraction utility like WinRAR or ZipGenius that can handle .tar archives.

Conventions

Italics denote file paths and names. C:\Server\PHP\dev
Screenshot icons provide links to images showing how dialogs are configured.
Document icons provide links to sample code files.

Configuring the Environment

I will not discuss installing and running the HTTP web server. If you do not already have IIS available on your machine, I recommend the Sambar Server.

If you do not already have PHP 5 installed, download it from php.net. If you download and run the MSI installer package, it will configure your web server.

After you’ve got PHP 5 installed and configured for your server, download and extract the complete PHP 5 source code, also from php.net. Caution: Do NOT extract the source archive over top of your existing binary installation. This article’s example setup includes the PHP 5 binaries installed to C:\Server\PHP5, and the source code extracted to C:\Server\PHP5Src.

Creating the Project

In Visual Studio 2005, create a new Visual C++ Win32 project using the project template.Creating the Project For this example, I named the project CustomExt. When the Win32 Application Wizard appears, click Application Settings (on the left) and select DLL as the application type. Change to DLL project Click Finish to create the project.

I recommend following along step-by-step, but you can also download the complete project source (zipped). You’ll still need to follow the instructions for changing the directories to match your system.

Change Default C++ Options

Bring up the Project Properties dialog, and make sure the Debug configuration is active. Under Configuration Properties > General, change the Character Set to “Use Multi-Byte Character Set”.

Then under Configuration Properties > C/C++ > Code Generation, change the following options:

  • Enable String Pooling to “Yes (/GF)”
  • Enable Minimal Rebuild to “No”
  • Basic Runtime Checks to “Default”
  • Runtime Library to “Multi-threaded Debug (/MTd)”

Under Configuration Properties > C/C++ > General, change the following:

  • Debug Information Format to “Program Database (/Zi)”
  • Detect 64-bit Portability Issues to “No”

Be sure to click Apply to save the settings.

Set the INCLUDE Paths

Still on the Project Properties dialog unders Configuration Properties > C/C++ > General, in the Additional Include Directories, add the following paths, replacing C:\Server\PHP5Src with the location on your machine where you extracted the source code.

C:/Server/PHP5Src/main 
C:/Server/PHP5Src/Zend 
C:/Server/PHP5Src/TSRM 
C:/Server/PHP5Src/regex 
C:/Server/PHP5Src

Again, be sure to click Apply to save the settings as you go along.

Set the Preprocessor Definitions

On the Project Properties dialog, under Configuration Properties > C/C++ > Preprocessor, add the following definitions:

ZEND_DEBUG=0    

ZTS=1    

ZEND_WIN32    

PHP_WIN32

Note: Do not be tempted to change ZEND_DEBUG to 1, even for this Debug build, as this will prevent your extension from being loaded into the pre-built PHP binaries installed on your system.

Set the Linker Options

On the Project Properties dialog, under Configuration Properties > Linker > General add the path to the C:\Server\PHP\dev directory to the Additional Library Directories. This is the directory underneath your installed PHP binaries (not the extracted source). This directory should contain php5ts.lib.

Under Configuration Properties > Linker > Input, add php5ts.lib to the Additional Dependecies.

By convention, php extensions start with php_, so under Configuration Properties > Linker > General, for the Output File option, I changed the output name to php_custom_ext.dll. This is entirely optional.

The Extension Code

Replace the contents of stdafx.h with the following, or download it :

#pragma once    
/* PHP Extension headers */    
/* include zend win32 config first */    
#include "zend_config.w32.h"    
/* include standard header */    
#include "php.h"

Replace the contents of ProjectName.cpp (in this example CustomExt.cpp) with the following (or download it ):

#include "stdafx.h"/* declaration of functions to be exported */    
ZEND_FUNCTION(DoubleUp);    
/* compiled function list so Zend knows what's in this module */    
zend_function_entry CustomExtModule_functions[] = {    
   ZEND_FE(DoubleUp, NULL)    
    {NULL, NULL, NULL}    
};    

/* compiled module information */    
zend_module_entry CustomExtModule_module_entry = {    
    STANDARD_MODULE_HEADER,    
    "CustomExt Module",    
    CustomExtModule_functions,    
    NULL, NULL, NULL, NULL, NULL,    
    NO_VERSION_YET, STANDARD_MODULE_PROPERTIES    
};    

/* implement standard "stub" routine to introduce ourselves to Zend */    
ZEND_GET_MODULE(CustomExtModule)    
/* DoubleUp function */    
/* This method takes 1 parameter, a long value, returns    
   the value multiplied by 2 */    
ZEND_FUNCTION(DoubleUp){    
    long theValue = 0;    
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,    
                              "l", &theValue) == FAILURE){    
        RETURN_STRING("Bad parameters!", true);    
    }    
    theValue *= 2;    
    RETURN_LONG(theValue);    
}

You should now be able to build the project without any errors. Be careful when renaming CustomExtModule or DoubleUp, and you must replace every instance in the file, or the Zend macros will yield nearly indecipherable compiler errors.

Enable the extension

The first step is to locate the \ext directory beneath your PHP 5 binaries (not the directory where you extracted the PHP source code). On my sample system this is C:\Server\PHP\ext. Copy the DLL that you just built to this directory. If you are going to attach a debugger to your extension, you will also want to copy the .pdb files.

The second step is to configure PHP to load your extension DLL. You do this by modifying PHP.ini, adding a extension=php_ext_name.dll line to the Dynamic Extensions section, like the following.

;;;;;;;;;;;;;;;;;;;;;;    
; Dynamic Extensions ;    
;;;;;;;;;;;;;;;;;;;;;;    
[CustomExt]    
extension=php_custom_ext.dll

Verify that the extension can be loaded

Once you’ve edited and saved php.ini, you may need to restart the HTTP server in order to pick up the new module. After the server is restarted, browse to the phpinfo.php web page. If you do not already have a phpinfo.php page, create one using the following text, and save it to one of your virtual directories:

<?php    
phpinfo();    
?>

Now browse to http://localhost/virtualpath/phpinfo.php, and look for the Additional Modules section. Your new extension should now be seen in the listing. If your extension is not listed, you may need to resave php.ini and restart the server. Also, make sure that the php.ini file that your server is using is the same ini file that you just edited. On most servers, php.ini is located in the PHP 5 installation directory, but your server may be different. To make certain, look at the information at the top of the page for the Loaded Configuration File entry, which shows the full path to the ini file PHP is currently using.

Write a test page

Copy the following code to testext.php (or download it ) , save it under one of your web server’s virtual directories.

<?php    
$value = 14;    
$result = DoubleUp($value);    
print "Calling DoubleUp($value) returned $result";    
?>

You can now browse to http://localhost/virtualpath/testext.php.

Debugging the extension

In order to debug the extension you need to attach the debugger to the running instance of the web server process. For IIS, this is w3wp.exe. For the Sambar server, this is sambar70\bin\server.exe. On the Project Properties dialog, under Configuration Properties > Debugging , set the following values:

  • Command to the full path to the server executable
  • Attach to “Yes”
  • Debugger Type to “Native Only”

Stop you web server and make certain that you have copied the latest version of the extension DLL and the .pdb files to the \ext directory, then restart the web server. Set a breakpoint in the DoubleUp method and execute Debug > Start Debugging. You may get a message that the web server executable was not built with debugging information. Ignore this warning and click Yes to continue debugging.

Open a web browser and browse to your test page that calls the DoubleUp method. Your breakpoint should be hit. Step through the code, and be sure to use Debug > Continue (F5) so that the web server doesn’t hang.

To stop debugging, go to Debug > Detach All or stop the web server.

Where to from here?

The Zend API section of the PHP documentation provides a wealth of detailed information on the macros and functions available for extension development. And now that you’ve got your own extension up and running, you can finally make use of it! Some areas to explore are enabling your extension to accept configuration parameters from php.ini, and creating methods that accept multiple parameters, and/or optional parameters.