PHP 的 Zend Engine (ZE) 預設每次請求都會重新編譯腳本(Compile → Execute),效能有損且原始碼裸露。若能將「編譯」與「執行」分離,並對中間碼進行加密,即可同時提升效能與保護智慧財產。
ZE 內部透過兩個函數指標控制流程:
zend_compile_file:將 PHP 檔案編譯為 zend_op_array (opcode 陣列)。zend_execute:執行該 opcode 陣列。我們可以編寫 PHP 擴展模組,在模組初始化 (MINIT) 時攔截並替換這兩個指標。
static zend_compile_file_t old_compile_file;
static zend_execute_t old_execute;
PHP_MINIT_FUNCTION(sample) {
old_compile_file = zend_compile_file; // 保存現場
old_execute = zend_execute;
zend_compile_file = my_compile_file; // 截獲編譯
zend_execute = my_execute; // 截獲執行
return SUCCESS;
}
static zend_op_array *my_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) {
// 若為已編譯的 .ze 檔,直接讀取 opcode 內容返回
if (strstr(file_handle->filename, ".ze") != NULL) {
return load_precompiled_opcodes(file_handle->filename);
}
// 否則呼叫原始編譯器,並可選擇性將產出的 opcode 存盤為 .ze
zend_op_array *op_array = old_compile_file(file_handle, type TSRMLS_CC);
if (op_array) {
save_opcodes_to_file(op_array, file_handle->filename);
}
return op_array;
}
static void my_execute(zend_op_array *op_array TSRMLS_DC) {
// 若 opcode 經過加密,此處先解密
if (is_encrypted(op_array)) {
decrypt_op_array(op_array);
}
old_execute(op_array TSRMLS_CC); // 交由原始執行器
}
my_compile_file 邏輯)將所有腳本編譯為 opcode 並加密儲存為 .zec 檔。my_compile_file 辨識 .zec 副檔名 → 讀取 → 解密 → 返回 opcode 陣列 → my_execute 執行。my_execute 中可加入授權驗證、執行統計等邏輯。透過 Zend Engine 擴展機制實現編譯/執行分離,是 PHP 原始碼加密與效能優化的核心路徑。雖開發門檻較高(需 C 語言與 ZE 內部結構知識),但為商業級產品提供了堅實的保護基礎。