Programming PHPProgramming PHPSearch this book

14.3. Building Your First Extensions

This section walks you through the steps of building your first extension, from design through testing. Most extensions are created by writing a file that defines the functions the extension will have, building a skeleton from that, and then filling in the C code that does the actual work of the extension. This section doesn't cover advanced topics such as returning complex values or managing memory—we'll talk about those later, after you have the basics down.

14.3.4. Fleshing Out the Skeleton

The rot13.c file contains the C code that implements the extension. After including a standard collection of header files, the first important part of the extension is:

/* {{{ rot13_functions[]
 *
 * every user-visible function must have an entry in rot13_functions[]
 */
function_entry rot13_functions[] = {
    PHP_FE(confirm_rot13_compiled,  NULL)  /* for testing; remove later */
    PHP_FE(rot13,   NULL)
    {NULL, NULL, NULL}  /* must be the last line in rot13_functions[] */
};
/* }}} */

The {{{ and }}} sequences in the comments don't have meaning to the C compiler or PHP—they indicate a "fold" to editors that understand text folding. If your editor supports it (Vim6 and Emacs do), you can represent a block of text (e.g., a function definition) with a single line (e.g., a description of the function). This makes it easier to edit large files.

The important part in this code is the function_entry array, which lists the user-visible functions that this extension implements. Two such functions are shown here. The ext_skel tool generated the confirm_rot13_compiled( ) function for the purposes of testing. The rot13( ) function came from the definition in rot13.def.

PHP_FE( ) is a macro that stands for PHP Function Entry. The PHP API has many such convenience macros. While they speed up development for programmers experienced with the API, they add to the learning curve for beginners.

Next comes the zend_module_entry struct:

zend_module_entry rot13_module_entry = {
  STANDARD_MODULE_HEADER,
  "rot13",
  rot13_functions,
  PHP_MINIT(rot13),
  PHP_MSHUTDOWN(rot13),
  PHP_RINIT(rot13), /* replace with NULL if no request init code */
  PHP_RSHUTDOWN(rot13), /* replace with NULL if no request shutdown code */
  PHP_MINFO(rot13),
  "0.1", /* replace with version number for your extension */
  STANDARD_MODULE_PROPERTIES
};

This defines the functions to be called for the various stages of startup and shutdown. Like most extensions, rot13 doesn't need per-request startup and shutdown functions, so follow the instructions in the comments and replace PHP_RINIT(rot13) and PHP_RSHUTDOWN(rot13) with NULL. The resulting zend_module_entry struct looks like this:

zend_module_entry rot13_module_entry = {
  STANDARD_MODULE_HEADER,
  "rot13",
  rot13_functions,
  PHP_MINIT(rot13),
  PHP_MSHUTDOWN(rot13),
  NULL,
  NULL,
  PHP_MINFO(rot13),
  "0.1", /* replace with version number for your extension */
  STANDARD_MODULE_PROPERTIES
};

The extension API changed between PHP 4.0.x and PHP 4.1.x. To make your extension be source-compatible with PHP 4.0.x, you need to make some of the elements of the structure conditional, as follows:

zend_module_entry rot13_module_entry = {
#if ZEND_MODULE_API >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    "rot13",
    rot13_functions,
    PHP_MINIT(rot13),
    PHP_MSHUTDOWN(rot13),
    NULL,
    NULL,
    PHP_MINFO(rot13),
#if ZEND_MODULE_API >= 20010901
    "0.1",
#endif
    STANDARD_MODULE_PROPERTIES
};

Next in the rot13.c file is commented code showing how to deal with php.ini entries. The rot13 extension doesn't need to be configured via php.ini, so leave them commented out. Section 14.12 explains the use of these functions.

Next comes implementations of the MINIT( ), MSHUTDOWN( ), RINIT( ), RSHUTDOWN( ), and MINFO( ) functions. For our simple rot13 example, we simply need to return SUCCESS from the MINIT( ) and MSHUTDOWN( ) functions, and we can get rid of the RINIT( ) and RSHUTDOWN( ) functions entirely. So, after deleting some commented code, we just have:

