$ cd php-4.4.2/ext
$ ./ext_skel --extname=mypg
Creating directory mypg
Creating basic files: config.m4, config.w32, php_mypg.h, mypg.c, ...
生成的關鍵檔案:
config.m4:Unix 建置腳本(phpize && ./configure --enable-mypg && make)。php_mypg.h:對外 API 宣告(PHP_FUNCTION、模組入口)。mypg.c:核心實現(MINIT/RINIT、資源管理、函式體)。
PHP_ARG_ENABLE(mypg, whether to enable mypg support,
[ --enable-mypg Enable mypg support])
if test "$PHP_MYPG" != "no"; then
# 尋找 PostgreSQL 安裝路徑
SEARCH_PATH="/usr/local /usr"
SEARCH_FOR="include/libpq-fe.h"
for i in $SEARCH_PATH; do
if test -r $i/$SEARCH_FOR; then
PGSQL_DIR=$i
break
fi
done
if test -z "$PGSQL_DIR"; then
AC_MSG_ERROR([PostgreSQL headers not found. Install libpq-dev or specify --with-pgsql=DIR])
fi
PHP_ADD_INCLUDE($PGSQL_DIR/include)
PHP_ADD_LIBRARY_WITH_PATH(pq, $PGSQL_DIR/lib, MYPG_SHARED_LIBADD)
PHP_SUBST(MYPG_SHARED_LIBADD)
PHP_NEW_EXTENSION(mypg, mypg.c, $ext_shared)
fi
#ifndef PHP_MYPG_H
#define PHP_MYPG_H
extern zend_module_entry mypg_module_entry;
#define phpext_mypg_ptr &mypg_module_entry
#ifdef PHP_WIN32
# define PHP_MYPG_API __declspec(dllexport)
#else
# define PHP_MYPG_API
#endif
PHP_MINIT_FUNCTION(mypg);
PHP_FUNCTION(mypg_connect);
PHP_FUNCTION(mypg_query);
PHP_FUNCTION(mypg_fetch_assoc);
PHP_FUNCTION(mypg_close);
#ifdef ZTS
#include "TSRM.h"
#endif
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_mypg.h"
#include "libpq-fe.h" // PostgreSQL C API
// 資源類型編號
static int le_pgsql_link;
// 連結資源析構器:PHP 自動呼叫 (請求結束或 unset)
static void _close_pgsql_link(zend_resource *rsrc) {
PGconn *conn = (PGconn*)rsrc->ptr;
if (conn) PQfinish(conn);
}
PHP_MINIT_FUNCTION(mypg) {
// 註冊資源析構器
le_pgsql_link = zend_register_list_destructors_ex(_close_pgsql_link, NULL, "pgsql link", module_number);
return SUCCESS;
}
// 連線函式:mypg_connect("host=localhost dbname=test user=postgres")
PHP_FUNCTION(mypg_connect) {
char *conninfo;
size_t conninfo_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &conninfo, &conninfo_len) == FAILURE) return;
PGconn *conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK) {
php_error_docref(NULL, E_WARNING, "Connection failed: %s", PQerrorMessage(conn));
PQfinish(conn);
RETURN_FALSE;
}
// 建立 zend_resource 並返回 resource ID
RETURN_RES(zend_register_resource(conn, le_pgsql_link));
}
// 查詢執行
PHP_FUNCTION(mypg_query) {
zval *link_res;
char *sql; size_t sql_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &link_res, &sql, &sql_len) == FAILURE) return;
PGconn *conn = (PGconn*)zend_fetch_resource(Z_RES_P(link_res), "pgsql link", le_pgsql_link);
if (!conn) { RETURN_FALSE; }
PGresult *res = PQexec(conn, sql);
ExecStatusType status = PQresultStatus(res);
if (status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK) {
php_error_docref(NULL, E_WARNING, "Query failed: %s", PQerrorMessage(conn));
PQclear(res);
RETURN_FALSE;
}
// 將 PGresult 也註冊為資源,供 fetch 使用
RETURN_RES(zend_register_resource(res, le_pgsql_result));
}
// 釋放結果集資源
static void _free_pgsql_result(zend_resource *rsrc) {
PGresult *res = (PGresult*)rsrc->ptr;
if (res) PQclear(res);
}
// 模組入口
zend_module_entry mypg_module_entry = {
STANDARD_MODULE_HEADER,
"mypg",
mypg_functions, // function_entry 陣列
PHP_MINIT(mypg), NULL, NULL, NULL, NULL,
"0.1.0",
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(mypg)
$ cd php-4.4.2/ext/mypg
$ phpize
$ ./configure --enable-mypg --with-pgsql=/usr/local/pgsql
$ make && sudo make install
# php.ini 加入 extension=mypg.so
$ php -m | grep mypg
$conn = mypg_connect("host=localhost dbname=test user=postgres password=secret");
if (!$conn) die("Connect failed");
$res = mypg_query($conn, "SELECT id, name FROM users WHERE active=1");
while ($row = mypg_fetch_assoc($res)) {
echo "User #{$row['id']}: {$row['name']}\n";
}
mypg_close($conn);
PQprepare/PQexecPrepared 實現 Prepared Statement,防 SQL 注入。PQsetNoticeProcessor 捕獲 NOTICE/LISTEN/NOTIFY。pconnect)。PQexec 必須配對 PQclear;資源析構器不可漏註冊。php_error_docref 回報 PHP 警告,嚴重錯誤 RETURN_FALSE 而非 exit。PQsetClientEncoding(conn, "UTF8") 確保中文正常。PHP 擴展開發核心流程:ext_skel 生成骨架 → 修改 config.m4 連結外部庫 → 實現 MINIT/資源管理/函式體 → phpize && configure && make install。掌握 Zend API 資源機制(zend_register_resource/zend_fetch_resource/析構器),即可將任意 C 庫無縫暴露給 PHP,獲得數量級效能提升。