設置宿主環境
現在你已經了解了PHPAPI的世界, 並可以使用zval以及語言內部擴展機制執行很多工作了, 是時候轉移目標用它做它最擅長的事情了: 解釋腳本代碼.
嵌入式SAPI
回顧介紹中, php構建了一個層級系統. 最高層是提供用戶空間函數和類庫的所有擴展. 同時, 其下是服務API(SAPI)層, 它扮演了webserver(比如apache, iis以及命令行接口cli)的接口.
在這許多sapi實現中有一個特殊的sapi就是嵌入式sapi. 當這個sapi實現被構建時, 將會創建一個包含所有你已知的php和zend api函數以及變量的庫對象, 這個庫對象還包含一些額外的幫助函數和宏, 用以簡化外部程序的調用.
生成嵌入式api的庫和頭文件和其他sapi的編譯所執行的動作相同. 只需要傳遞--enable-embed到./configure命令中即可. 和以前一樣, 使用--enable-debug對於錯誤報告和跟蹤很有幫助.
你可能還需要打開--enable-maintainer-zts, 當然, 理由你已經耳熟能詳了, 它將幫助你注意到代碼的錯誤, 不過, 這裡還有其他原因. 假設某個時刻, 你有多個應用使用php嵌入庫執行腳本任務; 其中一個應用是簡單的短生命周期的, 它並沒有使用線程, 因此為了效率你可能想要關閉ZTS.
現在假設第二個應用使用了線程, 比如webserver, 每個線程需要跟蹤自己的請求上下文. 如果ZTS被關閉, 則只有第一個應用可以使用這個庫; 然而, 如果打開ZTS, 則兩個應用都可以在自己的進程空間使用同一個共享對象.
當然, 你也可以同時構建兩個版本, 並給它們不同的名字, 但是這相比於在不需要ZTS時包括ZTS帶來的很小的效率影響更多的問題.
默認情況下, 嵌入式庫將構建為libphp5.so共享對象, 或者在windows下的動態鏈接庫, 不過, 它也可能使用可選的static關鍵字(--enable-embed=static)被構建為靜態庫.
構建為靜態庫的版本避免了ZTS/非ZTS的問題, 以及潛在的可能在一個系統中有多個php版本的情況. 風險在於這就意味著你的結果應用二進制將顯著變大, 它將承載整個ZendEngine和PHP框架, 因此, 選擇的時候就需要慎重的考慮你是否需要的是一個相對更小的庫.
無論你選擇那種構建方式, 一旦你執行make install, libphp5都將被拷貝到你的./configure指定的PREFIX目錄下的lib/目錄中. 此外還會在PREFIX/include/php/sapi/embed目錄下放入名為php_embed.h的頭文件, 以及你在使用php嵌入式庫編譯程序時需要的其他幾個重要的頭文件.
構建並編譯一個宿主應用
究其本質而言, 庫只是一個沒有目的的代碼集合. 為了讓它工作, 你需要用以嵌入php的應用. 首先, 我們來封裝一個非常簡單的應用, 它啟動Zend引擎並初始化PHP處理一個請求, 接著就回頭進行資源的清理.
#include <sapi/embed/php_embed.h> int main(int argc, char *argv[]) { PHP_EMBED_START_BLOCK(argc,argv) PHP_EMBED_END_BLOCK() return 0; }
由於這涉及到了很多頭文件, 構建實際上需要的時間要長於這麼小的代碼片段通常需要的時間. 如果你使用了不同於默認路徑(/usr/local)的PREFIX, 請確認以下面的方式指定路徑:
gcc -I /usr/local/php-dev/include/php/ \ -I /usr/local/php-dev/include/php/main/ \ -I /usr/local/php-dev/include/php/Zend/ \ -I /usr/local/php-dev/include/php/TSRM/ \ -lphp5 \ -o embed1 embed1.c
由於這個命令每次輸入都很麻煩, 你可能更原意用一個簡單的Makefile替代:
CC = gcc CFLAGS = -c -I /usr/local/php-dev/include/php/ \ -I /usr/local/php-dev/include/php/main/ \ -I /usr/local/php-dev/include/php/Zend/ \ -I /usr/local/php-dev/include/php/TSRM/ \ -Wall -g \ LDFLAGS = -lphp5 all: embed1.c $(CC) -o embed1.o embed1.c $(CFLAGS) $(CC) -o embed1 embed1.o $(LDFLAGS)
這個Makefile和前面提供的命令有一些重要的區別. 首先, 它用-Wall開關打開了編譯期的警告, 並且用-g打開了調試信息. 此外它將編譯和鏈接兩個階段分為了兩個獨立的階段, 這樣在後期增加更多源文件的時候就相對容易. 請自己重新組著這個Makefile, 不過這裡用於對齊的是Tab(水平制表符)而不是空格.
現在, 你對embed1.c源文件做修改後, 只需要執行一個make命令就可以構建出新的embed1可執行程序了.