
Advanced PHP Programming
.pdf
528 Chapter 21 Extending PHP: Part I
In most functions, you are handed a resource handle zval, and you need to extract the actual resource for it. Fortunately, doing so is very easy. If you are looking in a single list, you can use the following macro:
ZEND_FETCH_RESOURCE(void *rsrc_struct, rsrc_struct_type, zval **zval_id, int default_id, char *name, int rsrc_list);
These are the arguments of ZEND_FETCH_RESOURCE():
n rsrc_struct is the actual pointer you want the resource data to be stored in.
n rsrc_struct_type is the type of struct the resource is (for example, FILE *).
n zval_id is a zval of resource type that contains the resource ID.
n default_id is an integer that specifies the default resource to use. A common use for this is to store the last accessed resource ID in an extension’s globals.Then, if a function that requires a resource does not have one passed to it, it simply uses the last resource ID. If -1 is used, no default is attempted.
n name is a character string that is used to identify the resource you were seeking. This string is used only in information warning messages and has no technical purpose.
n rsrc_list is the list that should be searched for the resource.
If the resource fetch fails, a warning is generated, and the current function returns NULL. The following is the function pfgets(),which reads a line from a file resource creat-
ed by pfopen():
PHP_FUNCTION(pfgets)
{
char *out;
int length = 1024; zval *rsrc;
FILE *fh;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “r|l”, &rsrc, &length) == FAILURE) {
return;
}
ZEND_FETCH_RESOURCE(fh, FILE *, rsrc, -1, “Persistent File Handle”, persist); out = (char *) emalloc(length);
fgets(out, length, fh); RETURN_STRING(out, 0);
}

Extension Basics |
529 |
Returning Errors
Generating procedural errors in extension code is almost identical to generating errors in PHP. Instead of calling trigger_error() in PHP, you can use zend_error() in C. zend_error() has the following API:
zend_error(int error_type, char *fmt, ...);
error_type is the full range of errors enumerated in Chapter 3,“Error Handling.” Otherwise, the API is identical to the printf() family of functions.The following function generates a warning:
zend_error(E_WARNING, “Hey this is a warning”);
Remember that if you use E_ERROR, the error is fatal, and script execution is stopped. (Chapter 23,“Writing SAPIs and Extending the Zend Engine,” describes how to override this behavior).
Throwing exceptions is covered in detail in Chapter 22, which looks at object-ori- ented extensions in detail.
Using Module Hooks
In addition to enabling you to define and export function definitions, PHP also gives extensions the ability to run code in response to certain events in the PHP runtime. These events include the following:
nModule startup
nModule shutdown
nRequest startup
nRequest shutdown
nphpinfo registration
When you create a module, one of the required components is zend_module_entry, which looks like this:
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),
VERSION,
STANDARD_MODULE_PROPERTIES
};

530Chapter 21 Extending PHP: Part I
The third member of this structure, example_functions, specifies the array of functions that will be registered by the extension.The rest of the structure declares the callbacks that will be executed by the various module hooks.
Module Startup and Shutdown
An extension’s module initialization and shutdown hooks are called when the extension is loaded and unloaded, respectively. For most extensions (those that are either compiled statically into PHP or loaded via an INI setting), module initialization happens once, at server startup. Module shutdown is similarly called during server shutdown. In the Apache 1.3 (or Apache 2 prefork MPM), this hook is called before any children are forked off.Thus, it is an ideal place to create or initialize any sort of global or shared resource, and it’s a poor place to initialize any resource that cannot be shared between processes.
The module initialization hook is registered via the following function:
PHP_MINIT_FUNCTION(example)
{
return SUCCESS;
}
In general, module initialization is the ideal place to define constants, initialize global data structures, and register and parse INI options.
Defining Constants
Because constants are immutable, they should be created during module initialization. In contrast to userspace PHP, where using a define() is not very different performancewise from using global variables, defining constants in extension code is a clear win.This is because extension constants (such as functions and classes) do not need to be reinstated between requests (although you can specify them to be destroyed at request end).This means that declaring even a large number of constants is basically free.
To define a constant, you can use the following macros:
REGISTER_LONG_CONSTANT(name, value, flags)
REGISTER_DOUBLE_CONSTANT(name, value, flags)
REGISTER_STRING_CONSTANT(name, string, flags)
REGISTER_STRNIG_CONSTANT(name, string, string_length, flags)
These are the possible flags for the macros:
nCONST_CS—Constant is case-sensitive.
nCONST_PERSISTENT—Constant should persist across requests.
Obviously, if you are defining constants during module initialization, you must specify CONST_PERSISTENT. Unless you have specific reasons that you need to use conditional defines, you should define your constants as persistent and register them during module


