PHP內核探索:嵌入式PHP

從PHP源碼目錄結構的介紹以及PHP生命週期可知:嵌入式PHP類似CLI,也是SAPI接口的另一種實現。 一般情況下,它的一個請求的生命週期也會和其它的SAPI一樣:模塊初始化=>請求初始化=>處理請求=>關閉請求=>關閉模塊。 當然,這只是理想情況。因爲特定的應用由自己特殊的需求,只是在處理PHP腳本這個環節基本一致。

對於嵌入式PHP或許我們瞭解比較少,或者說根本用不到,甚至在網上相關的資料也不多, 例如很多遊戲中使用Lua語言作爲粘合語言,或者作爲擴展遊戲的腳本語言,類似的, 瀏覽器中的Javascript語言就是嵌入在瀏覽器中的。只是目前很少有應用將PHP作爲嵌入語言來使用, PHP的強項目前還是在Web開發方面。

PHP對於嵌入式PHP的支持以及PHP爲嵌入式提供了哪些接口或功能呢?首先我們看下所要用到的示例源碼:

#include <sapi/embed/php_embed.h>
#ifdef ZTS
    void ***tsrm_ls;
#endif
/* Extension bits */
zend_module_entry php_mymod_module_entry = {
    STANDARD_MODULE_HEADER,
    "mymod", /* extension name */
    NULL, /* function entries */
    NULL, /* MINIT */
    NULL, /* MSHUTDOWN */
    NULL, /* RINIT */
    NULL, /* RSHUTDOWN */
    NULL, /* MINFO */
    "1.0", /* version */
    STANDARD_MODULE_PROPERTIES
};
/* Embedded bits */
static void startup_php(void)
{
    int argc = 1;
    char *argv[2] = { "embed5", NULL };
    php_embed_init(argc, argv PTSRMLS_CC);
    zend_startup_module(&php_mymod_module_entry);
}
static void execute_php(char *filename)
{
    zend_first_try {
        char *include_script;
        spprintf(&include_script, 0, "include '%s'", filename);
        zend_eval_string(include_script, NULL, filename TSRMLS_CC);
        efree(include_script);
    } zend_end_try();
}
int main(int argc, char *argv[])
{
    if (argc <= 1) {
        printf("Usage: embed4 scriptfile";);
        return -1;
    }
    startup_php();
    execute_php(argv[1]);
    php_embed_shutdown(TSRMLS_CC);
    return 0;
}

以上的代碼可以在《Extending and Embedding PHP》在第20章找到(原始代碼有一個符號錯誤,有興趣的童鞋可以去圍觀下)。 上面的代碼是一個嵌入式PHP運行器(我們權當其爲運行器吧),在這個運行器上我們可以運行PHP代碼。 這段代碼包括了對於PHP嵌入式支持的聲明,啓動嵌入式PHP運行環境,運行PHP代碼,關閉嵌入式PHP運行環境。 下面我們就這段代碼分析PHP對於嵌入式的支持做了哪些工作。 首先看下第一行:

#include <sapi/embed/php_embed.h>

在sapi目錄下的embed目錄是PHP對於嵌入式的抽象層所在。在這裏有我們所要用到的函數或宏定義。 如示例中所使用的php_embed_init,php_embed_shutdown等函數。

第2到4行:

#ifdef ZTS
    void ***tsrm_ls;
#endif

ZTS是Zend Thread Safety的簡寫,與這個相關的有一個TSRM(線程安全資源管理)的東東,這個後面的章節會有詳細介紹,這裏就不再作闡述。

第6到17行:

zend_module_entry php_mymod_module_entry = {
    STANDARD_MODULE_HEADER,
    "mymod", /* extension name */
    NULL, /* function entries */
    NULL, /* MINIT */
    NULL, /* MSHUTDOWN */
    NULL, /* RINIT */
    NULL, /* RSHUTDOWN */
    NULL, /* MINFO */
    "1.0", /* version */
    STANDARD_MODULE_PROPERTIES
};

以上PHP內部的模塊結構聲明,此處對於模塊初始化,請求初始化等函數指針均爲NULL, 也就是模塊在初始化及請求開始結束等事件發生的時候不執行任何操作。 不過這些操作在sapi/embed/php_embed.c文件中的php_embed_shutdown等函數中有體現。 關於模塊結構的定義在zend/zend_modules.h中。

