第一步,先完成一个最简单的扩展,只提供一个函数,hello。
主要代码:
ZEND_FUNCTION(hello)
{
php_printf(
"
Hello World!\n
");
}
static zend_function_entry tonic_functions[] = {
ZEND_FE(hello, NULL)
{ NULL, NULL, NULL }
};
zend_module_entry tonic_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"
tonic
",
tonic_functions,
/*
Functions
*/
NULL,
/*
MINIT
*/
NULL,
/*
MSHUTDOWN
*/
NULL,
/*
RINIT
*/
NULL,
/*
RSHUTDOWN
*/
NULL,
/*
MINFO
*/
#if ZEND_MODULE_API_NO >= 20010901
"
2.1
",
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_TONIC
ZEND_GET_MODULE(tonic)
#endif
这几行代码,就完成了一个最简单的扩展了...
ZEND_GET_MODULE宏展开如下:
#define ZEND_GET_MODULE(name) \
BEGIN_EXTERN_C()\
ZEND_DLEXPORT zend_module_entry *get_module(
void) {
return &name##_module_entry; }\
END_EXTERN_C()
这个宏,定义了一个名为get_module的函数,返回当前模块定义的zend_module_entry的指针。(通过这个宏可以发现,module_entry的名称,是固定格式的..不然就要自己实现get_module函数)
再看看php加载扩展的实现(我去掉了一些老版本兼容等与分析无关的代码) :
PHPAPI
int php_load_extension(
char *filename,
int type,
int start_now TSRMLS_DC)
/*
{{{
*/
{
void *handle;
char *libpath;
zend_module_entry *module_entry;
zend_module_entry *(*get_module)(
void); /* 函数指针,获取module的信息 */
int error_type;
char *extension_dir;
if (type == MODULE_PERSISTENT) {
extension_dir = INI_STR(
"
extension_dir
");
}
else {
extension_dir = PG(extension_dir);
}
if (type == MODULE_TEMPORARY) {
error_type = E_WARNING;
}
else {
error_type = E_CORE_WARNING;
}
/*
Check if passed filename contains directory separators
*/
if (strchr(filename,
'
/
') != NULL || strchr(filename, DEFAULT_SLASH) != NULL) {
/*
Passing modules with full path is not supported for dynamically loaded extensions
*/
if (type == MODULE_TEMPORARY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"
Temporary module name should contain only filename
");
return FAILURE;
}
libpath = estrdup(filename);
}
else
if (extension_dir && extension_dir[
0]) {
int extension_dir_len = strlen(extension_dir);
if (IS_SLASH(extension_dir[extension_dir_len-
1])) {
spprintf(&libpath,
0,
"
%s%s
", extension_dir, filename);
/*
SAFE
*/
}
else {
spprintf(&libpath,
0,
"
%s%c%s
", extension_dir, DEFAULT_SLASH, filename);
/*
SAFE
*/
}
}
else {
return FAILURE;
/*
Not full path given or extension_dir is not set
*/
}
/*
load dynamic symbol
*/
handle = DL_LOAD(libpath); /* 加载动态链接文件 */
if (!handle) {
php_error_docref(NULL TSRMLS_CC, error_type,
"
Unable to load dynamic library '%s' - %s
", libpath, GET_DL_ERROR());
GET_DL_ERROR();
/*
free the buffer storing the error
*/
efree(libpath);
return FAILURE;
}
efree(libpath);
get_module = (zend_module_entry *(*)(
void)) DL_FETCH_SYMBOL(handle,
"
get_module
");/* 获取动态链接库内部实现的get_module函数的指针供下面调用 */
/*
Some OS prepend _ to symbol names while their dynamic linker
* does not do that automatically. Thus we check manually for
* _get_module.
*/
if (!get_module) {
get_module = (zend_module_entry *(*)(
void)) DL_FETCH_SYMBOL(handle,
"
_get_module
");
}
if (!get_module) { /* 如果动态链接库没有实现get_module函数,或者函数原型与前面定义的不符,则不是合法的PHP扩展 */
DL_UNLOAD(handle);
php_error_docref(NULL TSRMLS_CC, error_type,
"
Invalid library (maybe not a PHP library) '%s'
", filename);
return FAILURE;
}
module_entry = get_module();/* 执行get_module函数,获取zend_module_entry的信息,这里是个指针 */
module_entry->type = type;
module_entry->module_number = zend_next_free_module();
module_entry->handle = handle;
if ((module_entry = zend_register_module_ex(module_entry TSRMLS_CC)) == NULL) { /* 这里完成三个任务,第一判断此扩展依赖的其它扩展是否都已经正确加载,第二将扩展的module_entry添加到全局的module_registry这个HashTable中,第三,注册扩展提供的所有函数 */
DL_UNLOAD(handle);
return FAILURE;
}
if ((type == MODULE_TEMPORARY || start_now) && zend_startup_module_ex(module_entry TSRMLS_CC) == FAILURE) { /* 执行module_entry的MINIT函数 */
DL_UNLOAD(handle);
return FAILURE;
}
if ((type == MODULE_TEMPORARY || start_now) && module_entry->request_startup_func) {
if (module_entry->request_startup_func(type, module_entry->module_number TSRMLS_CC) == FAILURE) { /* 执行module_entry的RINIT函数 */
php_error_docref(NULL TSRMLS_CC, error_type,
"
Unable to initialize module '%s'
", module_entry->name);
DL_UNLOAD(handle);
return FAILURE;
}
}
return SUCCESS;
}