Anatomy of an Extension
In practice, there is typically a second or third configuration file and one or more header files as well. For your first extension, you'll be working with one of each of these types of files and adding from there.
Configuration File
To start out, create a directory under the ext/ dir in your PHP source tree called "sample". In reality this new directory could be placed anywhere, but in order to demonstrate Win32 and static build options later in this chapter, I'll be asking you to put it here this one time.
Next, enter this directory and create a file called config.m4 with the following contents:
PHP_ARG_ENABLE(sample, [Whether to enable the "sample" extension], [ enable-sample Enable "sample" extension support]) if test $PHP_SAMPLE != "no"; then PHP_SUBST(SAMPLE_SHARED_LIBADD) PHP_NEW_EXTENSION(sample, sample.c, $ext_shared) fi
This minimalist configuration sets up a ./configure option called enable-sample. The second parameter to PHP_ARG_ENABLE will be displayed during the ./configure process as it reaches this extension's configuration file. The third parameter will be displayed as an available option if the end-user issues ./configurehelp.
Note
Ever wonder why some extensions are configured using enable-extname and some are configured using with-extname? Functionally, there is no difference between the two. In practice, however, enable is meant for features that can be turned on without requiring any third-party libraries. with, by contrast, is meant for features that do have such prerequisites.
For now, your sample extension won't require linking against other libraries, so you'll be using the enable version. Chapter 17, "External Libraries," will introduce using with and instructing the compiler to use additional CFLAGS and LDFLAGS settings.
If an end user calls ./configure using the enable-sample option, then a local environment variable, $PHP_SAMPLE, will be set to yes. PHP_SUBST() is a PHP-modified version of the standard autoconf AC_SUBST() macro and is necessary to enable building the extension as a shared module.
Last but not least, PHP_NEW_EXTENSION() declares the module and enumerates all the source files that must be compiled as part of the extension. If multiple files were required, they would be listed in the second parameter using a space as a delimiter, for example:
PHP_NEW_EXTENSION(sample, sample.c sample2.c sample3.c, $ext_shared)
The final parameter is a counterpart to the PHP_SUBST(SAMPLE_SHARED_LIBADD) command and is likewise necessary for building as a shared module.
Header
When developing in C, it almost always makes sense to segregate certain types of data into external header files that are then included by the source files. Although PHP does not require this, it lends simplicity when a module grows beyond the scope of a single source file.
You'll start with the following contents in your new header file, called php_sample.h:
#ifndef PHP_SAMPLE_H /* Prevent double inclusion */ #define PHP_SAMPLE_H /* Define Extension Properties */ #define PHP_SAMPLE_EXTNAME "sample" #define PHP_SAMPLE_EXTVER "1.0" /* Import configure options when building outside of the PHP source tree */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Include PHP Standard Header */ #include "php.h" /* Define the entry point symbol * Zend will use when loading this module */ extern zend_module_entry sample_module_entry; #define phpext_sample_ptr &sample_module_entry #endif /* PHP_SAMPLE_H */
This header file accomplishes two primary tasks: If the extension is being built using the phpize toolwhich is how you'll be building it through most of this bookthen HAVE_CONFIG_H gets defined and config.h will be included as well. Regardless of how the extension is being compiled, it also includes php.h from the PHP source tree. This header file subsequently includes several other headers spread across the PHP sources providing access to the bulk of the PHPAPI.
Next, the zend_module_entry struct used by your extension is declared external so that it can be picked up by Zend using dlopen() and dlsym() when this module is loaded using an extension= line.
This header file also includes a few preprocessor defines that will be used in the source file shortly.
Source
Last, and by no means least, you'll create a simple source skeleton in the file sample.c:
#include "php_sample.h" zend_module_entry sample_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_SAMPLE_EXTNAME, NULL, /* Functions */ NULL, /* MINIT */ NULL, /* MSHUTDOWN */ NULL, /* RINIT */ NULL, /* RSHUTDOWN */ NULL, /* MINFO */ #if ZEND_MODULE_API_NO >= 20010901 PHP_SAMPLE_EXTVER, #endif STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_SAMPLE ZEND_GET_MODULE(sample) #endif
And that's it! These three files are everything needed to create a module skeleton. Granted, it doesn't do anything useful, but it's a place to start and you'll be adding functionality through the rest of this section. First though, let's go through what's happening.
The opening line is simple enough: Include the header file you just created, and by extension all the other PHP core header files from the source tree.
Next, create the zend_module_entry struct you declared in the header file. You'll notice that the first element of the module entry is conditional based on the current ZEND_MODULE_API_NO definition. This API number roughly equates to PHP 4.2.0, so if you know for certain that your extension will never be built on any version older than this, you could eschew the #ifdef lines entirely and just include the STANDARD_MODULE_HEADER element directly.
Consider, however, that it costs you very little in terms of compile time and nothing in terms of the resulting binary or the time it takes to process, so in most cases it will be best to just leave this condition in. The same applies to the version property near the end of this structure.
The other six elements of this structure you've initially set to NULL for now; you can see a hint from the comments next to these lines as to what they'll eventually be used for.
Finally, at the bottom you'll find a short element common to every PHP extension, which is able to be built as a shared module. This brief conditional simply adds a reference used by Zend when your extension is loaded dynamically. Don't worry about what it does or how it does it too much; just make sure that it's around or the next section won't work.