
Advanced PHP Programming
.pdf
498 Chapter 20 PHP and Zend Engine Internals
The following are some important elements of this struct:
n module_startup_func—This hook is called when the module is first loaded.This traditionally registers globals, performs any one-time initializations, and registers any .ini file entries that the module wants to use. In some pre-fork architectures, notably Apache, this function is called in the parent process, before forking.This makes it an inappropriate place to initialize open sockets or database connections because they may not behave well if multiple processes try to use the same resources.
n module_shutdown_func—This hook is called when the interpreter shuts down. Any resources that the module has allocated should be freed here.
n request_startup_func—This is called at the beginning of each request.This hook is particularly useful for setting up any sort of per-request resources that a script may need.
nrequest_shutdown_func—This is called at the end of every request.
nfunctions—This is the function that the extension defines.
nini_functions—This is the .ini file entries that the extension registers.
The Zend Extension API
The final component of the PHP request life cycle is the extension API that the Zend Engine itself provides for extensibility.There are two major components of the extensibility: Certain key internal functions are accessed via function pointers, meaning that they can be overridden at runtime, and there is a hook API that allows an extension to register code to be run before certain opcodes.
These are the main function pointers used in the Zend Engine:
n zend_compile—We discussed this function at the beginning of the chapter. zend_compile is the wrapper for the lexer, parser, and code generator. APC and the other compiler caches overload this pointer so that they can return cached copies of scripts’ op arrays.
n zend_execute—Also discussed earlier in this chapter, this is the function that executes the code generated by zend_compile. APD and the other code profilers overload zend_execute so that they can track with high granularity the time spent in every function call.
n zend_error_cb—This is a pointer that sets the function called anytime an error is triggered in PHP. If you wanted to write an extension that automatically converts errors to exceptions, this would be the place to do it.
n zend_fopen—This is the function that implements the open call that is used internally whenever a file needs to be opened.

The PHP Request Life Cycle |
499 |
The hook API is an extension of the PHP extension API:
struct _zend_extension { char *name;
char *version; char *author; char *URL;
char *copyright;
startup_func_t startup; shutdown_func_t shutdown; activate_func_t activate; deactivate_func_t deactivate; message_handler_func_t message_handler;
op_array_handler_func_t op_array_handler; statement_handler_func_t statement_handler; fcall_begin_handler_func_t fcall_begin_handler; fcall_end_handler_func_t fcall_end_handler; op_array_ctor_func_t op_array_ctor; op_array_dtor_func_t op_array_dtor;
int (*api_no_check)(int api_no); void *reserved2;
void *reserved3; void *reserved4; void *reserved5; void *reserved6; void *reserved7; void *reserved8; DL_HANDLE handle; int resource_number;
};
The pointers provide the following functionality:
n startup—This is functionally identical to an extension’s module_startup_func function.
n shutdown—This is functionally identical to an extension’s module_shutdown_func function.
n activate—This is functionally identical to an extension’s request_startup_func function.
ndeactivate—This is functionally identical to an extension’s request_shutdown_func function.

500 Chapter 20 PHP and Zend Engine Internals
nmessage_handler—This is called when the extension is registered.
nop_array_handler—This is called on a function’s op_array after the function is
compiled.
n statement_handler—If this handler is set, an additional opcode is inserted before every statement.This opcode’s handler executes all the registered statement handlers.This handler can be useful for debugging extensions, but because it effectively doubles the size of the script’s op array, it can have a deleterious effect on system performance.
nfcall_begin_handler—If this handler is set, an additional opcode is inserted before every ZEND_DO_FCALL and ZEND_DO_FCALL_BY_NAME opcode.That opcode’s
handler executes all registered fcall_begin_handler functions.
nfcall_end_handler—If this handler is set, an additional opcode is inserted after every ZEND_DO_FCALL and ZEND_DO_FCALL_BY_NAME opcode.That opcode’s han-
dler executes all registered fcall_end_handler functions.
How All the Pieces Fit Together
The preceding sections provide a lot of information. PHP, SAPIs, the Zend Engine— there are a lot of moving parts to consider.The most important part in understanding how a system works is understanding how all the pieces fit together. Each SAPI is unique in how it ties all the pieces together, but all the SAPIs follow the same basic pattern.
Figure 20.3 shows the complete life cycle of the mod_php5 SAPI. After the initial server startup, the process loops the handling requests.


502 Chapter 20 PHP and Zend Engine Internals
Further Reading
Documentation for the Zend Engine is pretty scarce. If you prefer a more hands-on introduction than is presented here, skip ahead to Chapter 23, where you will see a complete walkthrough of the CGI SAPI as well as extensive coverage of how to embed PHP into external applications.