startup_php函數:

static void startup_php(void)
{
    int argc = 1;
    char *argv[2] = { "embed5", NULL };
    php_embed_init(argc, argv PTSRMLS_CC);
    zend_startup_module(&php_mymod_module_entry);
}

這個函數調用了兩個函數php_embed_init和zend_startup_module完成初始化工作。 php_embed_init函數定義在sapi/embed/php_embed.c文件中。它完成了PHP對於嵌入式的初始化支持。 zend_startup_module函數是PHP的內部API函數,它的作用是註冊定義的模塊,這裏是註冊mymod模塊。 這個註冊過程僅僅是將所定義的zend_module_entry結構添加到註冊模塊列表中。

execute_php函數:

static void execute_php(char *filename)
{
    zend_first_try {
        char *include_script;
        spprintf(&include_script, 0, "include '%s'", filename);
        zend_eval_string(include_script, NULL, filename TSRMLS_CC);
        efree(include_script);
    } zend_end_try();
}

從函數的名稱來看,這個函數的功能是執行PHP代碼的。 它通過調用sprrintf函數構造一個include語句,然後再調用zend_eval_string函數執行這個include語句。 zend_eval_string最終是調用zend_eval_stringl函數,這個函數是流程是一個編譯PHP代碼, 生成zend_op_array類型數據,並執行opcode的過程。 這段程序相當於下面的這段php程序,這段程序可以用php命令來執行,雖然下面這段程序沒有實際意義, 而通過嵌入式PHP中,你可以在一個用C實現的系統中嵌入PHP,然後用PHP來實現功能。

<?php
if($argc < 2) die("Usage: embed4 scriptfile");
 
include $argv[1];
?>

main函數:

int main(int argc, char *argv[])
{
    if (argc <= 1) {
        printf("Usage: embed4 scriptfile";);
        return -1;
    }
    startup_php();
    execute_php(argv[1]);
    php_embed_shutdown(TSRMLS_CC);
    return 0;
}

這個函數是主函數,執行初始化操作,根據輸入的參數執行PHP的include語句,最後執行關閉操作,返回。 其中php_embed_shutdown函數定義在sapi/embed/php_embed.c文件中。它完成了PHP對於嵌入式的關閉操作支持。 包括請求關閉操作,模塊關閉操作等。

以上是使用PHP的嵌入式方式開發的一個簡單的PHP代碼運行器,它的這些調用的方式都基於PHP本身的一些實現, 而針對嵌入式的SAPI定義是非常簡單的,沒有Apache和CGI模式的複雜,或者說是相當簡陋,這也是由其所在環境決定。 在嵌入式的環境下,很多的網絡協議所需要的方法都不再需要。如下所示,爲嵌入式的模塊定義。

sapi_module_struct php_embed_module = {
    "embed",                       /* name */
    "PHP Embedded Library",        /* pretty name */
 
    php_embed_startup,              /* startup */
    php_module_shutdown_wrapper,   /* shutdown */
 
    NULL,                          /* activate */
    php_embed_deactivate,           /* deactivate */
 
    php_embed_ub_write,             /* unbuffered write */
    php_embed_flush,                /* flush */
    NULL,                          /* get uid */
    NULL,                          /* getenv */
 
    php_error,                     /* error handler */
 
    NULL,                          /* header handler */
    NULL,                          /* send headers handler */
    php_embed_send_header,          /* send header handler */
 
    NULL,                          /* read POST data */
    php_embed_read_cookies,         /* read Cookies */
 
    php_embed_register_variables,   /* register server variables */
    php_embed_log_message,          /* Log message */
    NULL,                           /* Get request time */
    NULL,                           /* Child terminate */
 
    STANDARD_SAPI_MODULE_PROPERTIES
};
/* }}} */

在這個定義中我們看到了若干的NULl定義,在前面一小節中說到SAPI時,我們是以cookie的讀取爲例, 在這裏也有讀取cookie的實現——php_embed_read_cookies函數,但是這個函數的實現是一個空指針NULL。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章