### 7.3.1 擴展的構成
擴展首先需要創建一個`zend_module_entry`結構,這個變量必須是全局變量,且變量名必須是:`擴展名稱_module_entry`,內核通過這個結構得到這個擴展都提供了哪些功能,換句話說,一個擴展可以只包含一個`zend_module_entry`結構,相當于定義了一個什么功能都沒有的擴展。
```c
//zend_modules.h
struct _zend_module_entry {
unsigned short size; //sizeof(zend_module_entry)
unsigned int zend_api; //ZEND_MODULE_API_NO
unsigned char zend_debug; //是否開啟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); //擴展初始化回調函數,PHP_MINIT_FUNCTION或ZEND_MINIT_FUNCTION定義的函數
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); //php_info展示的擴展信息處理函數
const char *version; //版本
...
unsigned char type;
void *handle;
int module_number; //擴展的唯一編號
const char *build_id;
};
```
這個結構包含很多成員,但并不是所有的都需要自己定義,經常用到的主要有下面幾個:
* __name:__ 擴展名稱,不能重復
* __functions:__ 擴展定義的內部函數entry
* __module_startup_func:__ PHP在模塊初始化時回調的hook函數,可以使擴展介入module startup階段
* __module_shutdown_func:__ 在模塊關閉階段回調的函數
* __request_startup_func:__ 在請求初始化階段回調的函數
* __request_shutdown_func:__ 在請求結束階段回調的函數
* __info_func:__ php_info()函數時調用,用于展示一些配置、運行信息
* __version:__ 擴展版本
除了上面這些需要手動設置的成員,其它部分可以通過`STANDARD_MODULE_HEADER`、`STANDARD_MODULE_PROPERTIES`宏統一設置,擴展提供的內部函數及四個執行階段的鉤子函數是擴展最常用到的部分,幾乎所有的擴展都是基于這兩部分實現的。有了這個結構還需要提供一個接口來獲取這個結構變量,這個接口是統一的,擴展中通過`ZEND_GET_MODULE(extension_name)`完成這個接口的定義:
```
//zend_API.h
#define ZEND_GET_MODULE(name) \
BEGIN_EXTERN_C()\
ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; }\
END_EXTERN_C()
```
展開后可以看到,實際就是定義了一個get_module()函數,返回擴展zend_module_entry結構的地址,這就是為什么這個結構的變量名必須是`擴展名稱_module_entry`這種格式的原因。
有了擴展的zend_module_entry結構以及獲取這個結構的接口一個合格的擴展就編寫完成了,只是這個擴展目前還什么都干不了:
```c
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
zend_module_entry mytest_module_entry = {
STANDARD_MODULE_HEADER,
"mytest",
NULL, //mytest_functions,
NULL, //PHP_MINIT(mytest),
NULL, //PHP_MSHUTDOWN(mytest),
NULL, //PHP_RINIT(mytest),
NULL, //PHP_RSHUTDOWN(mytest),
NULL, //PHP_MINFO(mytest),
"1.0.0",
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(mytest)
```
編譯、安裝后執行`php -m`就可以看到my_test這個擴展了。
- 前言
- 第1章 PHP基本架構
- 1.1 PHP簡介
- 1.2 PHP7的改進
- 1.3 FPM
- 1.3.1 概述
- 1.3.2 基本實現
- 1.3.3 FPM的初始化
- 1.3.4 請求處理
- 1.3.5 進程管理
- 1.4 PHP執行的幾個階段
- 第2章 變量
- 2.1 變量的內部實現
- 2.2 數組
- 2.3 靜態變量
- 2.4 全局變量
- 2.5 常量
- 第3章 Zend虛擬機
- 3.1 PHP代碼的編譯
- 3.1.1 詞法解析、語法解析
- 3.1.2 抽象語法樹編譯流程
- 3.2 函數實現
- 3.2.1 內部函數
- 3.2.2 用戶函數的實現
- 3.3 Zend引擎執行流程
- 3.3.1 基本結構
- 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.1 什么是線程安全
- 6.2 線程安全資源管理器
- 第7章 擴展開發
- 7.1 概述
- 7.2 擴展的實現原理
- 7.3 擴展的構成及編譯
- 7.3.1 擴展的構成
- 7.3.2 編譯工具
- 7.3.3 編寫擴展的基本步驟
- 7.3.4 config.m4
- 7.4 鉤子函數
- 7.5 運行時配置
- 7.5.1 全局變量
- 7.5.2 ini配置
- 7.6 函數
- 7.6.1 內部函數注冊
- 7.6.2 函數參數解析
- 7.6.3 引用傳參
- 7.6.4 函數返回值
- 7.6.5 函數調用
- 7.7 zval的操作
- 7.7.1 新生成各類型zval
- 7.7.2 獲取zval的值及類型
- 7.7.3 類型轉換
- 7.7.4 引用計數
- 7.7.5 字符串操作
- 7.7.6 數組操作
- 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.1 概述
- 8.2 命名空間的定義
- 8.2.1 定義語法
- 8.2.2 內部實現
- 8.3 命名空間的使用
- 8.3.1 基本用法
- 8.3.2 use導入
- 8.3.3 動態用法
- 附錄
- break/continue按標簽中斷語法實現
- defer推遲函數調用語法的實現
- 一起線上事故引發的對PHP超時控制的思考