21
Extending PHP: Part I
UNDER THE HOOD, PHP INTERNAL FUNCTIONS and classes are all implemented in C. Developers can write PHP functions and classes in C or C++ as well.There are two major reasons you might want to write your own PHP extensions:
nInterfacing with an external library—If you have an external library you would like to have access to in PHP, the only real solution is to write an extension wrapper for it.You might want to do this for a library that you have developed inhouse, a library whose license precludes a wrapper library for it being included in the PHP distribution, or a library that simply hasn’t had a PHP interface released. In the latter case, the library may be an ideal candidate for inclusion in PHP via the PECL extension library in PEAR.
nPerformance—There may be critical portions of your business logic that you have been unable to optimize using the other techniques presented in this book up to now.The ultimate step in performance tuning is to convert your business logic to C. Because C functions do not execute on the Zend virtual machine, they have significantly less overhead. Function speedups of 10 to 100 times are reasonable to expect for functions that are not bound by external resources (database calls, remote data fetching, RPCs, and so on).
Although both of these reasons are strong, a general word of warning should be given for anyone considering writing an application, especially for performance reasons: One of the strengths of PHP is its shallow learning curve. One of the major benefits of using a high-level language (like PHP or Perl and unlike C or C++) is that it shields you from having to perform your own memory management and from making errors that can cause the PHP interpreter itself to crash.
When you write a C extension, you lose both of these benefits.When application logic becomes (even partially) implemented in C, you need a C programmer to maintain the application.This can be impractical for many smaller organizations (and even some larger ones) if staffing efforts are focused on having PHP programmers, not C

504 Chapter 21 Extending PHP: Part I
programmers. Just because you are proficient in C does not mean that your replacement will be. Although it’s possible to think of this as some sort of twisted job security, painting either yourself or your employer (who now has to staff C programmers, as well as PHP programmers) into a corner is something you should not do without considerable forethought.
In addition, C is more difficult to program well than PHP. Because data created in extensions is not magically handled by the Zend garbage-collection system, you have to be careful not to leak memory or resources; the Zend API in particular approaches black magic when it comes to handling resource references in extensions.The C debugging process is much longer than the PHP debugging process:You cannot simply change a line of code and have it take effect; you must make the change, recompile, and restart the application for the change to take effect.You also expose yourself to application crashes (segmentation faults, and so on) if you perform actions you shouldn’t in C.
Like almost every potential performance optimization, retooling an application in C is a matter of trade-offs.With C, these are the benefits:
nSpeed
nReduced complexity of the PHP code
These are the drawbacks:
nReduced maintainability
nLengthened development cycle
nIncreased brittleness of the application
For some organizations, these trade-offs make sense. Also, if you are trying to interface with an external library, there is usually no choice but to provide access via a wrapper extension.
Extension Basics
If you know C, writing PHP extensions is not terribly difficult. PHP provides a number of tools that make bridging PHP and C code easy.This section provides all the steps necessary to build a PHP extension that registers procedural functions.
Creating an Extension Stub
The easiest way to create a new extension is to use a default extension skeleton.You do this with the ext_skel script in the ext directory of the PHP source tree.To create an extension named example, you would use the following from the root of the source tree:
>cd ext
>./ext_skel --extname=example Creating directory example

Extension Basics |
505 |
Creating basic files: config.m4 .cvsignore example.c
php_example.h CREDITS EXPERIMENTAL tests/001.phpt example.php
[done].
To use your new extension, you have to execute the following:
1.$ cd ..
2.$ vi ext/example/config.m4
3.$ ./buildconf
4.$ ./configure --[with|enable]-example
5.$ make
6.$ ./php -f ext/example/example.php
7.$ vi ext/example/example.c
8.$ make
Repeat steps 3-6 until you are satisfied with ext/example/config.m4 and step 6 confirms that your module is compiled into PHP.Then, start writing code and repeat the last two steps as often as necessary.
This code creates a directory named example with all the files necessary to build the extension.The first file of importance is example.c; it is the master C source file for the extension. It looks like the following (from which I’ve trimmed some nonessential parts for readability):
#ifdef HAVE_CONFIG_H #include “config.h” #endif
#include “php.h” #include “php_ini.h”
#include “ext/standard/info.h” #include “php_example.h”
#define VERSION “1.0”
function_entry example_functions[] = { {NULL, NULL, NULL}
};
zend_module_entry example_module_entry = { STANDARD_MODULE_HEADER,
“example”,
example_functions,
PHP_MINIT(example),
PHP_MSHUTDOWN(example),
PHP_RINIT(example),
PHP_RSHUTDOWN(example),
PHP_MINFO(example),

506 Chapter 21 Extending PHP: Part I
VERSION,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_EXAMPLE ZEND_GET_MODULE(example) #endif
PHP_MINIT_FUNCTION(example)
{
return SUCCESS;
PHP_MSHUTDOWN_FUNCTION(example)
{
return SUCCESS;
}
PHP_RINIT_FUNCTION(example)
{
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(example)
{
return SUCCESS;
}
PHP_MINFO_FUNCTION(example)
{
php_info_print_table_start();
php_info_print_table_header(2, “example support”, “enabled”); php_info_print_table_end();
}
Later sections of this chapter discuss the meanings of the parts of this code.
The next file to inspect is the config.m4 file.This is a set of m4 macros that specify the build-time flags for the extension.The following is a simple .m4 script that requires you to specify --enable-example to build the extension:
PHP_ARG_ENABLE(example, |
to enable the example extension, |
[ --enable-example |
enable the example extension.]) |
if test “$PHP_EXAMPLE” != “no”; then PHP_NEW_EXTENSION(example, example.c, $ext_shared)
fi
