# 12.4 PHP擴展中的全局變量
這一章,我們將學會如何在PHP擴展中使用全局變量。
在擴展中定義全局變量
首先,我們需要在擴展的頭文件中(默認是php_*.h)中定義所有的全局變量。舉個例子,比如我們要定義一個無符號的long類型的全局變量,我們可以這樣定義:
````c
ZEND_BEGIN_MODULE_GLOBALS(sample4)
unsigned long counter;
ZEND_END_MODULE_GLOBALS(sample4)
````
用ZEND_BEGIN_MODULE_GLOBALS和ZEND_END_MODULE_GLOBALS宏將定義的全局變量包起來。將上例中的宏展開后,是下面這個樣子:
````c
typedef struct _zend_sample4_globals {
unsigned long counter;
} zend_sample4_globals;
````
如果你還有其他的全局變量需要定義,只需加在兩個宏之間就可以了。接下來我們該在simple4.c中聲明我們在頭文件中定義的這些全局變量了:
````c
ZEND_DECLARE_MODULE_GLOBALS(sample4);
````
這個宏的內部實現取決于是否啟用了線程安全,在非線程安全的環境下,如:Apache1,Apache2-prefork, CGI,CLI...會使用zend_sample4_globals結構來定義
全局變量:
````c
zend_sample4_globals sample4_globals;
````
我們可以直接通過sample4_globals.counter來獲取計數器的值。在線程安全的版本中,另一種方法是聲明一個整數:
````c
int sample4_globals_id;
````
填充這個ID就等于定義了擴展中的全局變量。根據其定義的信息,將為每個新線程的獨立存儲空間分配內存塊。我們可以在MINIT中這樣定義:
````c
#ifdef ZTS
ts_allocate_id(
&sample4_globals_id,
sizeof(zend_sample4_globals),
NULL, NULL);
#endif
````
有一點需要注意這種方法需要包裹在#ifdef中,以防止它在沒有啟動Zend Thread Safety(ZTS)時執行。因為sample4_globals_id只能在多線程環境中使用。非線程
的版本用我們在前面提到的sample4_globals來聲明全局變量。
線程中的初始化和關閉
在非線程的環境中,會將一個zend_sample4_globals結構的副本保存在指定進程中。你可以指定他的默認值,或者在MINIT或者RINIT中分配資源來初始化它。要記得
在對應的MSHUTDOWN或者RSHUTDOWN中及時釋放這些資源。
然而在線程版本中,一個新的結構會在一個新線程spun的時候被分配。為了知道怎樣初始化和關閉擴展中的全局變量,需要向ZE引擎提供回調函數。在前面我們在調用
ts_allocate_id()的時候是是以NULL來作為這個值的,接下來我們添加2個一會需要在MINIT調用的方法:
````c
static void php_sample4_globals_ctor(zend_sample4_globals *sample4_globals TSRMLS_DC)
{
/* Initialize a new zend_sample4_globals struct
* During thread spin-up */
sample4_globals->counter = 0;
}
static void php_sample4_globals_dtor(zend_sample4_globals *sample4_globals TSRMLS_DC)
{
/* Any resources allocated during initialization
* May be freed here */
}
````
我們在啟用和關閉的時候調用它們:
````c
PHP_MINIT_FUNCTION(sample4) {
REGISTER_STRING_CONSTANT("SAMPLE4_VERSION", PHP_SAMPLE4_EXTVER, CONST_CS | CONST_PERSISTENT);
#ifdef ZTS
ts_allocate_id(&sample4_globals_id,
sizeof(zend_sample4_globals),
(ts_allocate_ctor)php_sample4_globals_ctor,
(ts_allocate_dtor)php_sample4_globals_dtor);
#else
php_sample4_globals_ctor(&sample4_globals TSRMLS_CC);
#endif
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(sample4) {
#ifndef ZTS
php_sample4_globals_dtor(&sample4_globals TSRMLS_CC);
#endif
return SUCCESS;
}
````
現在我們已經知道如何在擴展中創建全局變量了,在不是ZTS的環境中,使用它們很簡單,我們還來看前面定義的那個計數器的遞增功能如何實現:
````c
PHP_FUNCTION(sample4_counter) {
RETURN_LONG(++sample4_globals.counter);
}
````
是不是看起來很簡單,但是,在線程版本中將無法正常工作。那么我們來看看怎么在線程環境中完成這個功能吧:
````c
PHP_FUNCTION(sample4_counter)
{
#ifdef ZTS
RETURN_LONG(++TSRMG(sample4_globals_id, \
zend_sample4_globals*, counter));
#else
/* non-ZTS */
RETURN_LONG(++sample4_globals.counter);
#endif
}
````
看起來很丑對嗎?想象一下,在你的整個代碼庫中,這些IFDEF指令在每一個線程安全的全局訪問時穿插。它會看起來比Perl更糟糕!這就是為什么所有
的核心擴展,都有使用一個額外的宏觀層抽象這種情況。我們可以在php_sample4.h中找到下面這段代碼:
````c
#ifdef ZTS
#include "TSRM.h"
#define SAMPLE4_G(v) TSRMG(sample4_globals_id, zend_sample4_globals*, v)
#else
#define SAMPLE4_G(v) (sample4_globals.v)
#endif
````
使用它們會讓你的方法看起來更簡潔:
````c
PHP_FUNCTION(sample4_counter) {
RETURN_LONG(++SAMPLE4_G(counter));
}
````
看到*G()這樣的宏是不是有種似曾相識的感覺?也許以前你看到過EG()、CG()等宏,了解他們會讓你對PHP的了解更深一步:
Accessor Macro | Associated Data
------------- | -------------
EG() | 這個宏可以用來訪問符號表,函數,資源信息和常量。
CG() | 用來訪問核心全局變量。
PG() | PHP全局變量。我們知道php.ini會映射一個或者多個PHP全局結構。舉幾個使用這個宏的例子:PG(register_globals), PG(safe_mode), PG(memory_limit)
FG() | 文件全局變量。大多數文件I/O或相關的全局變量的數據流都塞進標準擴展出口結構。
## links
* [目錄](<preface.md>)
* 12.3 [常量](<12.3.md>)
* 12.5 [PHP語言中的超級全局變量](<12.5.md>)
- about
- 開始閱讀
- 目錄
- 1 PHP的生命周期
- 1.讓我們從SAPI開始
- 2.PHP的啟動與終止
- 3.PHP的生命周期
- 4.線程安全
- 5.小結
- 2 PHP變量在內核中的實現
- 1. 變量的類型
- 2. 變量的值
- 3. 創建PHP變量
- 4. 變量的存儲方式
- 5. 變量的檢索
- 6. 類型轉換
- 7. 小結
- 3 內存管理
- 1. 內存管理
- 2. 引用計數
- 3. 總結
- 4 動手編譯PHP
- 1. 編譯前的準備
- 2. PHP編譯前的config配置
- 3. Unix/Linux平臺下的編譯
- 4. 在Win32平臺上編譯PHP
- 5. 小結
- 5 Your First Extension
- 1. 一個擴展的基本結構
- 2. 編譯我們的擴展
- 3. 靜態編譯
- 4. 編寫函數
- 5. 小結
- 6 函數返回值
- 1. 一個特殊的參數:return_value
- 2. 引用與函數的執行結果
- 3. 小結
- 7 函數的參數
- 1. zend_parse_parameters
- 2. Arg Info 與類型綁定
- 3. 小結
- 8 使用HashTable與{數組}
- 1. 數組(C中的)與鏈表
- 2. 操作HashTable的API
- 3. 在內核中操作PHP語言中數組
- 4. 小結
- 9 PHP中的資源類型
- 1. 復合類型的數據——{資源}
- 2. Persistent Resources
- 3. {資源}自有的引用計數
- 4. 小結
- 10 PHP中的面向對象(一)
- 1. zend_class_entry
- 2. 定義一個類
- 3. 定義一個接口
- 4. 類的繼承與接口的實現
- 5. 小結
- 11 PHP中的面向對象(二)
- 1. 生成對象的實例與調用方法
- 2. 讀寫對象的屬性
- 3. 小結
- 12 啟動與終止的那點事
- 2. 小結
- 1. 關于生命周期
- 2. MINFO與phpinfo
- 3. 常量
- 4. PHP擴展中的全局變量
- 5. PHP語言中的超級全局變量
- 6. 小結
- 13 INI設置
- 1. 聲明和訪問ini設置
- 2. 小結
- 2. 小結
- 14 流式訪問
- 1. 概覽
- 2. 打開流
- 3. 訪問流
- 4. 靜態資源操作
- 5. 小結
- 15 流的實現
- 1. php流的表象之下
- 2. 包裝器操作
- 3. 實現一個包裝器
- 4. 操縱
- 5. 檢查
- 6. 小結
- 16 有趣的流
- 1. 上下文
- 2. 過濾器
- 3. 小結
- 17 配置和鏈接
- 1. autoconf
- 2. 庫的查找
- 3. 強制模塊依賴
- 4. Windows方言
- 5. 小結
- 18 擴展生成
- 1. ext_skel
- 2. PECL_Gen
- 3. 小結
- 19 設置宿主環境
- 1. 嵌入式SAPI
- 2. 構建并編譯一個宿主應用
- 3. 通過嵌入包裝重新創建cli
- 4. 老技術新用
- 5. 小結
- 20 高級嵌入式
- 1. 回調到php中
- 2. 錯誤處理
- 3. 初始化php
- 4. 覆寫INI_SYSTEM和INI_PERDIR選項
- 5. 捕獲輸出
- 6. 同時擴展和嵌入
- 7. 小結
- 約定