1. PHP原始碼結構
PHP的核心子系統有兩個,ZE(Zend Engine)和PHP Core.
ZE負責將PHP腳本解析成機器碼(也成為token符)後,在進程空間執行這些機器碼;ZE也負責記憶體管理,變數作用域管理和對PHP函數的調度管理。
PHP Core負責和SAPI層的通訊;PHP Core也為safe_mode, open_basedir的檢查提供了統一控制層;PHP Core還提供了streams層,用於用戶域的檔案和網路IO操作。其中SAPI(Server Application Programming Interface)通常包含Nginx,Apache,IIS,CLI,CGI等主機環境。
PHP擴充在ZE和PHP Core的基礎上提供對各種常用操作的封裝,例如對mysql,redis,memcache,sqlite等的讀寫,對json,xml檔的解析,對soap,sokcet,curl的網路協定的封裝,對加密解密壓縮解壓縮等的封裝,對影像處理的封裝等等。有些擴充是從零開始實現某個功能,例如按照redis的通訊協定使用C來實現和redis的通訊;有些擴充則是透過呼叫系統已有的函式庫,例如圖片處理的gb擴充需要係統本身要安裝了對應的gd庫。
在PHP源碼php-5.6.24/ext中提供了78個擴充。
總之,由ZE和PHP Core提供基礎的架構,由EXT(擴展)提供用戶域的各種操作。
以php-5.6.24原始碼為例,ZE對應資料夾php-5.6.24/Zend, PHP Core對應資料夾php-5.6.24/main, 擴充對應資料夾php-5.6.24/ext。
2. PHP擴充的生命週期
PHP在接收到SAPI指令時,首先初始化並啟動它的核心子系統,在內核子系統的啟動快結束時,PHP開始載入它的擴充程式碼並對擴充初始化,此時PHP將呼叫每個模組的初始化例程Module Initialization routine (MINIT)。
MINIT(Module Initialization)
PHP呼叫MINIT相關例程,使得每個擴充功能有機會初始化內部變數、分配資源、註冊資源處理句柄,以及向ZE註冊自己的函數,以便於腳本呼叫這其中的函數時候ZE知道執行哪些程式碼
RINIT(Request Initialization)
在模組初始化完成後,PHP等待來自SAPI的請求,當接收到SAPI請求後,由ZE為目前被要求的php腳本建立運行環境,並呼叫每個擴展的Request Initialization(RINIT)函數,使得每個擴充都有機會設定特定的環境變量,根據請求分配資源,或執行其他任務,如審核。
這裡所說的SAPI請求分為兩類,一類是Apache, IIS, 和其他成熟的web server SAPIs,他們在啟動時PHP先執行了MINIT,之後等待來自用戶的頁面請求,當收到請求後來執行RINIT;另一類SAPI請求則是CGI or CLI SAPIs,PHP收到這類SAPI要求時,執行完MINIT馬上就執行RINIT。
當RINIT請求初始化完畢後,ZE接回控制權並將目前被要求的腳本翻譯成tokens, 最終構成opcodes(操作碼),opcodes被執行過程中,如果某個opcode要求執行某個擴充函數,這是ZE就會將相關參數綁定到改函數,並將控制權暫時交給函數去執行,直到函數執行完畢。
RSHUTDOWN(Request Shutdown)
PHP腳本運行結束後,PHP調用每個擴充功能的請求關閉(RSHUTDOWN)函數以執行最後的清理工作(如將session變數存入磁碟)。接下來,ZE執行清理過程(垃圾收集),有效地對先前的請求期間所用到的每個變數執行unset()。
MSHUTDOWN(Module Shutdown)
當RSHUTDOWN完成後,PHP繼續等待SAPI的其他文件請求或關閉訊號。對於CGI和CLI等SAPI,沒有“下一個請求”,所以SAPI立刻開始關閉。關閉期間,PHP再次遍歷每個擴展,呼叫其模組關閉(MSHUTDOWN)函數,並最終關閉自己的核心子系統。
GINIT
初始化全域變數
GSHUTDOWN
釋放全域變數
MINFO
設定phpinfo模組的訊息,phpinfo要等級每個擴充的設定資訊
// main/php.h #define PHP_MINIT ZEND_MODULE_STARTUP_N #define PHP_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N #define PHP_RINIT ZEND_MODULE_ACTIVATE_N #define PHP_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N #define PHP_MINFO ZEND_MODULE_INFO_N #define PHP_GINIT ZEND_GINIT #define PHP_GSHUTDOWN ZEND_GSHUTDOWN #define PHP_MINIT_FUNCTION ZEND_MODULE_STARTUP_D #define PHP_MSHUTDOWN_FUNCTION ZEND_MODULE_SHUTDOWN_D #define PHP_RINIT_FUNCTION ZEND_MODULE_ACTIVATE_D #define PHP_RSHUTDOWN_FUNCTION ZEND_MODULE_DEACTIVATE_D #define PHP_MINFO_FUNCTION ZEND_MODULE_INFO_D #define PHP_GINIT_FUNCTION ZEND_GINIT_FUNCTION #define PHP_GSHUTDOWN_FUNCTION ZEND_GSHUTDOWN_FUNCTION
自己內部的記憶體管理時,透過附加的標誌來識別某某記憶體變數是否是持久性的,對於非持久內存,ZE會去清理。但在擴展內部最好還是自己去清理非持久內存,因為擴展自己請求分配的非持久內存,將在長時間內保持為未回收狀態,這樣與之相關的資源長時間得不到釋放。