參考文獻:https://blog.csdn.net/zhuocr/article/details/60328967 、《PHP7內核剖析》秦鵬/著
SAPI:Server Application Programming Interface 服務器端應用編程端口。它就是PHP與其它應用交互的接口,PHP腳本要執行有很多種方式,通過Web服務器,或者直接在命令行下,也可以嵌入在其他程序中。
SAPI提供了一個和外部通信的接口,常見的SAPI有:cgi 、fast-cgi、cli、isapi、apache 模塊的 DLL
1、常見的SAPI介紹
CGI
CGI即通用網關接口(Common Gateway Interface),一段把網頁和web服務器中的執行程序連接起來的程序。它把HTML接收的指令傳遞給服務器執行程序,再把服務器執行程序的結果返還給HTML頁。CGI的跨平臺性也極強,幾乎能在任何操作系統上運行。
CGI在遇到用戶連接請求時要先創建CGI的子進程,然後處理請求,處理完後結束這個子進程。這就是fork-and-execute模式。不過有多少連接請求就有多少CGI子進程,會造成資源的擁擠。
fast-cgi
CGI的升級版本,常住型的CGI,前面介紹過。PHP的Fpm就是FastCGI Process Manager,全稱爲PHP FastCGI進程管理器。FastCGI的工作原理:
(1)Web Server啓動時載入FastCGI進程管理器(IIS ISAPI或Apache Module)
(2)FastCGI進程管理器自身初始化,啓動多個CGI解釋器進程(可見多個php-cgi)並等待來自Web Server的連接。
(3)當客戶端請求到達Web Server時,FastCGI進程管理器選擇並連接到一個CGI解釋器。Web server將CGI環境變量和標準輸入發送到FastCGI子進程php-cgi。
(4)FastCGI子進程完成處理後將標準輸出和錯誤信息從同一連接返回Web Server。當FastCGI子進程關閉連接時,請求便告處理完成。FastCGI子進程接着等待並處理來自FastCGI進程管理器(運行在Web Server中)的下一個連接。 在CGI模式中,php-cgi在此便退出了。
在上述情況中,你可以想象CGI通常有多慢。每一個Web 請求PHP都必須重新解析php.ini、重新載入全部擴展並重初始化全部數據結構。使用FastCGI,所有這些都只在進程啓動時發生一次。一個額外的 好處是,持續數據庫連接(Persistent database connection)可以工作。
APACHE2HANDLER
PHP作爲Apache模塊,Apache服務器在系統啓動後,預先生成多個進程副本駐留在內存中,一旦有請求出現,就立即使用這些空餘的子進程進行處理,這樣就不存在生成子進程造成的延遲了。這些服務器副本在處理完一次HTTP請求之後並不立即退出,而是停留在計 算機中等待下次請求。對於客戶瀏覽器的請求反應更快,性能較高。
apache模塊的DLL:
該運行模式是我們以前在windows環境下使用apache服務器經常使用的,而在模塊化(DLL)中,PHP是與Web服務器一起啓動並運行的。(是apache在CGI的基礎上進行的一種擴展,加快PHP的運行效率)
ISAPI:
ISAPI即Internet Server Application Program Interface,是微軟提供的一套面向Internet服務的API接口.一個ISAPI的DLL,可以在被用戶請求激活後長駐內存,等待用戶的另一個請求,還可以在一個DLL裏設置多個用戶請求處理函數,此外,ISAPI的DLL應用程序和WWW服務器處於同一個進程中,效率要顯著高於CGI。
cli:
cli是php的命令行運行模式,大家經常會使用它,但是可能並沒有注意到(例如:我們在linux下經常使用 “php -m”查找PHP安裝了那些擴展就是PHP命令行運行模式;
在windows下使用cli模式從命令行進入www目錄,然後運行。
Embed
如果我們自己的第三方程序想使用PHP,那麼我們就需要這類SAPI,它在編譯後就是普通的庫文件(可以選擇編譯爲靜態庫、共享庫),我們可以在其它C/C++應用中調用PHP提供的API,甚至可以提供給其它語言處理。
2、Embed
編譯PHP時通過 --enable-embed=[shared|static]指定庫類型,默認是共享庫。編譯之後可以在PHP安裝位置的/lib目錄下看到生成的庫文件,同時在/include/php/sapi目錄下會生成一個存放Embed頭文件的目錄。
Embed的實現邏輯非常簡單,只是把PHP生命週期的幾個處理函數進行了封裝,它對外提供了兩個API。
2.1 php_embed_init()
這個接口主要進行PHP框架的初始化操作,比如TSRM、初始化SAPI、初始化信號處理,另外它還完成了非常重要的兩個操作,那就是php_module_startup()、php_request_startup()。
EMBED_SAPI_API int php_embed_init(int argc, char **argv)
{
zend_llist global_vars;
#ifdef ZTS
//初始化SAPI
tsrm_startup(1, 1, 0, NULL);
(void)ts_resource(0);
ZEND_TSRMLS_CACHE_UPDATE();
#endif
zend_signal_startup();
//初始化SAPI
sapi_startup(&php_embed_module);
...
//進入module startup階段
if (php_embed_module.startup(&php_embed_module)==FAILURE) {
return FAILURE;
}
...
//請求request startup階段
if (php_request_startup()==FAILURE) {
php_module_shutdown();
return FAILURE;
}
...
}
在第三方應用中嵌入PHP時首先需要調用這個接口,然後就可以使用PHP/Zend提供的API完成PHP腳本的執行了。
2.2 php_embed_shutdown()
此接口與php_embed_init()對應,主要完成PHP框架的關閉收尾工作,包括request shutdown、module shutdown兩個階段的操作。
EMBED_SAPI_API void php_embed_shutdown(void)
{
//關閉請求,request shutdown階段
php_request_shutdown((void *) 0);
//關閉模塊
php_module_shutdown();
sapi_shutdown();
#ifdef ZTS
tsrm_shutdown();
TSRMLS_CACHE_RESET();
#endif
if (php_embed_module.ini_entries) {
free(php_embed_module.ini_entries);
php_embed_module.ini_entries = NULL;
}
}