The PHP Request Life CycleNow that you have a decent understanding of how the Zend Engine works, let's look at how the engine sits inside PHP and how PHP itself sits inside other applications. Any discussion of the architecture of PHP starts with a diagram such as Figure 20.2, which shows the application layers in PHP. Figure 20.2. The architecture of PHP.
The outermost layer, where PHP interacts with other applications, is the Server Abstraction API (SAPI) layer. The SAPI layer partially handles the startup and shutdown of PHP inside an application, and it provides hooks for handling data such as cookies and POST data in an application-agnostic manner. Below the SAPI layer lies the PHP engine itself. The core PHP code handles setting up the running environment (populating global variables and setting default .ini options), providing interfaces such as the stream's I/O interface, parsing of data, and most importantly, providing an interface for loading extensions (both statically compiled extensions and dynamically loaded extensions). At the core of PHP lies the Zend Engine, which we have discussed in depth here. As you've seen, the Zend Engine fully handles the parsing and execution of scripts. The Zend Engine was also designed for extensibility and allows for entirely overriding its basic functionality (compilation, execution, and error handling), overriding selective portions of its behavior (overriding op_handlers in particular ops), and having functions called on registerable hooks (on every function call, on every opcode, and so on). These features allow for easy integration of caches, profilers, debuggers, and semantics-altering extensions. The SAPI LayerThe SAPI layer is the abstraction layer that allows for easy embedding of PHP into other applications. Some SAPIs include the following:
The idea is that regardless of the application, PHP needs to communicate with an application in a number of common places, so the SAPI interface provides a hook for each of those places. When an application needs to start up PHP, for instance, it calls the startup hook. Conversely, when PHP wants to output information, it uses the provided ub_write hook, which the SAPI layer author has coded to use the correct output method for the application PHP is running in. To understand the capabilities of the SAPI layer, it is easiest to look at the hooks it implements. Every SAPI interface registers the following struct, with PHP describing the callbacks it implements: struct _sapi_module_struct { char *name; char *pretty_name; int (*startup)(struct _sapi_module_struct *sapi_module); int (*shutdown)(struct _sapi_module_struct *sapi_module); int (*activate)(TSRMLS_D); int (*deactivate)(TSRMLS_D); int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC); void (*flush)(void *server_context); struct stat *(*get_stat)(TSRMLS_D); char *(*getenv)(char *name, size_t name_len TSRMLS_DC); void (*sapi_error)(int type, const char *error_msg, ...); int (*header_handler)(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC); int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC); void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC); int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC); char *(*read_cookies)(TSRMLS_D); void (*register_server_variables)(zval *track_vars_array TSRMLS_DC); void (*log_message)(char *message); char *php_ini_path_override; void (*block_interruptions)(void); void (*unblock_interruptions)(void); void (*default_post_reader)(TSRMLS_D); void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC); char *executable_location; int php_ini_ignore; int (*get_fd)(int *fd TSRMLS_DC); int (*force_http_10)(TSRMLS_D); int (*get_target_uid)(uid_t * TSRMLS_DC); int (*get_target_gid)(gid_t * TSRMLS_DC); unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len TSRMLS_DC); void (*ini_defaults)(HashTable *configuration_hash); int phpinfo_as_text; }; The following are some of the notable elements from this example:
Chapter 23 takes a closer look at using the SAPI interface to integrate PHP into applications and does a complete walkthrough of the CGI SAPI. The PHP CoreThere are several key steps in activating and running a PHP interpreter. When an application wants to start a PHP interpreter, it starts by calling php_module_startup. This function is like the master switch that turns on the interpreter. It activates the registered SAPI, initializes the output buffering system, starts the Zend Engine, reads in and acts on the php.ini file, and prepares the interpreter for its first request. Some important functions that are used in the core are
The PHP Extension APIMost of our discussion regarding the PHP extension API will be carried on in Chapter 22, where you will actually implement extensions. Here we'll only look at the basic callbacks available to extensions and when they are called. Extensions can be registered in two ways. When an extension is compiled statically into PHP, the configuration system permanently registers that module with PHP. An extension can also be loaded from the .ini file, in which case it is registered during the .ini parsing. The hooks that an extension can register are contained in its zend_module_entry function, like so: struct _zend_module_entry { unsigned short size; unsigned int zend_api; unsigned char zend_debug; unsigned char zts; struct _zend_ini_entry *ini_entry; char *name; zend_function_entry *functions; int (*module_startup_func)(INIT_FUNC_ARGS); int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); int (*request_startup_func)(INIT_FUNC_ARGS); int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); char *version; int (*global_startup_func)(void); int (*global_shutdown_func)(void); int globals_id; int module_started; unsigned char type; void *handle; int module_number; }; The following are some important elements of this struct:
The Zend Extension APIThe 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:
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:
How All the Pieces Fit TogetherThe preceding sections provide a lot of information. PHP, SAPIs, the Zend Enginethere 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. Figure 20.3. The mod_php5 request life cycle. |