# 老技術新用
在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()代碼塊替換為下面的代碼:
````c
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, 并用下面的測試腳本進行測試:
````c
<?php
var_dump($type);
?>
````
當然, 這個簡單的概念可以很容易的擴展為填充這個類型信息到$_SERVER超級全局變量數組中.
````c
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()命令打開或關閉:
````c
?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.
````c
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 超級全局變量并給它設置一個初始值來進行測試:
````c
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.md>)
* 19.5 [小結](<19.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. 小結
- 約定