鍍金池/ 教程/ PHP/ 回調到php中
ext_skel生成器
構建并編譯一個宿主應用
總結
小結
小結
小結
編譯前的準備
變量的存儲方式
強制模塊依賴
編譯我們的擴展
庫的查找
讓我們從SAPI開始
Unix/Linux平臺下的編譯
小結
函數的參數
zendparseparameters
PHP語言中的超級全局變量(Superglobals)
同時擴展和嵌入
線程安全
變量的值
啟動與終止的那點事
一個擴展的基本結構
資源自有的引用計數
小結
創(chuàng)建PHP變量
有趣的流
一個特殊的參數:return_value
設置宿主環(huán)境
小結
關于生命周期
變量的檢索
內存管理
狀態(tài)與屬性讀取
Persistent Resources
Autoconf
小結
PHP編譯前的config配置
函數返回值
PHP中的面向對象(一)
PHP擴展中的全局變量
嵌入式SAPI
通過嵌入包裝重新創(chuàng)建cli
小結
在Win32平臺上編譯PHP
小結
老技術新用
過濾器
MINFO與phpinfo
zendclassentry
編寫函數
PHP中的資源類型
實現(xiàn)wrapper
引用計數
小結
變量的類型
流的概覽
PHP中的面向對象(二)
擴展生成器
覆寫INI_SYSTEM和INI_PERDIR選項
捕獲輸出
小結
回調到php中
PHP的啟動與終止
定義一個接口
初始化php
配置編譯環(huán)境
PHP Streams的本質
小結
類的繼承與接口的實現(xiàn)
讀寫ini配置
定義一個類
小結
PHP的生命周期
流的上下文
引用與函數的執(zhí)行結果
類型轉換
小結
Array與HashTable
PECL_Gen
復合類型的數據——資源
小結
小結
數組(C中的)與鏈表
PHP的生命周期
讀寫對象的屬性
PHP變量在內核中的實現(xiàn)
Manipulation
高級嵌入式
操作HashTable的API
流式訪問
Arg Info 與類型綁定
小結
在內核中操作PHP語言中數組
第一個擴展
流的封裝——wrapper
配置和鏈接
流的實現(xiàn)
PHP的生命周期
生成對象的實例與調用方法
錯誤處理
INI設置
靜態(tài)編譯
小結
內存管理
Static Stream Operations
常量

回調到php中

除了加載外部的腳本, 和你在上?章看到的類似, 你的php嵌入式應用, 下面將實現(xiàn)?個類似于用戶空間eval()的命令.

?int zend_eval_string(char *str, zval *retval_ptr,
                     char *string_name TSRMLS_DC)

這里, str是實際要執(zhí)行的php腳本代碼, 而string_name是?個與執(zhí)行關聯(lián)的任意描述信息. 如果發(fā)生錯誤, php會將這個描述信息作為錯誤輸出中的"文件名". retval_ptr, 你應該 已經猜到了, 它將被設置為所傳遞代碼產生的返回值. 試試用下面的代碼創(chuàng)建新的項目吧.

#include <sapi/embed/php_embed.h>
int main(int argc, char *argv[]) {
    PHP_EMBED_START_BLOCK(argc, argv)
        zend_eval_string("echo 'Hello World!';", NULL, "Simple Hello World App" TSRMLS_CC);
    PHP_EMBED_END_BLOCK()
return 0; 
}

現(xiàn)在使用命令或第19章"設置宿主環(huán)境"構建它(將Makefile中或命令中的embed1替換為embed2)

備選方案: 腳本文件的包含

可以預見的是, 這使得編譯和執(zhí)行外部腳本文件遠比之前的方法更加容易, 因為你的 應用可以將原本復雜的打開/準備/執(zhí)行的執(zhí)行序列, 以這種簡化但功能更加強大的設計替代:

#include <sapi/embed/php_embed.h>
int main(int argc, char *argv[]) {
    char    *filename;
    if ( argc <= 1 ) {
        fprintf(stderr, "Usage: %s <filename.php> <arguments>\n", argv[1]);
        return -1;
    }
    filename    = argv[1];
/* 忽略第0個參數 */ argc --;
argv ++;
    PHP_EMBED_START_BLOCK(argc, argv)
        char    *include_script;
        spprintf(&include_script, 0, "include '%s';", filename);
        zend_eval_string(include_script, NULL, filename TSRMLS_CC);
        efree(include_script);
    PHP_EMBED_END_BLOCK()
    return 0;
}

