声明 Zend 函数块
现在你已经声明了导出函数,除此之外你还必须得将其引入 Zend 。这些函数的引入是通过一个包含有 N 个zend_function_entry 结构的数组来完成的。数组的每一项都对应于一个外部可见的函数,每一项都包含了某个函数在 PHP 中出现的名字以及在 C 代码中所定义的名字。zend_function_entry 的内部定义如“例3.4 zend_function_entry 的内部声明”所示:
typedef struct _zend_function_entry {
char *fname;
void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
unsigned char *func_arg_types;
} zend_function_entry;
对于上面的例子,我们可以这样来声明:
zend_function_entry firstmod_functions[] =
{
ZEND_FE(first_module, NULL)
{NULL, NULL, NULL}
};
你可能已经看到了,这个结构的最后一项是 {NULL, NULL, NULL} 。事实上,这个结构的最后一项也必须始终是 {NULL, NULL, NULL} ,因为 Zend Engine 需要靠它来确认这些导出函数的列表是否列举完毕。
注意:
你不应该使用一个预定义的宏来代替列表的结尾部分(即{NULL, NULL, NULL}),因为编译器会尽量寻找一个名为 “NULL” 的函数的指针来代替 NULL !
宏 ZEND_FE(“Zend Function Entry”的简写)将简单地展开为一个zend_function_entry 结构。不过需要注意,这些宏对函数采取了一种很特别的命名机制:把你的C函数前加上一个 zif_ 前缀。比方说,ZEND_FE(first_module) 其实是指向了一个名为 zif_first_module() 的 C 函数。如果你想把宏和一个手工编码的函数名混合使用时(这并不是一个好的习惯),请你务必注意这一点。
小提示: 如果出现了一些引用某个名为 zif_*() 函数的编译错误,那十有八九与 ZEND_FE 所定义的函数有关。
“表 3.2 可用来定义函数的宏”给出了一个可以用来定义一个函数的所有宏的列表:
|
宏 |
说明 |
| ZEND_FE(name, arg_types) |
定义了一个zend_function_entry 内字段name为 “name” 的函数。arg_types 应该被设定为 NULL。这个声明需要有一个对应的 C 函数,该这个函数的名称将自动以 zif_ 为前缀。举例来说, ZEND_FE("first_module", NULL) 就引入了一个名为 first_module() 的 PHP 函数,并被关联到一个名为 zif_first_module() 的C函数。这个声明通常与 ZEND_FUNCTION 搭配使用。 |
| ZEND_NAMED_FE(php_name, name, arg_types) |
定义了一个名为 php_name 的 PHP 函数,并且被关联到一个名为 name 的 C 函数。arg_types 应该被设定为 NULL。 如果你不想使用宏 ZEND_FE 自动创建带有 zif_ 前缀的函数名的话可以用这个来代替。通常与 ZEND_NAMED_FUNCTION搭配使用。 |
| ZEND_FALIAS(name, alias, arg_types) |
为 name 创建一个名为 alias 的别名。arg_types 应该被设定为 NULL。这个声明不需要有一个对应的 C 函数,因为它仅仅是创建了一个用来代替 name 的别名而已。 |
| PHP_FE(name, arg_types) |
以前的 PHP API,等同于 ZEND_FE 。仅为兼容性而保留,请尽量避免使用。 |
| PHP_NAMED_FE(runtime_name, name, arg_types) |
以前的 PHP API,等同于ZEND_NAMED_FE 。仅为兼容性而保留,请尽量避免使用。 |
注意:你不能将 ZEND_FE 和 PHP_FUNCTION 混合使用,也不能将PHP_FE 和 ZEND_FUNCTION 混合使用。但是将 ZEND_FE + ZEND_FUNCTION 和 PHP_FE + PHP_FUNCTION 一起混合使用是没有任何问题的。当然我们并不推荐这样的混合使用,而是建议你全部使用 ZEND_* 系列的宏。
Zend 模块的信息被保存在一个名为zend_module_entry 的结构,它包含了所有需要向 Zend 提供的模块信息。你可以在“例 3.5 zend_module_entry 的内部声明”中看到这个 Zend 模块的内部定义:
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;
… // 其余的一些我们不感兴趣的信息
};
在我们的例子当中,这个结构被定义如下:
zend_module_entry firstmod_module_entry =
{
STANDARD_MODULE_HEADER,
"First Module",
firstmod_functions,
NULL, NULL, NULL, NULL, NULL,
NO_VERSION_YET,
STANDARD_MODULE_PROPERTIES,
};
这基本上是你可以设定最简单、最小的一组值。该模块名称为“First Module”,然后是所引用的函数列表,其后所有的启动和关闭函数都没有使用,均被设定为了 NULL。
作为参考,你可以在表 3.3 “所有可声明模块启动和关闭函数的宏”中找到所有的可设置启动与关闭函数的宏。这些宏暂时在我们的例子中还尚未用到,但稍后我们将会示范其用法。你应该使用这些宏来声明启动和关闭函数,因为它们都需要引入一些特殊的变量(INIT_FUNC_ARGS 和 SHUTDOWN_FUNC_ARGS),而这两个参数宏将在你使用下面这些预定义宏时被自动引入(其实就是图个方便)。如果你是手工声明的函数或是对函数的参数列表作了一些必要的修改,那么你就应该修改你的模块相应的源代码来保持兼容。
|
宏 |
描述 |
| ZEND_MINIT(module) |
声明一个模块的启动函数。函数名被自动设定为zend_minit_<module> (比如:zend_minit_first_module)。通常与ZEND_MINIT_FUNCTION 搭配使用。 |
| ZEND_MSHUTDOWN(module) |
声明一个模块的关闭函数。函数名被自动设定为zend_mshutdown_<module> (比如:zend_mshutdown_first_module)。通常与ZEND_MSHUTDOWN_FUNCTION搭配使用。 |
| ZEND_RINIT(module) |
声明一个请求的启动函数。函数名被自动设定为zend_rinit_<module> (比如:zend_rinit_first_module)。通常与ZEND_RINIT_FUNCTION搭配使用。 |
| ZEND_RSHUTDOWN(module) |
声明一个请求的关闭函数。函数名被自动设定为zend_rshutdown_<module> (比如:zend_rshutdown_first_module)。通常与ZEND_RSHUTDOWN_FUNCTION 搭配使用。 |
| ZEND_MINFO(module) |
声明一个输出模块信息的函数,用于phpinfo()。函数名被自动设定为zend_info_<module> (比如:zend_info_first_module)。通常与ZEND_MINFO_FUNCTION搭配使用。 |