當 PHP 效能不足或需複用既有 C 程式庫時,有三種整合路徑。本文從架構、效能、開發成本三面向比較,並完整示範「編寫 PHP 擴展」的標準流程。
| 方案 | 適用場景 | 效能 | 複雜度 | 維護性 |
|---|---|---|---|---|
1. system()/exec() 呼叫獨立執行檔 | 既有 CLI 工具、低頻任務、隔離崩潰風險 | 低 (進程啟動開銷大) | 低 | 高 (語言無關) |
| 2. C Daemon + Socket/IPC | 高頻、長連線、狀態保持、多語言共用 | 中高 (避免重啟) | 高 (需處理通訊協定、併發) | 中 |
| 3. PHP C 擴展 | 高頻運算、需共享記憶體、深度整合 Zend | 最高 (內嵌執行) | 中高 (需懂 Zend API) | 中 (隨 PHP 部署) |
hello 為例)cd php-src/ext
./ext_skel --extname=hello
# 產生 hello/ 目錄,關鍵檔:config.m4, hello.c, php_hello.h
config.m4 啟用 --with-hello# 將 PHP_ARG_WITH 區塊取消註解
PHP_ARG_WITH(hello, for hello support,
[ --with-hello Include hello support])
cd hello
phpize
./configure --with-hello
make
# 產生 modules/hello.so
<?php
function dl_local(string $so): string {
if (!ini_get('enable_dl') || ini_get('safe_mode')) die('dl disabled');
$path = __DIR__ . '/' . $so;
if (!is_file($path) || !is_executable($path)) die("invalid $so");
if (!dl($path)) die('load failed');
return end(get_loaded_extensions());
}
if (!extension_loaded('hello')) dl_local('hello.so');
hello_world(); // 呼叫擴展函式
?>
// hello.c
#include "php_hello.h"
ZEND_FUNCTION(hello_strdiff) {
char *s1, *s2;
size_t l1, l2;
// 參數解析:"ss" = 兩個字串
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &s1, &l1, &s2, &l2) == FAILURE) return;
// 計算 Levenshtein 距離 (示範演算法)
int n = l1, m = l2;
int *d = ecalloc((n+1)*(m+1), sizeof(int));
for (int i=0;i<=n;i++) d[i*(m+1)] = i;
for (int j=0;j<=m;j++) d[j] = j;
for (int i=1;i<=n;i++) {
for (int j=1;j<=m;j++) {
int cost = (s1[i-1]==s2[j-1]) ? 0 : 1;
int a = d[(i-1)*(m+1)+j] + 1;
int b = d[i*(m+1)+j-1] + 1;
int c = d[(i-1)*(m+1)+j-1] + cost;
d[i*(m+1)+j] = a < b ? (a < c ? a : c) : (b < c ? b : c);
}
}
RETVAL_LONG(d[n*(m+1)+m]);
efree(d);
}
// 註冊函式
zend_function_entry hello_functions[] = {
ZEND_FE(hello_strdiff, NULL)
ZEND_FE_END
};
| 型別 | 參數規格字元 | C 變數 | 回傳宏 |
|---|---|---|---|
| 布林 | b | zend_bool | RETURN_BOOL |
| 整數 | l | zend_long | RETURN_LONG |
| 浮點 | d | double | RETURN_DOUBLE |
| 字串 | s | char*, size_t | RETURN_STRING(str, copy) |
| 陣列/物件/資源 | a/o/r | zval* | RETURN_ZVAL |
可選參數用 | 分隔,如 "s|l" 表字串 + 可選長整數。
emalloc/ecalloc/erealloc/efree (請求生命週期自動釋放)。pemalloc(ptr, 1)。estrdup/estrndup,回傳時 RETURN_STRING(str, 0) 移交所有權。同一演算法:純 PHP → C 擴展 約 **90 倍** 提升;純 PHP → 獨立 C 程式 + exec() 約 **500 倍** (但含進程開銷)。擴展適合「高頻、小資料、低延遲」場景。