## 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 概述