欢迎转载,转载请注明出处 。
本文地址:http://imsiren.com/archives/547
一个简单的扩展模块
PHP非常容易扩展,因为它提供了我们想用的所有API.
如果要新建一个扩展,需要在PHP源码中执行ext_skel
位置 PHP源码目录/ext/ext_skel
它有几个参数
–extname=module module is the name of your extension
–proto=file file contains prototypes of functions to create
–stubs=file generate only function stubs in file
–xml generate xml documentation to be added to phpdoc-cvs
–skel=dir path to the skeleton directory
–full-xml generate xml documentation for a self-contained extension
(not yet implemented)
–no-help don’t try to be nice and create comments in the code
and helper functions to test if the module compiled
如果我们要建一个 扩展名称为siren的模块,那么我们只要执行
ext_skel –extname=siren 它就会在 ext/目录下生成以 模块名称为名的文件夹.而且还会创建一些文件:
config.m4 config.w32 CREDITS EXPERIMENTAL php_siren.h siren.c siren.php tests
config.m4 和config.w32是我们的配置文件,我是在linux下编译的 所以要修改config.m4文件
两种加载方式 with 和 enable
dnl PHP_ARG_WITH(siren, for siren support, dnl Make sure that the comment is aligned: dnl [ --with-siren Include siren support]) dnl Otherwise use enable: dnl PHP_ARG_ENABLE(siren, whether to enable siren support, dnl Make sure that the comment is aligned: dnl [ --enable-siren Enable siren support])
enable方式 需要重新编译PHP ,这样是非常浪费时间的,所以我把它编译为so模块..
所以就用 with啦
dnl PHP_ARG_WITH(siren, for siren support,
dnl Make sure that the comment is aligned:
dnl [ --with-siren Include siren support])
为
PHP_ARG_WITH(siren, for siren support,
Make sure that the comment is aligned:
[ --with-siren Include siren support])
这样在编译PHP的时候 –with-siren就可以加载此模块,也可以在php.ini中extension 模块.
在ext/siren目录下有一个siren.c文件
它提供了一个默认函数
PHP_FUNCTION(confirm_siren_compiled) { char *arg = NULL; int arg_len, len; char *strg; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { return; } len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "siren", arg); RETURN_STRINGL(strg, len, 0); }
如果看过 我之前的文章,你肯定明白 如果不知道 那就看看这篇文章
http://imsiren.com/archives/196
下面看看如何编译到PHP
1. /usr/local/php53/bin/phpize
2../configure –with-php-config=/usr/local/php53/bin/php-config
3.make && make install
这样 就会在/usr/local/php53/lib/php/extensions/no-debug-non-zts-20090626/目录下生成一个siren.so文件
这样 一个简单的扩展模块就完成了..我们在PHP.INI里面开启此模块
重启apache/nginx, 这样 在php文件里 就可以 执行 confirm_siren_compiled函数了.
下面我们就详细讲解一下里面的东西
首先是 php_siren.h
它是siren.c加载的头文件
#ifndef PHP_SIREN_H #define PHP_SIREN_H extern zend_module_entry siren_module_entry; #define phpext_siren_ptr &siren_module_entry #ifdef PHP_WIN32 # define PHP_SIREN_API __declspec(dllexport) #elif defined(__GNUC__) && __GNUC__ >= 4 # define PHP_SIREN_API __attribute__ ((visibility("default"))) #else # define PHP_SIREN_API #endif #ifdef ZTS #include "TSRM.h" #endif PHP_MINIT_FUNCTION(siren); PHP_MSHUTDOWN_FUNCTION(siren); PHP_RINIT_FUNCTION(siren); PHP_RSHUTDOWN_FUNCTION(siren); PHP_MINFO_FUNCTION(siren); PHP_FUNCTION(confirm_siren_compiled); /*这是一个测试函数*/ /* 如果要声明全局变量,就在这里声明 如果要启用 全局变量,那还要把siren.c中的ZEND_DECLARE_MODULE_GLOBALS(siren)注释去掉 ZEND_BEGIN_MODULE_GLOBALS(siren) long global_value; char *global_string; ZEND_END_MODULE_GLOBALS(siren) */ /* In every utility function you add that needs to use variables in php_siren_globals, call TSRMLS_FETCH(); after declaring other variables used by that function, or better yet, pass in TSRMLS_CC after the last function argument and declare your utility function with TSRMLS_DC after the last declared argument. Always refer to the globals in your function as SIREN_G(variable). You are encouraged to rename these macros something shorter, see examples in any other php module directory. */ #ifdef ZTS #define SIREN_G(v) TSRMG(siren_globals_id, zend_siren_globals *, v) #else #define SIREN_G(v) (siren_globals.v) #endif #endif /* PHP_SIREN_H */
上面有几个 PHP_*的函数,他们的作用如下
PHP_MINIT_FUNCTION() 当PHP被装载时,模块启动函数即被Zend引擎调用,这里可以做一些初始化操作
PHP_MSHUTDOWN_FUNCTION() 当PHP完全关闭时,Zend引擎调用的函数,
PHP_RINIT_FUNCTION() 在每次PHP请求开始,请求前启动函数被调用。通常用于管理请求前逻辑。
PHP_RSHUTDOWN_FUNCTION() 在每次PHP请求结束后,请求前关闭函数被调用。经常应用在清理请求前启动函数的逻辑。
PHP_MINFO_FUNCTION() 调用phpinfo()时模块信息函数被呼叫,从而打印出模块信息。
这些函数的代码都定义在siren.c文件中.
#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_siren.h" /* 如果php_siren.h中开启了全局变量,那就去掉注释 ZEND_DECLARE_MODULE_GLOBALS(siren) */ /* True global resources - no need for thread safety here */ static int le_siren; /* {{{ siren_functions[] * * Every user visible function must have an entry in siren_functions[]. */ const zend_function_entry siren_functions[] = { PHP_FE(confirm_siren_compiled, NULL) /* For testing, remove later. */ PHP_FE_END /* Must be the last line in siren_functions[] */ }; /* }}} */ /* {{{ siren_module_entry */ zend_module_entry siren_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "siren", siren_functions, PHP_MINIT(siren), PHP_MSHUTDOWN(siren), PHP_RINIT(siren), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(siren), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(siren), #if ZEND_MODULE_API_NO >= 20010901 "0.1", /* Replace with version number for your extension */ #endif STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_SIREN ZEND_GET_MODULE(siren) #endif /* {{{ PHP_INI */ /* Remove comments and fill if you need to have entries in php.ini PHP_INI_BEGIN() STD_PHP_INI_ENTRY("siren.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_siren_globals, siren_globals) STD_PHP_INI_ENTRY("siren.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_siren_globals, siren_globals) PHP_INI_END() */ /* }}} */ /* {{{ php_siren_init_globals */ /* Uncomment this function if you have INI entries static void php_siren_init_globals(zend_siren_globals *siren_globals) { siren_globals->global_value = 0; siren_globals->global_string = NULL; } */ /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(siren) { /* If you have INI entries, uncomment these lines REGISTER_INI_ENTRIES(); */ return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(siren) { /* uncomment this line if you have INI entries UNREGISTER_INI_ENTRIES(); */ return SUCCESS; } /* }}} */ /* Remove if there's nothing to do at request start */ /* {{{ PHP_RINIT_FUNCTION */ PHP_RINIT_FUNCTION(siren) { return SUCCESS; } /* }}} */ /* Remove if there's nothing to do at request end */ /* {{{ PHP_RSHUTDOWN_FUNCTION */ PHP_RSHUTDOWN_FUNCTION(siren) { return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(siren) { php_info_print_table_start(); php_info_print_table_header(2, "siren support", "enabled"); php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ } /* Every user-visible function in PHP should document itself in the source */ /* {{{ proto string confirm_siren_compiled(string arg) Return a string to confirm that the module is compiled in */ PHP_FUNCTION(confirm_siren_compiled) { char *arg = NULL; int arg_len, len; char *strg; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { return; } len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "siren", arg); RETURN_STRINGL(strg, len, 0); } /* }}} */
第21行 zend_function_entry是一个结构体
typedef struct _zend_function_entry { const char *fname; //函数名称 void (*handler)(INTERNAL_FUNCTION_PARAMETERS); //指向对应 C 函数的句柄 const struct _zend_arg_info *arg_info;//函数的参数信息 zend_uint num_args;//参数个数 zend_uint flags; } zend_function_entry;
const zend_function_entry siren_functions[] = { PHP_FE(confirm_siren_compiled, NULL) /* For testing, remove later. */ PHP_FE_END /* Must be the last line in siren_functions[] */ };
上面就是定义了一个函数数组
PHP_FE是一个宏.
等于
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags },
只是做了一些初始化.
PHP_FE_END 等于 { NULL, NULL, NULL, 0, 0 }
用来结束数组
zend_module_entry 是一个结构体,用来保存模块信息
struct _zend_module_entry { unsigned short size; //模块结构的大小 unsigned int zend_api; unsigned char zend_debug; //是否wie调试版本 unsigned char zts; //是否启用了 线程安全 const struct _zend_ini_entry *ini_entry; //不详 const struct _zend_module_dep *deps; //不详 const char *name; //模块名称 const struct _zend_function_entry *functions; //Zend 函数块的指针 指向zend_function_entry结构数组 int (*module_startup_func)(INIT_FUNC_ARGS); //模块启动函数 int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); //模块停止函数 int (*request_startup_func)(INIT_FUNC_ARGS); //php请求开始执行的函数 int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);//php请求结束执行的函数 void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);//模块信息函数。当脚本调用 phpinfo() 函数时,Zend 便会遍历所有已加载的模块,并调用它们的这个函数。每个模块都有机会输出自己的信息。 const char *version;//模块版本号 size_t globals_size; #ifdef ZTS ts_rsrc_id* globals_id_ptr; #else void* globals_ptr; #endif void (*globals_ctor)(void *global TSRMLS_DC); void (*globals_dtor)(void *global TSRMLS_DC); int (*post_deactivate_func)(void); int module_started; unsigned char type; void *handle; int module_number; char *build_id; };
主要字段都在代码里注释了
创建一个 zend_module_entry对象
zend_module_entry siren_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "siren", //模块名称 siren_functions, //函数列表 ,执行了上面定义的 zend_function_entry PHP_MINIT(siren), //模块开始执行的函数 PHP_MSHUTDOWN(siren),//模块结束执行的函数 PHP_RINIT(siren), //PHP开始请求执行的函数 PHP_RSHUTDOWN(siren), //PHP请求结束执行的函数 PHP_MINFO(siren), #if ZEND_MODULE_API_NO >= 20010901 "0.1", //版本 #endif STANDARD_MODULE_PROPERTIES };
STANDARD_MODULE_HEADER宏:
sizeof(zend_module_entry), ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS
用来填充 前面四个参数
第48行:
只有你的模块编译成 动态模块的时候才会被调用.这个函数的作用就是把模块的信息块传递 给Zend 并通知 Zend 获取这个模块的相关内容
54-57行:
我们在写PHP的时候,php.ini里面的配置都会影响我们PHP代码的执行,比如register_global 等.
此处代码的作用就是获取php.ini里面的配置信息.
STD_PHP_INI_ENTRY("siren.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_siren_globals, siren_globals)
STD_PHP_INI_ENTRY宏:注册php INI的指令:
接受的参数列表如下
name: php.ini里面的名称
default_value: //默认值,永远都是字符串
PHP_INI_SYSTEM. 能够在php.ini或http.conf等系统文件更改
PHP_INI_PERDIR. 能够在 .htaccess中更改
PHP_INI_USER. 能够被用户脚本更改
PHP_INI_ALL. 能够在所有地方更改
on_modify: 处理INI条目更改的回调函数。你不需自己编写处理程序,使用下面提供的函数。包括:
OnUpdateString
OnUpdateBool
OnUpdateStringUnempty
OnUpdateReal
property_name: 应当被更新的变量名
struct_type: 变量驻留的结构类型。因为通常使用全局变量机制,所以这个类型自动被定义,类似于zend_myfile_globals。
struct_ptr: 全局结构名。如果使用全局变量机制,该名为myfile_globals。
剩下的东西就是我们上面提到的一些 启动模块时执行的函数…
明白了这些,再去写模块头就不会大啦…