PHP_MINIT_FUNCTION(rot13) {
    return SUCCESS;
}   
PHP_MSHUTDOWN_FUNCTION(rot13) {
    return SUCCESS;
}   
PHP_MINFO_FUNCTION(rot13) {
    php_info_print_table_start( );
    php_info_print_table_header(2, "rot13 support", "enabled");
    php_info_print_table_end( );
}

When you remove a function (such as RINIT( ) or RSHUTDOWN( )) from rot13.c, be sure to remove the corresponding prototype from php_rot13.h.

The MINFO( ) function is called by phpinfo( ) and adds whatever information you want about your extension to the phpinfo( ) output.

Finally, we get to the functions that are callable from PHP. The confirm_rot13_compiled( ) function exists only to confirm the successful compilation and loading of the rot13 extension. The skeleton tests use this. Most experienced extension writers remove the compilation-check function.

Here is the stub function that ext_skel created for our rot13( ) function:

/* {{{ proto string rot13(string arg)
   returns the rot13 version of arg */
PHP_FUNCTION(rot13)
{
    char *arg = NULL;
    int argc = ZEND_NUM_ARGS( );
    int arg_len;
    
    if (zend_parse_parameters(argc TSRMLS_CC, "s", &arg, &arg_len)
        == FAILURE)
        return;
        
    php_error(E_WARNING, "rot13: not yet implemented");
}   
/* }}} */

The {{{ proto line is not only used for folding in the editor, but is also parsed by the genfunclist and genfuncsummary scripts that are part of the PHP documentation project. If you are never going to distribute your extension and have no ambitions to have it bundled with PHP, you can remove these comments.

The PHP_FUNCTION( ) macro declares the function. The actual symbol for the function is zif_rot13, which is useful to know if you are debugging your code and wish to set a breakpoint.

The only thing the stubbed function does is accept a single string argument and then issue a warning saying it hasn't been implemented yet. Here is a complete rot13( ) function:

PHP_FUNCTION(rot13) {
    char *arg = NULL, *ch, cap;
    int arg_len, i, argc = ZEND_NUM_ARGS( );
  
    if (zend_parse_parameters(argc TSRMLS_CC, "s/", &arg, &arg_len)
        == FAILURE)
        return;
    for(i=0, ch=arg; i<arg_len; i++, ch++) {
        cap = *ch & 32; *ch &= ~cap;
        *ch = ((*ch >= 'A')&&(*ch <= 'Z') ? ((*ch-'A'+13) % 26+'A') : *ch)|cap;
    }
    RETURN_STRINGL(arg, arg_len, 1);
}

The zend_parse_parameters( ) function extracts the PHP values passed as parameters to the rot13( ) function. We'll talk about it in depth later. Don't worry too much about the string manipulation and bitwise logic here—that's merely the implementation of the rot13 behavior, not something that'll be in every extension you write. The RETURN_STRINGL( ) call at the end returns the string. You give it the string, the length of the string, and a flag that indicates whether a copy needs to be made. In this case, we need to have a copy made, so the last argument is a 1. Failing to return a copy may lead to memory leaks or crashes, as we'll see in Section 14.5 later.

14.3.5. Compiling Your Extension

Before you can build your extension, you must edit the config.m4 file and indicate how the user can specify that the module is to be compiled into PHP. These lines (commented out by default) do just that:

PHP_ARG_ENABLE(rot13, whether to enable rot13 support,
[  --enable-rot13           Enable rot13 support])

There are two main choices for building your extension. You can make a completely standalone source tree and build your extension as a shared module, or you can work within the framework of the PHP source tree. Shared modules are quicker to compile, but a line in the program source or php.ini file is required to load them. Compiling your extension into PHP takes time, but it means that the extension's functions are always visible to scripts.

14.3.5.2. Compiling the extension into PHP

To compile your extension into PHP, run the following from the top of your PHP4 source tree:

% ./buildconf

This will add your new --enable-rot13 switch to the top-level PHP ./configure script. You can run the following to verify that it worked:

% ./configure --help

Now build PHP with:

%./configure --enable-rot13 --enable-mysql=/usr .. 

See Chapter 1 for more information on building and installing PHP from the source code. After you issue a make install, your extension will be built statically into your PHP binary. This means you do not have to load the extension with dl( ) or a change to php.ini; the extension will always be available.

Use --enable-rot13=shared on your configure line to force the rot13 extension to be built as a shared library.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.