從第一章中對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](http://www.opengroup.org/onlinepubs/009695399/functions/printf.html)("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代碼的。它通過調用spprintf函數構造一個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](http://www.php.net/die)("Usage: embed4 scriptfile");
?
include $argv[1];
main函數:
int main(int argc, char *argv[])
{
if (argc <= 1) {
[printf](http://www.opengroup.org/onlinepubs/009695399/functions/printf.html)("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。
而這里的flush實現與Apache的不同:
static void php_embed_flush(void *server_context)
{
if (fflush(stdout)==EOF) {
php_handle_aborted_connection();
}
}
flush是直接調用fflush(stdout),以達到清空stdout的緩存的目的。如果輸出失敗(fflush成功返回0,失敗返回EOF),則調用php_handle_aborted_connection,進入中斷處理程序。
### 參與資料
《Extending and Embedding PHP》
- 第一章 準備工作和背景知識
- 第一節 環境搭建
- 第二節 源碼結構、閱讀代碼方法
- 第三節 常用代碼
- 第四節 小結
- 第二章 用戶代碼的執行
- 第一節 生命周期和Zend引擎
- 第二節 SAPI概述
- Apache模塊
- 嵌入式
- FastCGI
- 第三節 PHP腳本的執行
- 詞法分析和語法分析
- opcode
- opcode處理函數查找
- 第四節 小結
- 第三章 變量及數據類型
- 第一節 變量的結構和類型
- 哈希表(HashTable)
- PHP的哈希表實現
- 鏈表簡介
- 第二節 常量
- 第三節 預定義變量
- 第四節 靜態變量
- 第五節 類型提示的實現
- 第六節 變量的生命周期
- 變量的賦值和銷毀
- 變量的作用域
- global語句
- 第七節 數據類型轉換
- 第八節 小結
- 第四章 函數的實現
- 第一節 函數的內部結構
- 函數的內部結構
- 函數間的轉換
- 第二節 函數的定義,傳參及返回值
- 函數的定義
- 函數的參數
- 函數的返回值
- 第三節 函數的調用和執行
- 第四節 匿名函數及閉包
- 第五節 小結
- 第五章 類和面向對象
- 第一節 類的結構和實現
- 第二節 類的成員變量及方法
- 第三節 訪問控制的實現
- 第四節 類的繼承,多態及抽象類
- 第五節 魔術方法,延遲綁定及靜態成員
- 第六節 PHP保留類及特殊類
- 第七節 對象
- 第八節 命名空間
- 第九節 標準類
- 第十節 小結
- 第六章 內存管理
- 第一節 內存管理概述
- 第二節 PHP中的內存管理
- 第三節 內存使用:申請和銷毀
- 第四節 垃圾回收
- 新的垃圾回收
- 第五節 內存管理中的緩存
- 第六節 寫時復制(Copy On Write)
- 第七節 內存泄漏
- 第八節 小結
- 第七章 Zend虛擬機
- 第一節 Zend虛擬機概述
- 第二節 語法的實現
- 詞法解析
- 語法分析
- 實現自己的語法
- 第三節 中間代碼的執行
- 第四節 PHP代碼的加密解密
- 第五節 小結
- 第八章 線程安全
- 第二節 線程,進程和并發
- 第三節 PHP中的線程安全
- 第九章 錯誤和異常處理
- 第十章 輸出緩沖
- 第十六章 PHP語言特性的實現
- 第一節 循環語句
- foreach的實現
- 第二十章 怎么樣系列(how to)
- 附錄
- 附錄A PHP及Zend API
- 附錄B PHP的歷史
- 附錄C VLD擴展使用指南
- 附錄D 怎樣為PHP貢獻
- 附錄E phpt測試文件說明
- 附錄F PHP5.4新功能升級解析
- 附錄G:re2c中文手冊