生命周期
说到PHP扩展,需要先简单了解下PHP的生命周期,意义在于说明这个扩展在什么时候是生效的。
在CLI模式下,PHP的执行过程主要分为5大部分,分别是模块初始化(php_module_startup)、请求初始化(php_request_startup)、执行(php_execute_script)、请求关闭(php_request_shutdown)和模块关闭(php_module_shutdown)。

模块初始化阶段
这个阶段主要是针对PHP框架以及Zend进行初始化,这个只会在启动时运行一次,PHP会加载每个模块的MINIT,并为其注册相关变量以及分配空间。
其中php_startup_auto_globals()会自动注册全局变量_GET、_POST、_COOKIE等,php_register_internal_extensions()、php_register_extensions_bc()、php_ini_register_extensions()会注册内部扩展、启动附加的PHP扩展以及加载所有的外部扩展。
请求初始化阶段
重置输出全局并设置输出处理程序的堆栈,读取解析请求,并初始化相关全局变量。
执行阶段
执行阶段的入口函数是php_execute_script,该函数会调用zend_execute_scripts,先是对PHP代码进行词法和语法分析,生成AST,进而生成op_array,最后关联handler并执行op_array。
请求关闭阶段
本阶段主要调用各模块的关闭函数和析构函数,输出缓冲区内容,调用钩子函数RSHUTDOWN,销毁request全局变量,还原ini配置。
模块关闭阶段
这个阶段主要是进行资源的清理、php各模块的关闭操作,同时,将回调各扩展的module shutdown钩子函数。这是发生在所有请求都已经结束之后,例如关闭fpm的操作。(这个是对于CGI和CLI等SAPI,没有“下一个请求”,所以SAPI立刻开始关闭。)
扩展
在了解各个生命周期之后,扩展的生存周期也就明了了,PHP扩展的本质就是动态链接库的加载调用。
PHP扩展需要实现一个zend_module_entry结构体。
struct _zend_module_entry {
unsigned short size;
unsigned int zend_api;
unsigned char zend_debug;
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;
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);
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);
void (*globals_dtor)(void *global);
int (*post_deactivate_func)(void);
int module_started;
unsigned char type;
void *handle;
int module_number;
const char *build_id;
};
其中name表示扩展名称,不能与其他扩展重名,functions字段表示注册的函数,
module_startup_func、module_shutdown_func、request_startup_func、request_shutdown_func这几个函数非常关键,分别对应各个阶段的回调钩子函数。
PHP是通过get_module函数获取扩展名称,扩展名称必须为***_module_entry,例如json的扩展名为json_module_entry。
之后再PHP_MINIT阶段通过php_ini_register_extensions加载扩展并进行注册。
/* {{{ php_ini_register_extensions
*/
void php_ini_register_extensions(void)
{
zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb);
zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb);
zend_llist_destroy(&extension_lists.engine);
zend_llist_destroy(&extension_lists.functions);
}
简单示例
下面写一个简单的示例,ext_skel是一款快速编写扩展的工具。
cd /root/php-7.2.29/ext
# 生成扩展phpext
./ext_skel --extname=phpext
cd phpext
# 把PHP_ARG_ENABLE(phpext, whether to enable phpext support,之前的注释取消掉
vi config.m4
后面就开始编写phpext.c文件,实现功能,本示例中就以输出hello world为例。
首先看一下phpext_module_entry结构体
/* {{{ phpext_module_entry
*/
zend_module_entry phpext_module_entry = {
STANDARD_MODULE_HEADER,
"phpext",
phpext_functions,
PHP_MINIT(phpext),
PHP_MSHUTDOWN(phpext),
PHP_RINIT(phpext), /* Replace with NULL if there's nothing to do at request start */
PHP_RSHUTDOWN(phpext), /* Replace with NULL if there's nothing to do at request end */
PHP_MINFO(phpext),
PHP_PHPEXT_VERSION,
STANDARD_MODULE_PROPERTIES
};
其中的module名称为phpext,注册函数为phpext_functions,以及钩子回调函数等信息。
我们后面开始写函数,首先在phpext_functions中增加我们要注册的函数phpext。
const zend_function_entry phpext_functions[] = {
PHP_FE(confirm_phpext_compiled, NULL) /* For testing, remove later. */
PHP_FE(phpext,NULL)
PHP_FE_END /* Must be the last line in phpext_functions[] */
};
之后编写phpext函数
PHP_FUNCTION(phpext){
printf("hello world!");
return;
}
后面就可以使用phpize编译生成扩展so了。
phpize
./configure
make && make install
然后修改php.ini文件,增加extension=phpext.so即可完成扩展的注册,下面是扩展使用示例。
[root@VM_0_4_centos phpext]# php -r "phpext();"
hello world!
645

被折叠的 条评论
为什么被折叠?



