PHP 扩展编写(一)

生命周期

说到PHP扩展,需要先简单了解下PHP的生命周期,意义在于说明这个扩展在什么时候是生效的。

在CLI模式下,PHP的执行过程主要分为5大部分,分别是模块初始化(php_module_startup)、请求初始化(php_request_startup)、执行(php_execute_script)、请求关闭(php_request_shutdown)和模块关闭(php_module_shutdown)。

图2.1 单进程SAPI生命周期

模块初始化阶段

这个阶段主要是针对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!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值