532Chapter 21 Extending PHP: Part I
To initialize globals, you create an initialization and destruction function, like this:
static void example_init_globals(zend_example_globals *example_globals)
{
example_globals->default_path = NULL;
}
static void example_destroy_globals(zend_example_globals *example_globals)
{
}
Then, during the MINIT phase, you perform the registration via the ZEND_INIT_ MODULE_GLOBALS() macro, as shown here:
PHP_MINIT_FUNCTION(example)
{
ZEND_INIT_MODULE_GLOBALS(example, example_init_globals, example_destroy_globals); /* ... */
}
This destructor function is usually used when there are complex data types (such as a hashtable) that need to be cleaned on shutdown. If you do not need to register a destructor, you can simply pass NULL into the macro.
Parsing INI Entries
One thing that you can do in extensions that is impossible in userspace PHP code is registering and acting on php.ini settings. INI settings are useful for a couple reasons:
nThey provide global settings, independent of scripts.
nThey provide access controls on settings that can restrict developers from changing the INI settings in their scripts.
nThey allow for configuration of module hooks that are called before any scripts are run (during MINIT and RINIT, for instance).
PHP provides a set of macros for easy registration of INI directives. First, in the main body of the C file, you add a macro block, like this:
PHP_INI_BEGIN()
/* ini specifications go here ... */
PHP_INI_END()
This defines an array of zend_ini_entry entries. Inside the block you make your INI declarations via the following macro:
STD_PHP_INI_ENTRY(char *ini_directive, char *default_value,
int location, int type, struct_member,
struct_ptr, struct_property)

Extension Basics |
533 |
“ini_directive” is the full name of the INI directive that you are creating. It is a polite convention to namespace INI directives to avoid potential conflicts. For example, if you want to create an enabled setting for the sample extension, you should name it example.enabled.
default_value specifies the default value for the INI directive. Because INI values are set as strings in the php.ini file, the default value must be passed as a string, even if it is numeric.This value is copied, so using a statically allocated value is fine.
location specifies the places where a user can set the value of the directive.These places are defined as constants and can of course be combined with the bitwise OR operator.The following are acceptable bit settings for location:
Setting |
Description |
PHP_INI_USER |
Entry can be set in user scripts via ini_set(). |
PHP_INI_PERDIR |
Entry can be set in php.ini, .htaccess, or |
|
httpd.conf. In the .htaccess or httpd.conf file, it |
|
can be applied on a per-directory basis. |
PHP_INI_SYSTEM |
Entry can be set in php.ini or httpd.conf.The setting |
|
is serverwide. |
PHP_INI_ALL |
Entry can be set anywhere.This is equivalent to |
|
PHP_INI_USER|PHP_INI_PERDIR|PHP_INI_SYSTEM. |
type is a function name that specifies how to handle modifications to the INI directive (via php.ini, .htaccess, httpd.conf, or ini_set()).The following are the standard functions that can be used in this macro:
Function |
Destination C Type |
OnUpdateBool |
zend_bool |
OnUpdateLong |
long |
OnUpdateReal |
double |
OnUpdateString |
char * |
OnUpdateStringUnempty |
char * |
These functions are aptly named and should be self-explanatory. OnUpdateStringUnempty fails if an empty string is passed to it. Otherwise, it is identical to OnUpdateString.
INI values are almost always stored in extension globals.This makes sense because for an individual script, the INI values are globally set. (Even when you change them using ini_set(), you are effecting a global change.) In threaded environments, INI values are stored in thread local globals, so modification of an INI value affects only the value for that specific thread.To specify which global variable the setting should be stored in, you pass the final 3 bits of information.

