In addition to these four methods, which are linked directly into the module entry structure, there are two more methods used only in threaded environments that handle the startup and shutdown of individual threads and the private storage space they use. To get started, set up a slightly more comprehensive version of the basic extension skeleton using these source files in ext/sample4 under your PHP source tree (see Listings 12.1 through 12.3):
Listing 12.1. config.m4
PHP_ARG_ENABLE(sample4, [Whether to enable the "sample4" extension], [ enable-sample4 Enable "sample4" extension support]) if test $PHP_SAMPLE4 != "no"; then PHP_SUBST(SAMPLE4_SHARED_LIBADD) PHP_NEW_EXTENSION(sample4, sample4.c, $ext_shared) fi |
Listing 12.2. php_sample4.h
#ifndef PHP_SAMPLE4_H /* Prevent double inclusion */ #define PHP_SAMPLE4_H /* Define Extension Properties */ #define PHP_SAMPLE4_EXTNAME "sample4" #define PHP_SAMPLE4_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 sample4_module_entry; #define phpext_sample4_ptr &sample4_module_entry #endif /* PHP_SAMPLE4_H */ |
Listing 12.3. sample4.c
#include "php_sample4.h" #include "ext/standard/info.h" static function_entry php_sample4_functions[] = { { NULL, NULL, NULL } }; PHP_MINIT_FUNCTION(sample4) { return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(sample4) { return SUCCESS; } PHP_RINIT_FUNCTION(sample4) { return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(sample4) { return SUCCESS; } PHP_MINFO_FUNCTION(sample4) { } zend_module_entry sample4_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_SAMPLE4_EXTNAME, php_sample4_functions, PHP_MINIT(sample4), PHP_MSHUTDOWN(sample4), PHP_RINIT(sample4), PHP_RSHUTDOWN(sample4), PHP_MINFO(sample4), #if ZEND_MODULE_API_NO >= 20010901 PHP_SAMPLE4_EXTVER, #endif STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_SAMPLE4 ZEND_GET_MODULE(sample4) #endif |
Notice that each startup and shutdown method returns SUCCESS on exit. If any method were to return FAILURE, the module load or request would be aborted by PHP to avoid any serious problems elsewhere in the engine.
Module Cycle
MINIT should be familiar as you've used it several times throughout the previous chapters. It's triggered the first time a module is loaded into a process space, which for single-request SAPIs such as CLI And CGI, or multithreaded SAPIs such as Apache2-worker, is exactly once because no forking is involved.
For multiprocess SAPIs such as Apache1 and Apache2-prefork, multiple web server processes are forked and with them multiple instances of mod_php. Each instance of mod_php must then load its own instance of your extension module meaning that your MINIT method is run multiple times, but still only once per process space.
When a module is unloaded, the MSHUTDOWN method is invoked so that any resources owned by that module, such as persistent memory blocks, may be freed and returned to the operating system.
Enginewide features, such as Class Entries, Resource IDs, Stream wrappers and filters, userspace autoglobals, and php.ini entries are some common examples of resources that get allocated and cleaned up in the Module INIT and SHUTDOWN phases respectively.
Note
In theory, you could skip proper resource cleanup during the MSHUTDOWN phase, opting instead to allow the OS to implicitly free memory and file handles. When using your extension with Apache 1.3 however, you'll discover an interesting quirk as Apache will load mod_php, launching all MINIT methods in the process, and then immediately unload mod_php, TRigging the MSHUTDOWN methods, and then load it again. Without a proper MSHUTDOWN phase, resources allocated during the initial MINIT will be leaked and wasted.
Thread Cycle
In multithreaded SAPIs, it's sometimes necessary for each thread to allocate its own independent resources or track its own personal per-request counters. For these special situations there is a per-thread hook that allows for an additional set of startup and shutdown methods to be executed. Typically when a multithreaded SAPI such as Apache2-worker starts up, it will spin a dozen or more threads in order to be able to handle multiple concurrent requests.
Any resources that may be shared between requests, but must not be accessed by multiple threads in the same process space simultaneously, are usually allocated and freed in the thread constructor and destructor methods. Examples might include persistent resources in the EG(persistent_list) HashTable because they often include network or file resources that make assumptions about the consistency of their state from instruction to instruction.
Request Cycle
The last and most transient startup and shutdown cycle occurs with every request, and is where your extension might choose to initialize default userspace variables or initialize internal state tracking information. Because both of these methods are called on every single page request, it's important to keep the processing and memory allocation load to a bare minimum.
The PHP Life Cycle
Variables from the Inside Out
Memory Management
Setting Up a Build Environment
Your First Extension
Returning Values
Accepting Parameters
Working with Arrays and HashTables
The Resource Data Type
PHP4 Objects
PHP5 Objects
Startup, Shutdown, and a Few Points in Between
INI Settings
Accessing Streams
Implementing Streams
Diverting the Stream
Configuration and Linking
Extension Generators
Setting Up a Host Environment
Advanced Embedding
Appendix A. A Zend API Reference
Appendix B. PHPAPI
Appendix C. Extending and Embedding Cookbook
Appendix D. Additional Resources