## 7.4 钩子函数
PHP为扩展提供了5个钩子函数,PHP执行到不同阶段时回调各个扩展定义的钩子函数,扩展可以通过这些钩子函数介入到PHP生命周期的不同阶段中去,这些钩子函数的定义非常简单,PHP提供了对应的宏,定义完成后只需要设置`zend_module_entry`对应的函数指针即可。
前面已经介绍过PHP生命周期的几个阶段,这几个钩子函数执行的先后顺序:module startup -> request startup -> 编译、执行 -> request shutdown -> post deactivate -> module shutdown。
### 7.4.1 module_startup_func
这个函数在PHP模块初始化阶段执行,通常情况下,此过程只会在SAPI启动后执行一次。这个阶段可以进行内部类的注册,如果你的扩展提供了类就可以在此函数中完成注册;除了类还可以在此函数中注册扩展定义的常量;另外,扩展可以在此阶段覆盖PHP编译、执行的两个函数指针:zend_compile_file、zend_execute_ex,从而可以接管PHP的编译、执行,opcache的实现原理就是替换了zend_compile_file,从而使得PHP编译时调用的是opcache自己定义的编译函数,对编译后的结果进行缓存。
此钩子函数通过`PHP_MINIT_FUNCTION()`或`ZEND_MINIT_FUNCTION()`宏完成定义:
```c
PHP_MINIT_FUNCTION(extension_name)
{
...
}
```
展开后:
```c
zm_startup_extension_name(int type, int module_number)
{
...
}
```
最后通过`PHP_MINIT()`或`ZEND_MINIT()`宏将zend_module_entry的module_startup_func设置为上面定义的函数。
```c
#define PHP_MINIT ZEND_MODULE_STARTUP_N
#define ZEND_MINIT ZEND_MODULE_STARTUP_N
#define ZEND_MODULE_STARTUP_N(module) zm_startup_##module
```
### 7.4.2 request_startup_func
此函数在编译、执行之前回调,fpm模式下每一个http请求就是一个request,脚本执行前将首先执行这个函数。如果你的扩展需要针对每一个请求进行处理则可以设置这个函数,如:对请求进行filter、根据请求ip获取所在城市、对请求/返回数据加解密等。此函数通过`PHP_RINIT_FUNCTION()`或`ZEND_RINIT_FUNCTION()`宏定义:
```c
PHP_RINIT_FUNCTION(extension_name)
{
...
}
```
展开后:
```c
zm_activate_extension_name(int type, int module_number)
{
...
}
```
获取函数地址的宏:`PHP_RINIT()`或`ZEND_RINIT()`:
```c
#define PHP_RINIT ZEND_MODULE_ACTIVATE_N
#define ZEND_RINIT ZEND_MODULE_ACTIVATE_N
#define ZEND_MODULE_ACTIVATE_N(module) zm_activate_##module
```
### 7.4.3 request_shutdown_func
此函数在请求结束时被调用,通过`PHP_RSHUTDOWN_FUNCTION()`或`ZEND_RSHUTDOWN_FUNCTION()`宏定义:
```c
PHP_RSHUTDOWN_FUNCTION(extension_name)
{
...
}
```
函数地址通过`PHP_RSHUTDOWN()`或`ZEND_RSHUTDOWN()`获取:
```c
#define PHP_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N
#define ZEND_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N
#define ZEND_MODULE_DEACTIVATE_N(module) zm_deactivate_##module
```
### 7.4.4 post_deactivate_func
这个函数比较特殊,一般很少会用到,实际它也是在请求结束之后调用的,它比request_shutdown_func更晚执行:
```c
void php_request_shutdown(void *dummy)
{
...
//调用各扩展的request_shutdown_func
if (PG(modules_activated)) {
zend_deactivate_modules();
}
//关闭输出:发送http header
php_output_deactivate();
//释放超全局变量:$_GET、$_POST...
...
//关闭编译器、执行器
zend_deactivate();
//调用每个扩展的post_deactivate_func
zend_post_deactivate_modules();
...
}
```
从上面的执行顺序可以看出,request_shutdown_func、post_deactivate_func是先后执行的,此函数通过`ZEND_MODULE_POST_ZEND_DEACTIVATE_D()`宏定义,`ZEND_MODULE_POST_ZEND_DEACTIVATE_N()`获取函数地址:
```c
#define ZEND_MINIT ZEND_MODULE_STARTUP_N
#define ZEND_MODULE_POST_ZEND_DEACTIVATE_N(module) zm_post_zend_deactivate_##module
```
### 7.4.5 module_shutdown_func
模块关闭阶段回调的函数,与module_startup_func对应,此阶段主要可以进行一些资源的清理,通过`PHP_MSHUTDOWN_FUNCTION()`或`ZEND_MSHUTDOWN_FUNCTION()`定义:
```c
PHP_MSHUTDOWN_FUNCTION(extension_name)
{
...
}
```
通过`PHP_MSHUTDOWN()`或`ZEND_MSHUTDOWN()`获取函数地址:
```c
#define PHP_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N
#define ZEND_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N
#define ZEND_MODULE_SHUTDOWN_N(module) zm_shutdown_##module
```
7.4.6 小节
上面详细介绍了各个阶段定义的钩子函数的格式,使用gdb调试扩展时可以根据展开后实际的函数名称设置断点。这些钩子实际已经为扩展构造了一个整体的框架,通过这几个钩子扩展已经能实现很多功能了,后面我们介绍的很多内容都是在这几个函数中完成的,比如内部类的注册、常量注册、资源注册等。如果扩展名称为mytest,则最终定义的扩展:
```c
PHP_MINIT_FUNCTION(mytest)
{
...
}
PHP_RINIT_FUNCTION(mytest)
{
...
}
PHP_RSHUTDOWN_FUNCTION(mytest)
{
...
}
PHP_MSHUTDOWN_FUNCTION(mytest)
{
...
}
zend_module_entry mytest_module_entry = {
STANDARD_MODULE_HEADER,
"mytest",
NULL, //mytest_functions,
PHP_MINIT(mytest),
PHP_MSHUTDOWN(mytest),
PHP_RINIT(mytest),
PHP_RSHUTDOWN(mytest),
NULL, //PHP_MINFO(mytest),
"1.0.0",
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(mytest)
```
- 目录
- 第1章 PHP基本架构
- 1.1 PHP简介
- 1.2 PHP7的改进
- 1.3 FPM
- 1.4 PHP执行的几个阶段
- 第2章 变量
- 2.1 变量的内部实现
- 2.2 数组
- 2.3 静态变量
- 2.4 全局变量
- 2.5 常量
- 3.1 PHP代码的编译
- 3.1.1 词法解析、语法解析
- 3.1.2 抽象语法树编译流程
- 第3章 Zend虚拟机
- 3.2.1 内部函数
- 3.2.2 用户函数的实现
- 3.3 Zend引擎执行流程
- 3.3.1 基本结构
- 3.2 函数实现
- 3.3.2 执行流程
- 3.3.3 函数的执行流程
- 3.3.4 全局execute_data和opline
- 3.4 面向对象实现
- 3.4.1 类
- 3.4.2 对象
- 3.4.3 继承
- 3.4.4 动态属性
- 3.4.5 魔术方法
- 3.4.6 类的自动加载
- 3.5 运行时缓存
- 3.6 Opcache
- 3.6.1 opcode缓存
- 3.6.2 opcode优化
- 3.6.3 JIT
- 第4章 PHP基础语法实现
- 4.1 类型转换
- 4.2 选择结构
- 4.3 循环结构
- 4.4 中断及跳转
- 4.5 include/require
- 4.6 异常处理
- 第5章 内存管理
- 5.1 Zend内存池
- 5.2 垃圾回收
- 第6章 线程安全
- 6.2 线程安全资源管理器
- 第7章 扩展开发
- 7.1 概述
- 6.1 什么是线程安全
- 7.2 扩展的实现原理
- 7.3 扩展的构成及编译
- 7.4 钩子函数
- 7.5 运行时配置
- 7.6 函数
- 7.7 zval的操作
- 7.8 常量
- 7.9 面向对象
- 7.9.1 内部类注册
- 7.9.2 定义成员属性
- 7.9.3 定义成员方法
- 7.9.4 定义常量
- 7.9.5 类的实例化
- 7.10 资源类型
- 7.11 经典扩展解析
- 7.8.1 Yaf
- 7.8.2 Redis
- 第8章 命名空间
- 8.2 命名空间的定义
- 8.2.1 定义语法
- 8.2.2 内部实现
- 8.3 命名空间的使用
- 8.3.1 基本用法
- 8.3.2 use导入
- 8.3.3 动态用法
- 附录
- 附录1:break/continue按标签中断语法实现
- 附录2:defer推迟函数调用语法的实现
- 8.1 概述