### 7.5.1 全局變量(資源)
使用C語言開發程序時經常會使用全局變量進行數據存儲,這就涉及前面已經介紹過的一個問題:線程安全,PHP設計了TSRM(即:線程安全資源管理器)用于解決這個問題,內核中頻繁使用到的EG、CG等都是根據是否開啟ZTS封裝的宏,同樣的,在擴展中也需要必須按照TSRM的規范定義全局變量,除非你的擴展不支持多線程的環境。
PHP為擴展的全局變量提供了一種存儲方式:每個擴展將自己所有的全局變量統一定義在一個結構體中,然后將這個結構體注冊到TSRM中,這樣擴展就可以像使用EG、CG那樣訪問這個結構體。
這個結構體的定義通過`ZEND_BEGIN_MODULE_GLOBALS(extension_name)`、`ZEND_END_MODULE_GLOBALS(extension_name)`兩個宏完成,這兩個宏必須成對出現,中間定義擴展需要的全局變量即可。
```c
ZEND_BEGIN_MODULE_GLOBALS(mytest)
zend_long open_cache;
HashTable class_table;
ZEND_END_MODULE_GLOBALS(mytest)
```
展開后實際就是個普通的struct:
```c
typedef struct _zend_mytest_globals {
zend_long open_cache;
HashTable class_table;
}zend_mytest_globals;
```
接著創建一個此結構體的全局變量,這時候就會涉及ZTS了,如果未開啟線程安全直接創建普通的全局變量即可,如果開啟線程安全了則需要向TSRM注冊,得到一個唯一的資源id,這個操作也由專門的宏來完成:`ZEND_DECLARE_MODULE_GLOBALS(extension_name)`,展開后:
```c
//ZTS:此時只是定義資源id,并沒有向TSRM注冊
ts_rsrc_id mytest_globals_id;
//非ZTS
zend_mytest_globals mytest_globals;
```
最后需要定義一個像EG、CG那樣的宏用于訪問擴展的全局資源結構體,這一步將使用`ZEND_MODULE_GLOBALS_ACCESSOR()`宏完成:
```c
#define MYTEST_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(mytest, v)
```
看起來是不是跟EG、CG的定義非常像?這個宏展開后:
```c
//ZTS
#define MYTEST_G(v) ZEND_TSRMG(mytest_globals_id, zend_mytest_globals *, v)
//非ZTS
#define MYTEST_G(v) (mytest_globals.v)
```
接下來就可以在擴展中通過:MYTEST_G(opene_cache)、MYTEST_G(class_table)對結構體成員進行讀寫了。通常會把這個全局資源結構體及結構體的訪問宏定義在頭文件中,然后把全局變量的聲明放到源文件中:
```c
//php_mytest.h
#define MYTEST_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(mytest, v)
ZEND_BEGIN_MODULE_GLOBALS(mytest)
zend_long open_cache;
HashTable class_table;
ZEND_END_MODULE_GLOBALS(mytest)
//mytest.c
ZEND_DECLARE_MODULE_GLOBALS(mytest)
```
> 在一個擴展中并不是只能定義一個全局變量結構,數目是不限制的。
- 前言
- 第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超時控制的思考