注意: 這種特殊的方法必須接受一個缺點, 如果文件名包含單引號, 將導致解析錯誤. 不過這可以通過使用ext/standard/php_string.h中的php_addslashes()API調用解決. 花一些時間去閱讀這個 文件以及附錄中的API參考, 你會發(fā)現(xiàn)很多的特性, 它們可以讓你避免在以后重造輪子.

調用用戶空間函數

如你看到的加載和執(zhí)行腳本文件, 在內部有兩種方式調用用戶空間函數. 現(xiàn)在最明顯 的可能是重用zend_eval_string(), 將函數名和所有它的參數組織到?個龐大的字符串中, 然后收集返回值.

PHP_EMBED_START_BLOCK(argc, argv)
    char    *command;
    zval    retval;
    spprintf(&command, 0, "nl2br('%s');", argv[1]);
    zend_eval_string(command, &retval, "nl2br() execution" TSRMLS_CC);
    efree(command);
    printf("out: %s\n", Z_STRVAL(retval));
    zval_dtor(&retval);
PHP_EMBED_END_BLOCK()

和前面的include很像, 這個方法有?個致命的缺陷: 如果輸入參數paramin(譯者給出 的例子中是argv[1])給出?個錯誤的數據, 函數將會失敗, 或者更糟糕的是導致無法預期的 結果. 解決方案是永遠都避免編譯代碼的運行時片段, 并直接使用call_user_function()API調用函數.

int call_user_function(HashTable *function_table, zval **object_pp,
                       zval *function_name, zval *retval_ptr,
                       zend_uint param_count, zval *params[] TSRMLS_DC);

實際上從引擎外部調用時, function_table總是EG(function_table). 如果調用?個對象或類方法, object_pp需要是IS_OBJECT類型的調用實例zval, 或者對于類的靜態(tài)調用則是 IS_STRING的值. function_name通常是IS_STRING的值, 包含要調用的函數名, 但是它也 可以是IS_ARRAY, 第0個元素包含一個對象或類名, 第1個元素包含方法名.

這個函數調用的結果是向傳入的retval_ptr指向的zval設置返回值. param_count和 params扮演了argc/argv的角色. 也就是說, params[0]包含所傳遞的第一個參數, params[param_count - 1]包含了所傳遞的最后一個參數.

下面是用這種方法重新實現(xiàn)上面的例子:

PHP_EMBED_START_BLOCK(argc, argv)
    char    *command;
    zval    retval;
    spprintf(&command, 0, "nl2br('%s');", argv[1]);
    zend_eval_string(command, &retval, "nl2br() execution" TSRMLS_CC);
    efree(command);
    printf("out: %s\n", Z_STRVAL(retval));
    zval_dtor(&retval);
PHP_EMBED_END_BLOCK()
?int call_user_function(HashTable *function_table, zval **object_pp,
                       zval *function_name, zval *retval_ptr,
                       zend_uint param_count, zval *params[] TSRMLS_DC);
PHP_EMBED_START_BLOCK(argc, argv)
    zval    *args[1];
    zval    retval, str, funcname;
    ZVAL_STRING(&funcname, "nl2br", 0);
    args[0] = &str;
    ZVAL_STRINGL(args[0], "HELLO WORLD!", sizeof("HELLO WORLD!"), 1);
    call_user_function(EG(function_table), NULL, &funcname, &retval, 1, args TSRMLS_CC);
    printf("out: %s\n", Z_STRVAL(retval));
    zval_dtor(args[0]);
    zval_dtor(&retval);
PHP_EMBED_END_BLOCK()

盡管代碼看起來比較長, 但是工作量會顯著降低, 因為這里沒有要編譯的中間代碼, 傳 遞的數據不需要復制, 每個參數都已經在Zend兼容的結構體中. 同時, 要記得原來的例子中 在字符串中包含單引號時會有潛在的錯誤. 而這個版本沒有這個問題.

上一篇:PECL_Gen下一篇:定義一個接口