從PHP源碼目錄結構的介紹以及PHP生命週期可知:嵌入式PHP相似CLI,也是SAPI接口的另外一種實現。 通常狀況下,它的一個請求的生命週期也會和其它的SAPI同樣:模塊初始化=>請求初始化=>處理請求=>關閉請求=>關閉模 塊。 固然,這只是理想狀況。由於特定的應用由本身特殊的需求,只是在處理PHP腳本這個環節基本一致。php
對於嵌入式PHP或許咱們瞭解比較少,或者說根本用不到,甚至在網上相關的資料也很少, 例如不少遊戲中使用Lua語言做爲粘合語言,或者做爲擴展遊戲的腳本語言,相似的, 瀏覽器中的Javascript語言就是嵌入在瀏覽器中的。只是目前不多有應用將PHP做爲嵌入語言來使用, PHP的強項目前仍是在Web開發方面。api
PHP對於嵌入式PHP的支持以及PHP爲嵌入式提供了哪些接口或功能呢?首先咱們看下所要用到的示例源碼:瀏覽器
01 |
#include <sapi/embed/php_embed.h> |
02 |
#ifdef ZTS |
03 |
void ***tsrm_ls; |
04 |
#endif |
05 |
/* Extension bits */ |
06 |
zend_module_entry php_mymod_module_entry = { |
07 |
STANDARD_MODULE_HEADER, |
08 |
"mymod" , /* extension name */ |
09 |
NULL, /* function entries */ |
10 |
NULL, /* MINIT */ |
11 |
NULL, /* MSHUTDOWN */ |
12 |
NULL, /* RINIT */ |
13 |
NULL, /* RSHUTDOWN */ |
14 |
NULL, /* MINFO */ |
15 |
"1.0" , /* version */ |
16 |
STANDARD_MODULE_PROPERTIES |
17 |
}; |
18 |
/* Embedded bits */ |
19 |
static void startup_php( void ) |
20 |
{ |
21 |
int argc = 1; |
22 |
char *argv[2] = { "embed5" , NULL }; |
23 |
php_embed_init(argc, argv PTSRMLS_CC); |
24 |
zend_startup_module(&php_mymod_module_entry); |
25 |
} |
26 |
static void execute_php( char *filename) |
27 |
{ |
28 |
zend_first_try { |
29 |
char *include_script; |
30 |
spprintf(&include_script, 0, "include '%s'" , filename); |
31 |
zend_eval_string(include_script, NULL, filename TSRMLS_CC); |
32 |
efree(include_script); |
33 |
} zend_end_try(); |
34 |
} |
35 |
int main( int argc, char *argv[]) |
36 |
{ |
37 |
if (argc <= 1) { |
38 |
printf ( "Usage: embed4 scriptfile" ;); |
39 |
return -1; |
40 |
} |
41 |
startup_php(); |
42 |
execute_php(argv[1]); |
43 |
php_embed_shutdown(TSRMLS_CC); |
44 |
return 0; |
45 |
} |
以上的代碼能夠在《Extending and Embedding PHP》在第20章找到(原始代碼有一個符號錯誤,有興趣的童鞋能夠去圍觀下)。 上面的代碼是一個嵌入式PHP運行器(咱們權當其爲運行器吧),在這個運行器上咱們能夠運行PHP代碼。 這段代碼包括了對於PHP嵌入式支持的聲明,啓動嵌入式PHP運行環境,運行PHP代碼,關閉嵌入式PHP運行環境。 下面咱們就這段代碼分析PHP對於嵌入式的支持作了哪些工做。 首先看下第一行:安全
1 |
#include <sapi/embed/php_embed.h> |
在sapi目錄下的embed目錄是PHP對於嵌入式的抽象層所在。在這裏有咱們所要用到的函數或宏定義。 如示例中所使用的php_embed_init,php_embed_shutdown等函數。cookie
第2到4行:網絡
1 |
#ifdef ZTS |
2 |
void ***tsrm_ls; |
3 |
#endif |
ZTS是Zend Thread Safety的簡寫,與這個相關的有一個TSRM(線程安全資源管理)的東東,這個後面的章節會有詳細介紹,這裏就再也不做闡述。app
第6到17行:函數
01 |
zend_module_entry php_mymod_module_entry = { |
02 |
STANDARD_MODULE_HEADER, |
03 |
"mymod" , /* extension name */ |
04 |
NULL, /* function entries */ |
05 |
NULL, /* MINIT */ |
06 |
NULL, /* MSHUTDOWN */ |
07 |
NULL, /* RINIT */ |
08 |
NULL, /* RSHUTDOWN */ |
09 |
NULL, /* MINFO */ |
10 |
"1.0" , /* version */ |
11 |
STANDARD_MODULE_PROPERTIES |
12 |
}; |
以上PHP內部的模塊結構聲明,此處對於模塊初始化,請求初始化等函數指針均爲NULL, 也就是模塊在初始化及請求開始結束等事件發生的時候不執行任何操做。 不過這些操做在sapi/embed/php_embed.c文件中的php_embed_shutdown等函數中有體現。 關於模塊結構的定義在zend/zend_modules.h中。ui
startup_php函數:spa
1 |
static void startup_php( void ) |
2 |
{ |
3 |
int argc = 1; |
4 |
char *argv[2] = { "embed5" , NULL }; |
5 |
php_embed_init(argc, argv PTSRMLS_CC); |
6 |
zend_startup_module(&php_mymod_module_entry); |
7 |
} |
這個函數調用了兩個函數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函數:
1 |
static void execute_php( char *filename) |
2 |
{ |
3 |
zend_first_try { |
4 |
char *include_script; |
5 |
spprintf(&include_script, 0, "include '%s'" , filename); |
6 |
zend_eval_string(include_script, NULL, filename TSRMLS_CC); |
7 |
efree(include_script); |
8 |
} zend_end_try(); |
9 |
} |
從函數的名稱來看,這個函數的功能是執行PHP代碼的。 它經過調用sprrintf函數構造一個include語句,而後再調用zend_eval_string函數執行這個include語句。 zend_eval_string最終是調用zend_eval_stringl函數,這個函數是流程是一個編譯PHP代碼, 生成zend_op_array類型數據,並執行opcode的過程。 這段程序至關於下面的這段php程序,這段程序能夠用php命令來執行,雖然下面這段程序沒有實際意義, 而經過嵌入式PHP中,你能夠在一個用C實現的系統中嵌入PHP,而後用PHP來實現功能。
1 |
<?php |
2 |
if ( $argc < 2) die ( "Usage: embed4 scriptfile" ); |
3 |
|
4 |
include $argv [1]; |
5 |
?> |
main函數:
01 |
int main( int argc, char *argv[]) |
02 |
{ |
03 |
if (argc <= 1) { |
04 |
printf ( "Usage: embed4 scriptfile" ;); |
05 |
return -1; |
06 |
} |
07 |
startup_php(); |
08 |
execute_php(argv[1]); |
09 |
php_embed_shutdown(TSRMLS_CC); |
10 |
return 0; |
11 |
} |
這個函數是主函數,執行初始化操做,根據輸入的參數執行PHP的include語句,最後執行關閉操做,返回。 其中php_embed_shutdown函數定義在sapi/embed/php_embed.c文件中。它完成了PHP對於嵌入式的關閉操做支持。 包括請求關閉操做,模塊關閉操做等。
以上是使用PHP的嵌入式方式開發的一個簡單的PHP代碼運行器,它的這些調用的方式都基於PHP自己的一些實現, 而針對嵌入式的SAPI定義是很是簡單的,沒有Apache和CGI模式的複雜,或者說是至關簡陋,這也是由其所在環境決定。 在嵌入式的環境下,不少的網絡協議所須要的方法都再也不須要。以下所示,爲嵌入式的模塊定義。
01 |
sapi_module_struct php_embed_module = { |
02 |
"embed" , /* name */ |
03 |
"PHP Embedded Library" , /* pretty name */ |
04 |
|
05 |
php_embed_startup, /* startup */ |
06 |
php_module_shutdown_wrapper, /* shutdown */ |
07 |
|
08 |
NULL, /* activate */ |
09 |
php_embed_deactivate, /* deactivate */ |
10 |
|
11 |
php_embed_ub_write, /* unbuffered write */ |
12 |
php_embed_flush, /* flush */ |
13 |
NULL, /* get uid */ |
14 |
NULL, /* getenv */ |
15 |
|
16 |
php_error, /* error handler */ |
17 |
|
18 |
NULL, /* header handler */ |
19 |
NULL, /* send headers handler */ |
20 |
php_embed_send_header, /* send header handler */ |
21 |
|
22 |
NULL, /* read POST data */ |
23 |
php_embed_read_cookies, /* read Cookies */ |
24 |
|
25 |
php_embed_register_variables, /* register server variables */ |
26 |
php_embed_log_message, /* Log message */ |
27 |
NULL, /* Get request time */ |
28 |
NULL, /* Child terminate */ |
29 |
|
30 |
STANDARD_SAPI_MODULE_PROPERTIES |
31 |
}; |
32 |
/* }}} */ |
在這個定義中咱們看到了若干的NULl定義,在前面一小節中說到SAPI時,咱們是以cookie的讀取爲例, 在這裏也有讀取cookie的實現——php_embed_read_cookies函數,可是這個函數的實現是一個空指針NULL。