# 19.4 老技術新用
# 老技術新用
在PHP\_EMBED\_START\_BLOCK()被調用后, 你的應用處于?個php請求周期的開始 位置, 相當于RINIT回調函數完成以后. 此刻你就可以和前面一樣執行 php\_execute\_script()命令, 或者其他任意合法的, 可以在PHP\_FUNCTION()或RINIT()塊中出現的php/Zend API指令.
#### 設置初始變量
第2章"變量的里里外外"中介紹了操縱符號表的概念, 第5至18章則介紹了怎樣通過用 戶空間腳本調用內部函數使用這些技術. 到這里這些處理也并沒有發生變化, 雖然這里并 沒有激活的用戶空間腳本, 但是你的包裝應用仍然可以操縱符號表. 將你的 PHP\_EMBED\_START\_BLOCK()/PHP\_EMBED\_END\_BLOCK()代碼塊替換為下面的代碼:
```
PHP_EMBED_START_BLOCK(argc, argv)
zval *type;
ALLOC_INIT_ZVAL(type);
ZVAL_STRING(type, "Embedded", 1);
ZEND_SET_SYMBOL(&EG(symbol_table), "type", type);
php_execute_script(&script TSRMLS_CC);
PHP_EMBED_END_BLOCK()
```
現在使用make重新構建embed1, 并用下面的測試腳本進行測試:
```
<?php
var_dump($type);
?>
```
當然, 這個簡單的概念可以很容易的擴展為填充這個類型信息到$\_SERVER超級全局變量數組中.
```
PHP_EMBED_START_BLOCK(argc, argv)
zval **SERVER_PP, *type;
/* 注冊$_SERVER超級全局變量 */
zend_is_auto_global_quick("_SERVER", sizeof("_SERVER") - 1, 0 TSRMLS_CC);
/* 查找$_SERVER超級全局變量 */
zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&SERVER_PP) ;
/* $_SERVER['SAPI_TYPE'] = "Embedded"; */
ALLOC_INIT_ZVAL(type);
ZVAL_STRING(type, "Embedded", 1);
ZEND_SET_SYMBOL(Z_ARRVAL_PP(SERVER_PP), "SAPI_TYPE", type);
php_execute_script(&script TSRMLS_CC);
PHP_EMBED_END_BLOCK()
譯注: 譯者的環境中代碼運行到zend_hash_find()處$_SERVER尚未注冊, 經過跟蹤, 發現它 是直到編譯用戶空間代碼的時候, 發現用戶空間使用了$_SERVER變量才進行的注冊. 因此, 上面 的代碼中增加了zend_is_auto_global_quick()的調用, 通過這個調用將完成對$_SERVER的注冊.
```
#### 覆寫INI選項
在第13章"INI設置"中, 有?部分是講INI修改處理器的, 在那里看到的是INI階段的處 理. PHP\_EMBED\_START\_BLOCK()宏則將這些代碼放到了運行時階段. 也就是說這個時 候修改某些設置(比如register\_globals/magic\_quotes\_gpc)已經有點遲了.
不過在內部訪問也沒有什么不好. 所謂的"管理設置"比如safe\_mode在這個略遲的階 段可以使用下面的zend\_alter\_ini\_entry()命令打開或關閉:
```
?int zend_alter_ini_entry(char *name, uint name_length,
char *new_value, uint new_value_length,
int modify_type, int stage);
```
name, new\_value以及它們對應的長度參數的含義正如你所預期的: 修改名為name的 INI設置的值為new\_value. 要注意name\_length包含了末尾的NULL字節, 然而 new\_value\_length則不包含; 然而, 無論如何, 兩個字符串都必須是NULL終止的.
modify\_type則提供簡化的訪問控制檢查. 回顧每個INI設置都有一個modifiable屬性, 它是PHP\_INI\_SYSTEM, PHP\_INI\_PERDIR, PHP\_INI\_USER等常量的組合值. 當使用 zend\_alter\_ini\_entry()修改INI設置時, modify\_type參數必須包含至少?個INI設置的 modifiable屬性值.
用戶空間的ini\_set()函數通過傳遞PHP\_INI\_USER利用了這個特性, 也就是說只有 modifiable屬性包含PHP\_INI\_USER標記的INI設置才能使用這個函數修改. 當在你的嵌入 式應用中使用這個API調用時, 你可以通過傳遞PHP\_INI\_ALL標記短路這個訪問控制系統, 它將包含所有的INI訪問級別.
stage必須對應于Zend Engine的當前狀態; 對于這些簡單的嵌入式示例, 總是 PHP\_INI\_STAGE\_RUNTIME. 如果這是一個擴展或更高端的嵌入式應用, 你可能就需要將 這個值設置為PHP\_INI\_STAGE\_STARTUP或PHP\_INI\_STAGE\_ACTIVE.
下面是擴展embed1.c源文件, 讓它在執行腳本文件之前強制開啟safe\_mode.
```
PHP_EMBED_START_BLOCK(argc, argv)
zval **SERVER_PP, *type;
/* 不論php.ini中如何設置都強制開啟safe_mode */
zend_alter_ini_entry("safe_mode", sizeof("safe_mode"), "1", sizeof("1") - 1, PHP_INI_ALL,
PHP_INI_STAGE_RUNTIME);
/* 注冊$_SERVER超級全局變量 */
zend_is_auto_global_quick("_SERVER", sizeof("_SERVER") - 1, 0 TSRMLS_CC);
/* 查找$_SERVER超級全局變量 */
zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&SERVER_PP) ;
/* $_SERVER['SAPI_TYPE'] = "Embedded"; */
ALLOC_INIT_ZVAL(type);
ZVAL_STRING(type, "Embedded", 1);
ZEND_SET_SYMBOL(Z_ARRVAL_PP(SERVER_PP), "SAPI_TYPE", type);
php_execute_script(&script TSRMLS_CC);
PHP_EMBED_END_BLOCK()
```
#### 定義附加的超級全局變量
在第12章"啟動, 終止, 以及其中的一些點"中, 你知道了用戶空間全局變量以及超級全 局變量可以在啟動(MINIT)階段定義. 同樣, 本章介紹的嵌入式直接跳過了啟動階段, 處于 運行時狀態. 和覆寫INI一樣, 這并不會顯得太遲.
超級全局變量的定義實際上只需要在腳本編譯之前定義即可, 并且在php的進程生命 周期中它只應該出現?次. 在擴展中的正常情況下, MINIT是唯一可以保證這些條件的地方.
由于你的包裝應用現在是在控制中的, 因此可以保證定義用戶空間自動全局變量的這 些點位于真正編譯腳本源文件的php\_execute\_script()命令之前. 我們定義?個$\_EMBED 超級全局變量并給它設置一個初始值來進行測試:
```
HP_EMBED_START_BLOCK(argc, argv)
zval **SERVER_PP, *type, *EMBED, *foo;
/* 在全局作用域創建$_EMBED數組 */ ALLOC_INIT_ZVAL(EMBED);
array_init(EMBED); ZEND_SET_SYMBOL(&EG(symbol_table), "_EMBED", EMBED);
/* $_EMBED['foo'] = 'Bar'; */
ALLOC_INIT_ZVAL(foo);
ZVAL_STRING(foo, "Bar", 1);
add_assoc_zval_ex(EMBED, "foo", sizeof("foo"), foo);
/* 注冊超級全局變量$_EMBED */
zend_register_auto_global("_EMBED", sizeof("_EMBED")
#ifdef ZEND_ENGINE_2
#else #endif
, 1, NULL TSRMLS_CC);
, 1 TSRMLS_CC);
/* 不論php.ini中如何設置都強制開啟safe_mode */
zend_alter_ini_entry("safe_mode", sizeof("safe_mode"), "1", sizeof("1") - 1, PHP_INI_ALL,
PHP_INI_STAGE_RUNTIME);
/* 注冊$_SERVER超級全局變量 */
zend_is_auto_global_quick("_SERVER", sizeof("_SERVER") - 1, 0 TSRMLS_CC);
/* 查找$_SERVER超級全局變量 */
zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&SERVER_PP) ;
/* $_SERVER['SAPI_TYPE'] = "Embedded"; */
ALLOC_INIT_ZVAL(type);
ZVAL_STRING(type, "Embedded", 1);
ZEND_SET_SYMBOL(Z_ARRVAL_PP(SERVER_PP), "SAPI_TYPE", type);
php_execute_script(&script TSRMLS_CC);
PHP_EMBED_END_BLOCK()
```
要記住, Zend Engine 2(php 5.0或更高)使用了不同的zend\_register\_auto\_global()元嬰, 因此你需要用前面講php 4兼容時候講過的#ifdef. 如果你不關心舊版本php的兼容性, 則可以丟棄這些指令讓代碼變得更加整潔.
## links
- [目錄](preface.md)
- 19.3 [通過嵌入包裝重新創建cli](19.3.html)
- 19.5 [小結](19.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 小結