534 Chapter 21 Extending PHP: Part I
struct_type specifies the type of the structure you will be setting the value into. In the normal case, where this is the globals structure you created with ZEND_BEGIN_ MODULE_GLOBALS(example), this type would be zend_example_globals.
struct_ptr gives the specific instance of the type struct_type that should be modified. In the usual case, where globals are declared via the built-in macros, this is example_globals.
Finally, struct_property notes the element of the struct struct_name to modify. In the case of an integer value set, the STD_PHP_INI_ENTRY() macro roughly trans-
lates into the following C code:
(struct_type *)struct_ptr->struct_property = default_value;
The following is an example that allows setting of the default_path global in the sample extension via the INI directive example.path:
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY(“example.path”, NULL, PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateString, default_path, zend_example_globals,
example_globals)
STD_PHP_INI_ENTRY(“example.debug”, “off”, PHP_INI_ALL, OnUpdateBool, debug, zend_example_globals, example_globals)
PHP_INI_END()
The default path will be set to NULL, and access to this variable will only be allowed from the php.ini, httpd.conf, or .htaccess files. It also allows you to set debug, with a default value of off, from anywhere.
To then register these entries, you call REGISTER_INI_ENTRIES() in the MINIT function, as follows:
PHP_MINIT_FUNCTION(example)
{
ZEND_INIT_MODULE_GLOBALS(example, example_init_globals, example_destroy_globals);
REGISTER_INI_ENTRIES();
}
If you want to access the values in the code (via ini_get()), you can use a number of macros, which fetch the INI values as specified C types.The macros are broken into two groups.The first set, shown in Table 21.6, returns the current value of the macro.
Table 21.6 Current INI Setting Accessors
Macro |
Return C Type |
INI_BOOL(name) |
zend_bool |
INI_INT(name) |
long |
INI_FLT(name) |
double |
INI_STR(name) |
char * |
|
|

