Source Discussion
Now that you've got a safe build environment and you're able to include
the modules into PHP files, it's time to discuss how everything works.
Module Structure
All PHP modules follow a common structure:
Header file inclusions (to include all required macros, API
definitions, etc.)
C declaration of exported functions (required to declare the Zend
function block)
Declaration of the Zend function block
Declaration of the Zend module block
Implementation of get_module
Implementation of all exported functions
Declaring Exported Functions
To declare functions that are to be exported (i.e., made available to PHP
as new native functions), Zend provides a set of macros. A sample declaration
looks like this:
ZEND_FUNCTION ( my_function );
ZEND_FUNCTION declares a new C function that complies
with Zend's internal API. This means that the function is of
type void and
accepts INTERNAL_FUNCTION_PARAMETERS (another macro) as
parameters. Additionally, it prefixes the function name with
zif. The immediately expanded version of the above
definitions would look like this:
void zif_my_function ( INTERNAL_FUNCTION_PARAMETERS );
Expanding INTERNAL_FUNCTION_PARAMETERS
results in the following:
void zif_my_function( int ht
, zval * return_value
, zval * this_ptr
, int return_value_used
, zend_executor_globals * executor_globals
);
Since the interpreter and executor core have been separated from
the main PHP package, a second API defining macros and function
sets has evolved: the Zend API. As the Zend API now handles quite
a few of the responsibilities that previously belonged to PHP, a
lot of PHP functions have been reduced to macros aliasing to calls
into the Zend API. The recommended practice is to use the Zend API
wherever possible, as the old API is only preserved for
compatibility reasons. For example, the types zval
and pval are identical. zval is
Zend's definition; pval is PHP's definition
(actually, pval is an alias for zval
now). As the macro INTERNAL_FUNCTION_PARAMETERS
is a Zend macro, the above declaration contains
zval. When writing code, you should always use
zval to conform to the new Zend API.
The parameter list of this declaration is very important; you should keep these parameters in mind (see for descriptions).
Zend's Parameters to Functions Called from PHP
Parameter
Description
ht
The number of arguments passed to the Zend function.
You should not touch this directly, but instead use ZEND_NUM_ARGS() to obtain the
value.
return_value
This variable is used to pass any return values of
your function back to PHP. Access to this variable is best done using the
predefined macros. For a description of these see below.
this_ptr
Using this variable, you can gain access to the object
in which your function is contained, if it's used within an object. Use
the function getThis to obtain this pointer.
return_value_used
This flag indicates whether an eventual return value
from this function will actually be used by the calling script.
0 indicates that the return value is not used;
1 indicates that the caller expects a return value.
Evaluation of this flag can be done to verify correct usage of the function as
well as speed optimizations in case returning a value requires expensive
operations (for an example, see how array.c makes use of
this).
executor_globals
This variable points to global settings of the Zend
engine. You'll find this useful when creating new variables, for example
(more about this later). The executor globals can also be introduced to your
function by using the macro TSRMLS_FETCH().
Declaration of the Zend Function Block
Now that you have declared the functions to be exported, you also
have to introduce them to Zend. Introducing the list of functions is done by
using an array of zend_function_entry. This array consecutively
contains all functions that are to be made available externally, with the function's name
as it should appear in PHP and its name as defined in the C source.
Internally, zend_function_entry is defined as shown in
.
Internal declaration of zend_function_entry.
typedef struct _zend_function_entry {
char *fname;
void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
unsigned char *func_arg_types;
} zend_function_entry;
Entry
Description
fname
Denotes the function name as seen in PHP (for
example, fopen, mysql_connect, or, in our
example, first_module).
handler
Pointer to the C function responsible for handling calls
to this function. For example, see the standard macro
INTERNAL_FUNCTION_PARAMETERS discussed earlier.
func_arg_types
Allows you to mark certain parameters so that they're forced
to be passed by reference. You usually should set this to
NULL.
In the example above, the declaration looks like this:
zend_function_entry firstmod_functions[] =
{
ZEND_FE(first_module, NULL)
{NULL, NULL, NULL}
};
You can see that the last entry in the list always has to be
{NULL, NULL, NULL}.
This marker has to be set for Zend to know when the end of the
list of exported functions is reached.
You cannot use the predefined macros for the
end marker, as these would try to refer to a function named "NULL"!
The macro ZEND_FE (short for 'Zend Function
Entry') simply expands to a structure entry in
zend_function_entry. Note that these macros
introduce a special naming scheme to your functions - your C
functions will be prefixed with zif_, meaning
that ZEND_FE(first_module) will refer to a C
function zif_first_module. If you want to mix
macro usage with hand-coded entries (not a good practice), keep
this in mind.
Tip: Compilation errors that refer to functions
named zif_* relate to functions defined
with ZEND_FE.
shows a list of all the macros
that you can use to define functions.
Macros for Defining Functions
Macro Name
Description
ZEND_FE(name, arg_types)
Defines a function entry of the name name in
zend_function_entry. Requires a corresponding C
function. arg_types needs to be set to NULL.
This function uses automatic C function name generation by prefixing the PHP
function name with zif_.
For example, ZEND_FE("first_module", NULL) introduces a
function first_module to PHP and links it to the C
function zif_first_module. Use in conjunction
with ZEND_FUNCTION.
ZEND_NAMED_FE(php_name, name, arg_types)
Defines a function that will be available to PHP by the
name php_name and links it to the corresponding C
function name. arg_types needs to be set
to NULL. Use this function if you don't want the automatic
name prefixing introduced by ZEND_FE. Use in conjunction
with ZEND_NAMED_FUNCTION.
ZEND_FALIAS(name, alias, arg_types)
Defines an alias named alias for
name. arg_types needs to be set
to NULL. Doesn't require a corresponding C
function; refers to the alias target instead.
PHP_FE(name, arg_types)
Old PHP API equivalent of ZEND_FE.
PHP_NAMED_FE(runtime_name, name, arg_types)
Old PHP API equivalent of ZEND_NAMED_FE.
Note: You can't use
ZEND_FE in conjunction with
PHP_FUNCTION, or PHP_FE in
conjunction with ZEND_FUNCTION. However, it's
perfectly legal to mix ZEND_FE and
ZEND_FUNCTION with PHP_FE
and PHP_FUNCTION when staying with the same
macro set for each function to be declared. But mixing is
not recommended; instead, you're advised to
use the ZEND_* macros only.
Declaration of the Zend Module Block
This block is stored in the structure
zend_module_entry and contains all necessary
information to describe the contents of this module to Zend. You can
see the internal definition of this module in
.
Internal declaration of zend_module_entry.
typedef struct _zend_module_entry zend_module_entry;
struct _zend_module_entry {
unsigned short size;
unsigned int zend_api;
unsigned char zend_debug;
unsigned char zts;
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;
[ Rest of the structure is not interesting here ]
};
Entry
Description
size, zend_api,
zend_debug and zts
Usually filled with the
"STANDARD_MODULE_HEADER", which fills these
four members with the size of the whole zend_module_entry, the
ZEND_MODULE_API_NO, whether it is a debug
build or normal build (ZEND_DEBUG) and if
ZTS is enabled (USING_ZTS).
name
Contains the module name (for example, "File
functions", "Socket functions",
"Crypt", etc.). This name will show up in
phpinfo, in the section "Additional
Modules."
functions
Points to the Zend function block, discussed in the preceding
section.
module_startup_func
This function is called once upon module initialization and can
be used to do one-time initialization steps (such as initial
memory allocation, etc.). To indicate a failure during
initialization, return FAILURE; otherwise,
SUCCESS. To mark this field as unused, use
NULL. To declare a function, use the macro
ZEND_MINIT.
module_shutdown_func
This function is called once upon module shutdown and can be
used to do one-time deinitialization steps (such as memory
deallocation). This is the counterpart to
module_startup_func. To indicate a failure
during deinitialization, return FAILURE;
otherwise, SUCCESS. To mark this field as
unused, use NULL. To declare a function, use
the macro ZEND_MSHUTDOWN.
request_startup_func
This function is called once upon every page request and can be
used to do one-time initialization steps that are required to
process a request. To indicate a failure here, return
FAILURE; otherwise,
SUCCESS. Note: As
dynamic loadable modules are loaded only on page requests, the
request startup function is called right after the module
startup function (both initialization events happen at the same
time). To mark this field as unused, use
NULL. To declare a function, use the macro
ZEND_RINIT.
request_shutdown_func
This function is called once after every page request and works
as counterpart to request_startup_func. To
indicate a failure here, return FAILURE;
otherwise, SUCCESS.
Note: As dynamic loadable modules are
loaded only on page requests, the request shutdown function is
immediately followed by a call to the module shutdown handler
(both deinitialization events happen at the same time). To mark
this field as unused, use NULL. To declare a
function, use the macro ZEND_RSHUTDOWN.
info_func
When phpinfo is called in a script, Zend
cycles through all loaded modules and calls this function.
Every module then has the chance to print its own "footprint"
into the output page. Generally this is used to dump
environmental or statistical information. To mark this field as
unused, use NULL. To declare a function, use
the macro ZEND_MINFO.
version
The version of the module. You can use
NO_VERSION_YET if you don't want to give the
module a version number yet, but we really recommend that you
add a version string here. Such a version string can look like
this (in chronological order): "2.5-dev",
"2.5RC1", "2.5" or
"2.5pl3".
Remaining structure elements
These are used internally and can be prefilled by using the
macro STANDARD_MODULE_PROPERTIES_EX. You
should not assign any values to them. Use
STANDARD_MODULE_PROPERTIES_EX only if you
use global startup and shutdown functions; otherwise, use
STANDARD_MODULE_PROPERTIES directly.
In our example, this structure is implemented as follows:
zend_module_entry firstmod_module_entry =
{
STANDARD_MODULE_HEADER,
"First Module",
firstmod_functions,
NULL, NULL, NULL, NULL, NULL,
NO_VERSION_YET,
STANDARD_MODULE_PROPERTIES,
};
This is basically the easiest and most minimal set of values you
could ever use. The module name is set to First
Module, then the function list is referenced, after which
all startup and shutdown functions are marked as being unused.
For reference purposes, you can find a list of the macros involved
in declared startup and shutdown functions in
. These are
not used in our basic example yet, but will be demonstrated later
on. You should make use of these macros to declare your startup and
shutdown functions, as these require special arguments to be passed
(INIT_FUNC_ARGS and
SHUTDOWN_FUNC_ARGS), which are automatically
included into the function declaration when using the predefined
macros. If you declare your functions manually and the PHP
developers decide that a change in the argument list is necessary,
you'll have to change your module sources to remain compatible.
Macros to Declare Startup and Shutdown Functions
Macro
Description
ZEND_MINIT(module)
Declares a function for module startup. The generated name will
be zend_minit_<module> (for example,
zend_minit_first_module). Use in
conjunction with ZEND_MINIT_FUNCTION.
ZEND_MSHUTDOWN(module)
Declares a function for module shutdown. The generated name
will be zend_mshutdown_<module> (for
example, zend_mshutdown_first_module). Use
in conjunction with ZEND_MSHUTDOWN_FUNCTION.
ZEND_RINIT(module)
Declares a function for request startup. The generated name
will be zend_rinit_<module> (for
example, zend_rinit_first_module). Use in
conjunction with ZEND_RINIT_FUNCTION.
ZEND_RSHUTDOWN(module)
Declares a function for request shutdown. The generated name
will be zend_rshutdown_<module> (for
example, zend_rshutdown_first_module). Use
in conjunction with ZEND_RSHUTDOWN_FUNCTION.
ZEND_MINFO(module)
Declares a function for printing module information, used when
phpinfo is called. The generated name will
be zend_info_<module> (for example,
zend_info_first_module). Use in conjunction
with ZEND_MINFO_FUNCTION.
Creation of get_module
This function is special to all dynamic loadable modules. Take a
look at the creation via the ZEND_GET_MODULE
macro first:
#if COMPILE_DL_FIRSTMOD
ZEND_GET_MODULE(firstmod)
#endif
The function implementation is surrounded by a conditional
compilation statement. This is needed since the function
get_module is only required if your module is
built as a dynamic extension. By specifying a definition of
COMPILE_DL_FIRSTMOD in the compiler command
(see above for a discussion of the compilation instructions
required to build a dynamic extension), you can instruct your
module whether you intend to build it as a dynamic extension or as
a built-in module. If you want a built-in module, the
implementation of get_module is simply left
out.
get_module is called by Zend at load time
of the module. You can think of it as being invoked by the
dl call in your script. Its purpose is to pass the
module information block back to Zend in order to inform the engine about the
module contents.
If you don't implement a get_module function in
your dynamic loadable module, Zend will compliment you with an error message
when trying to access it.
Implementation of All Exported Functions
Implementing the exported functions is the final step. The
example function in first_module looks like this:
ZEND_FUNCTION(first_module)
{
long parameter;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", ¶meter) == FAILURE) {
return;
}
RETURN_LONG(parameter);
}
The function declaration is done
using ZEND_FUNCTION, which corresponds
to ZEND_FE in the function entry table (discussed
earlier).
After the declaration, code for checking and retrieving the function's
arguments, argument conversion, and return value generation follows (more on
this later).
Summary
That's it, basically - there's nothing more to implementing PHP modules.
Built-in modules are structured similarly to dynamic modules, so, equipped
with the information presented in the previous sections, you'll be able to
fight the odds when encountering PHP module source files.
Now, in the following sections, read on about how to make use of PHP's
internals to build powerful extensions.