手把手教學在 PHP 5.6 環境下開發 C 語言擴展:使用 ext_skel 產生骨架、修改 config.m4、實作 PHP_FUNCTION、編譯安裝 .so、撰寫 PHP 測試腳本驗證。以字串與整數拼接函數 extstrcat 為例。
php-5.6.x.tar.gz)autoconf, automake, libtool, gcc, makephp5-dev (Debian/Ubuntu) 或 php-devel (RHEL/CentOS)# 進入 PHP 原始碼 ext/ 目錄
cd php-5.6.x/ext
# 定義函數原型檔
cat > extstrcat.def << 'EOF'
string extstrcat(string strarg, int intarg)
EOF
# 產生骨架
./ext_skel --extname=extstrcat --proto=extstrcat.def
# 產生目錄:ext/extstrcat/ 包含 config.m4, extstrcat.c, php_extstrcat.h 等
# 編輯 ext/extstrcat/config.m4
# 找到並取消註解以下幾行(移除開頭 dnl)
PHP_ARG_ENABLE(extstrcat, whether to enable extstrcat support,
[ --enable-extstrcat Enable extstrcat support])
// 編輯 ext/extstrcat/extstrcat.c
// 找到 PHP_FUNCTION(extstrcat) 並替換為:
PHP_FUNCTION(extstrcat)
{
char *strarg = NULL;
size_t strarg_len;
zend_long intarg; // PHP 5.6 使用 long
// 解析參數:"s"=string, "l"=long
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &strarg, &strarg_len, &intarg) == FAILURE) {
return;
}
// 整數轉字串
char intargstr[32];
int intarg_len = snprintf(intargstr, sizeof(intargstr), "%ld", intarg);
// 分配結果記憶體
size_t ret_len = strarg_len + intarg_len + 1;
char *retstr = emalloc(ret_len); // 使用 PHP 記憶體管理
// 拼接
memcpy(retstr, strarg, strarg_len);
memcpy(retstr + strarg_len, intargstr, intarg_len);
retstr[ret_len - 1] = '\0';
// 回傳字串(1 表示複製,0 表示移交所有權)
RETVAL_STRINGL(retstr, ret_len - 1, 1);
efree(retstr); // 複製後釋放臨時緩衝區
}
| 宏/函數 | 用途 |
|---|---|
zend_parse_parameters() | 解析 PHP 傳入參數,型別規格:"s"=string, "l"=long, "d"=double, "b"=bool, "a"=array, "o"=object, "z"=zval |
TSRMLS_CC | 線程安全宏(PHP 5.6 必須),PHP 7+ 已移除 |
emalloc()/efree() | PHP 記憶體管理,請求結束自動清理 |
RETVAL_STRINGL() | 回傳二進位安全字串(長度、是否複製) |
RETURN_STRING() | 回傳字串並直接 return |
cd php-5.6.x/ext/extstrcat
phpize
./configure --enable-extstrcat
make
# 複製 .so 到 PHP 擴展目錄
cp modules/extstrcat.so /usr/local/lib/php/extensions/no-debug-non-zts-20131226/
# 修改 php.ini
echo "extension=extstrcat.so" >> /usr/local/etc/php.ini
# 重啟 PHP-FPM / Apache
systemctl restart php-fpm
<?php
// test_extstrcat.php
if (!extension_loaded('extstrcat')) {
dl('extstrcat.' . PHP_SHLIB_SUFFIX); // CLI 環境動態載入
}
$result = extstrcat('testarg', 1234);
var_dump($result); // string(11) "testarg1234"
// 命令列執行
// php test_extstrcat.php
?>
phpize 失敗:確認安裝 php5-dev/php-devel 且版本匹配。configure 錯誤:檢查 config.m4 語法、PHP_ARG_ENABLE 參數。TSRMLS_CC 遺漏(PHP 5.6 必須)。php -m 檢查模組、php -i | grep extension_dir 確認路徑、ldd extstrcat.so 檢查相依庫。PHP_FE(extstrcat) 在 zend_function_entry 陣列中(ext_skel 自動產生)。| 項目 | PHP 5.6 | PHP 7+ |
|---|---|---|
| 整數型別 | long / zend_long | zend_long |
| TSRMLS | 必須 TSRMLS_CC | 移除,不需傳遞 |
| zval 操作 | 巨集操作 | 新 API (ZVAL_*) |
| 記憶體管理 | emalloc/efree | 相同,但引入 zend_string |
ext/standard/string.c 內建字串函數實作完成第一個擴展後,你將擁有深入 PHP 核心、突破效能瓶頸、封裝 C/C++ 庫的能力。