# 12.4 PHP擴展中的全局變量
# 12.4 PHP擴展中的全局變量
這一章,我們將學會如何在PHP擴展中使用全局變量。
在擴展中定義全局變量
首先,我們需要在擴展的頭文件中(默認是php\_\*.h)中定義所有的全局變量。舉個例子,比如我們要定義一個無符號的long類型的全局變量,我們可以這樣定義:
```
ZEND_BEGIN_MODULE_GLOBALS(sample4)
unsigned long counter;
ZEND_END_MODULE_GLOBALS(sample4)
```
用ZEND\_BEGIN\_MODULE\_GLOBALS和ZEND\_END\_MODULE\_GLOBALS宏將定義的全局變量包起來。將上例中的宏展開后,是下面這個樣子:
```
typedef struct _zend_sample4_globals {
unsigned long counter;
} zend_sample4_globals;
```
如果你還有其他的全局變量需要定義,只需加在兩個宏之間就可以了。接下來我們該在simple4.c中聲明我們在頭文件中定義的這些全局變量了:
```
ZEND_DECLARE_MODULE_GLOBALS(sample4);
```
這個宏的內部實現取決于是否啟用了線程安全,在非線程安全的環境下,如:Apache1,Apache2-prefork, CGI,CLI...會使用zend\_sample4\_globals結構來定義 全局變量:
```
zend_sample4_globals sample4_globals;
```
我們可以直接通過sample4\_globals.counter來獲取計數器的值。在線程安全的版本中,另一種方法是聲明一個整數:
```
int sample4_globals_id;
```
填充這個ID就等于定義了擴展中的全局變量。根據其定義的信息,將為每個新線程的獨立存儲空間分配內存塊。我們可以在MINIT中這樣定義:
```
#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調用的方法:
```
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 */
}
```
我們在啟用和關閉的時候調用它們:
```
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的環境中,使用它們很簡單,我們還來看前面定義的那個計數器的遞增功能如何實現:
```
PHP_FUNCTION(sample4_counter) {
RETURN_LONG(++sample4_globals.counter);
}
```
是不是看起來很簡單,但是,在線程版本中將無法正常工作。那么我們來看看怎么在線程環境中完成這個功能吧:
```
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中找到下面這段代碼:
```
#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
```
使用它們會讓你的方法看起來更簡潔:
```
PHP_FUNCTION(sample4_counter) {
RETURN_LONG(++SAMPLE4_G(counter));
}
```
看到\*G()這樣的宏是不是有種似曾相識的感覺?也許以前你看到過EG()、CG()等宏,了解他們會讓你對PHP的了解更深一步:
Accessor MacroAssociated DataEG()這個宏可以用來訪問符號表,函數,資源信息和常量。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.html)
- 12.5 [PHP語言中的超級全局變量](12.5.html)
- 介紹
- 1 PHP的生命周期
- 1.1 讓我們從SAPI開始
- 1.2 PHP的啟動與終止
- 1.3 PHP的生命周期
- 1.4 線程安全
- 1.5 PHP的生命周期
- 2 PHP變量在內核中的實現
- 2.1 變量的類型
- 2.2 變量的值
- 2.3 創建PHP變量
- 2.4 變量的存儲方式
- 2.5 變量的檢索
- 2.6 類型轉換
- 2.7 小結
- 3 內存管理
- 3.1 內存管理
- 3.2 引用計數
- 3.3 內存管理
- 4 動手編譯PHP
- 4.1 動手編譯PHP
- 4.2 動手編譯PHP
- 4.3 Unix/Linux平臺下的編譯
- 4.4 在Win32平臺上編譯PHP
- 4.5 動手編譯PHP
- 5 Your First Extension
- 5.1 Your First Extension
- 5.2 編譯我們的擴展
- 5.3 靜態編譯
- 5.4 編寫函數
- 5.5 Your First Extension
- 6 函數返回值
- 6.1 函數返回值
- 6.2 引用與函數的執行結果
- 6.3 函數返回值
- 7 函數的參數
- 7.1 函數的參數
- 7.2 函數的參數
- 7.3 函數的參數
- 8 使用HashTable與{數組}
- 8.1 使用HashTable與{數組}
- 8.2 使用HashTable與{數組}
- 8.3 使用HashTable與{數組}
- 8.4 使用HashTable與{數組}
- 9 PHP中的資源類型
- 9.1 PHP中的資源類型
- 9.2 PHP中的資源類型
- 9.3 PHP中的資源類型
- 9.4 PHP中的資源類型
- 10 PHP中的面向對象(一)
- 10.1 PHP中的面向對象(一)
- 10.2 PHP中的面向對象(一)
- 10.3 PHP中的面向對象(一)
- 10.4 PHP中的面向對象(一)
- 10.5 PHP中的面向對象(一)
- 11 PHP中的面向對象(二)
- 11.1 PHP中的面向對象(二)
- 11.2 PHP中的面向對象(二)
- 11.3 PHP中的面向對象(二)
- 12 啟動與終止的那點事
- 12.1 關于生命周期
- 12.2 MINFO與phpinfo
- 12.3 常量
- 12.4 PHP擴展中的全局變量
- 12.5 PHP語言中的超級全局變量(Superglobals)
- 12.6 小結
- 13 INI設置
- 13.1 聲明和訪問INI設置
- 13.2 小結
- 14 流式訪問
- 14.1 流的概覽
- 14.2 訪問流
- 14.3 靜態資源操作
- 14.4 links
- 15 流的實現
- 15.1 php流的表象之下
- 15.2 包裝器操作
- 15.3 實現一個包裝器
- 15.4 操縱
- 15.5 檢查
- 15.6 小結
- 16 有趣的流
- 16.1 上下文
- 16.2 過濾器
- 16.3 小結
- 17 配置和鏈接
- 17.1 autoconf
- 17.2 庫的查找
- 17.3 強制模塊依賴
- 17.4 Windows方言
- 17.5 小結
- 18 擴展生成
- 18.1 ext_skel
- 18.2 PECL_Gen
- 18.3 小結
- 19 設置宿主環境
- 19.1 嵌入式SAPI
- 19.2 構建并編譯一個宿主應用
- 19.3 通過嵌入包裝重新創建cli
- 19.4 老技術新用
- 19.5 小結
- 20 高級嵌入式
- 20.1 回調到php中
- 20.2 錯誤處理
- 20.3 初始化php
- 20.4 覆寫INI_SYSTEM和INI_PERDIR選項
- 20.5 捕獲輸出
- 20.6 同時擴展和嵌入
- 20.7 小結