Extension Basics |
535 |
The second set of macros, shown in Table 21.7, returns the original value of the macro, before any modification via httpd.conf, .htaccess, or ini_set().
Table 21.7 Original INI Setting Accessors
Macro |
Return C Type |
INI_BOOL_ORIG(name) |
zend_bool |
INI_INT_ORIG(name) |
long |
INI_FLT_ORIG(name) |
double |
INI_STR_ORIG(name) |
char * |
|
|
Module Shutdown
If you have registered INI entries during MINIT, it is appropriate to unregister them during shutdown.You can do this via the following code:
PHP_MSHUTDOWN_FUNCTION(example)
{
UNREGISTER_INI_ENTRIES();
}
Request Startup and Shutdown
In addition to module startup and shutdown, PHP also provides hooks that are called at the beginning and end of each request.The request initialization (RINIT) and shutdown (RSHUTDOWN) hooks are useful for creating and destroying per-request data.
Request Startup
Often you have resources that will be used in every request and that should always start at a consistent state. For example, ExampleG(default_path) may correspond with a file that needs to be opened at the beginning of every request and closed at the end (for example, a debugging log private to the extension and whose path can be set in an
.htaccess file, thus making a persistent resource impractical). In that case, you might want to open the log at the beginning of every request and exit with an error if this is not possible.
The code to perform this logic is placed in a PHP_RINIT_FUNCTION() block. At the beginning of every distinct request, PHP calls this function. If the function does not return SUCCESS, the request ends with a fatal error.The following is a request startup function that opens a default file at the beginning of every request:
PHP_RINIT_FUNCTION(example)
{
if(ExampleG(default_path)) {
ExampleG(default_fd) = open(ExampleG(default_path), O_RDWR|O_CREAT, 0); if(ExampleG(default_fd) == -1) {

536 Chapter 21 Extending PHP: Part I
return FAILURE;
}
}
return SUCCESS;
}
Request Shutdown
Request shutdown is the ideal place to close any resources that you need to make sure are destroyed at the end of a script. It is also an ideal place to ensure that the extension’s state is set back to where it should be before a new request. PHP_RSHUTDOWN_ FUNCTION() declares this hook.
In the following example, the sample extension needs to clean its logfile at request end:
PHP_RSHUTDOWN _FUNCTION(example) {
if(ExampleG(default_fd) > -1) {
close(ExampleG(default_fd));
ExampleG(default_fd) = -1;
}
return SUCCESS;
}
The extension needs to close the file descriptor ExampleG(default_fd) that it opened during RINIT. If you wanted to leave it open, you could, and it would persist across requests. Because it can be set on a per-directory basis via .htaccess rules, leaving it open in this case is impractical.
As in RINIT, this function must return SUCCESS, or the request will terminate with a fatal error.
phpinfo() Registration
PHP extensions are able to register themselves with phpinfo(), so that their status and configuration can be displayed.
The PHP_MINFO_FUNCTION() function is registered with the PHP_MINFO() macro:
zend_module_entry mysql_module_entry = {
STANDARD_MODULE_HEADER,
“example”,
example_functions,
PHP_MINIT(example),
PHP_MSHUTDOWN(example),
PHP_RINIT(example),
PHP_RSHUTDOWN(example),
PHP_MINFO(example),
VERSION,
STANDARD_MODULE_PROPERTIES
};

An Example: The Spread Client Wrapper |
537 |
PHP_MINFO_FUNCTION()is basically a CGI script that outputs certain information—usual- ly an HTML table that lists the function’s status and certain configuration information. To ease output formatting and support both plain-text and HTML phpinfo() formats, you should use the built-in functions to generate output.The following is a simple MINFO block that just notes that the sample extension is enabled:
PHP_MINFO_FUNCTION(example)
{
php_info_print_table_start();
php_info_print_table_row(2, “Example Extension”, “enabled”); php_info_print_table_end();
}
The php_info_print_table_row() function takes the number of columns and a string for each one.
An Example: The Spread Client Wrapper
You now have all the tools you need to build a procedural interface PHP extension in C.To tie all these parts together, a full example is called for.
Chapter 15,“Building a Distributed Environment,” shows an implementation of a distributed cache management system that uses Spread. Spread is a group communication toolkit that allows members to join a set of named groups and receive messages for those groups by using certain semantics (for example, that every member in the group will receive all messages in the same order as every other member).These strong rules provide an excellent mechanism for tackling distributed tasks, such as building multireader distributed logging systems, master–master database replication, or, as in the case just shown, reliable messaging systems between multiple participants.
The Spread library presents a very simple C API, so it is an ideal example for writing a PHP extension around.The following parts of the C API are covered here:
int |
SP_connect( const char *spread_name, const char *private_name, |
|
int priority, int group_membership, mailbox *mbox, |
|
char *private_group ); |
int |
SP_disconnect( mailbox mbox ); |
int |
SP_join( mailbox mbox, const char *group ); |
int |
SP_multicast( mailbox mbox, service service_type, |
|
const char *group, |
|
int16 mess_type, int mess_len, const char *mess ); |
int |
SP_multigroup_multicast( mailbox mbox, service service_type, |
|
int num_groups, |
|
const char groups[][MAX_GROUP_NAME], |
|
int16 mess_type, |
|
const scatter *mess ); |
int |
SP_receive( mailbox mbox, service *service_type, |
|
char sender[MAX_GROUP_NAME], int max_groups, |