C是靜態編譯的,執行效率比PHP代碼高很多。同樣的運算代碼,使用C來開發,性能會比PHP要提升數百倍。
另外C擴展是在進程啟動時加載的,PHP代碼只能操作Request生命周期的數據,C擴展可操作的范圍更廣。
##本例用的php版本的是5.3.3
cd php-5.3.3/ext/
./ext_skel --extname=myfun --proto=myfun.def
##執行成功後會生成myfun的編譯文件
ls myfun/
config.m4 config.w32 CREDITS .cvsignore EXPERIMENTAL myfun.c myfun.php php_myfun.h tests
config.m4中 dnl 為注釋的意思
##動態編譯選項,通過.so的方式鏈接,去掉dnl注釋
PHP_ARG_WITH(myfun, for myfun support,
[ --with-myfun Include myfun support])
##靜態編譯選項,通過enable來啟用,去掉dnl注釋
PHP_ARG_ENABLE(myfun, whether to enable myfun support,
[ --enable-myfun Enable myfun support])
修改完成編譯下
phpize
./configure --enable-myfun
make
make install
在myfun有個php的測試腳本,執行測試下
php -d enable_dl=On myfile.php
輸出結果:
Functions available in the test extension:
confirm_myfun_compiled
Congratulations! You have successfully modified ext/myfun/config.m4. Module myfun is now compiled into PHP.
其實confirm_myfun_compiled是構建工具幫我們生成的測試函數
現在我們來創建屬於自己的函數 helloWorld()
,功能就是輸出 Hello World!
vim myfun/php_myfun.h
##在PHP_FUNCTION(confirm_myfun_compiled); 下追加一行
PHP_FUNCTION(helloWorld);
實現helloworld實體
vim myfun/myfun.c
##zend_function_entry myfun_functions 補充要實現的函數
const zend_function_entry myfun_functions[] = {
PHP_FE(confirm_myfun_compiled, NULL) /* For testing, remove later. */
PHP_FE(helloWorld, NULL) /*這是補充的一行,末尾沒有逗號*/
{NULL, NULL, NULL} /* Must be the last line in myfun_functions[] */
};
##在末尾實現helloworld的內容
PHP_FUNCTION(helloWorld)
{
php_printf("Hello World!\n");
RETURN_TRUE;
}
重新編譯
./configure && make && make install
php -d enable_dl=On -r "dl('myfun.so');helloWorld();"
Hello World!
php -d enable_dl=On -r "dl('myfun.so');print confirm_myfun_compiled('helloWorld');"
Congratulations! You have successfully modified ext/myfun/config.m4. Module helloWorld is now compiled into PHP.
#ifndef PHP_MYFUN_H
#define PHP_MYFUN_H
extern zend_module_entry myfun_module_entry;
#define phpext_myfun_ptr &myfun_module_entry
#ifdef PHP_WIN32
# define PHP_MYFUN_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
# define PHP_MYFUN_API __attribute__ ((visibility("default")))
#else
# define PHP_MYFUN_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(myfun); /*當PHP被裝載時,模塊啟動函數即被引擎調用。這使得引擎做一些例如資源類型,注冊INI變量等的一次初始化。*/
PHP_MSHUTDOWN_FUNCTION(myfun); /*當PHP完全關閉時,模塊關閉函數即被引擎調用。通常用於注銷INI條目*/
PHP_RINIT_FUNCTION(myfun); /*在每次PHP請求開始,請求前啟動函數被調用。通常用於管理請求前邏輯。*/
PHP_RSHUTDOWN_FUNCTION(myfun); /*在每次PHP請求結束後,請求前關閉函數被調用。經常應用在清理請求前啟動函數的邏輯。*/
PHP_MINFO_FUNCTION(myfun); /*調用phpinfo()時模塊信息函數被呼叫,從而打印出模塊信息。*/
PHP_FUNCTION(confirm_myfun_compiled); /* For testing, remove later. */
PHP_FUNCTION(helloWorld);
#ifdef ZTS
#define MYFUN_G(v) TSRMG(myfun_globals_id, zend_myfun_globals *, v)
#else
#define MYFUN_G(v) (myfun_globals.v)
#endif
#endif /* PHP_MYFUN_H */
PHP_FUNCTION(confirm_myfun_compiled) //使用了宏PHP_FUNCTION(),該宏可以生成一個適合於Zend引擎的函數原型
{
char *arg = NULL;
int arg_len, len;
char *strg;
//獲得函數傳遞的參數
//第一個參數是傳遞給函數的參數個數。通常的做法是傳給它ZEND_NUM_ARGS()。這是一個表示傳遞給函數參數總個數的宏。
//第二個參數是為了線程安全,總是傳遞TSRMLS_CC宏。
//第三個參數是一個字符串,指定了函數期望的參數類型,後面緊跟著需要隨參數值更新的變量列表。因為PHP采用松散的變量定義和動態的類型判斷,這樣做就使得把不同類型的參數轉化為期望的類型成為可能。例如,如果用戶傳遞一個整數變量,可函數需要一個浮點數,那麼zend_parse_parameters()就會自動地把整數轉換為相應的浮點數。如果實際值無法轉換成期望類型(比如整形到數組形),會觸發一個警告。
/*
類型指示符
l long 符號整數
d double 浮點數
s char *, int 二進制字符串,長度
b zend_bool 邏輯型(1或0)
r zval * 資源(文件指針,數據庫連接等)
a zval * 聯合數組
o zval * 任何類型的對象
O zval * 指定類型的對象。需要提供目標對象的類類型
z zval * 無任何操作的zval
*/
//第四個參數為傳遞的參數數據的引用
//第五個參數為傳遞的參數個數
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { //獲得函數傳遞的參數
return;
}
len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "myfun", arg);
/*
通過設置RETURN_type()的方式,將返回控制到PHP。下表解釋了大多數存在的宏。
RETVAL_LONG(l) 整數
RETVAL_BOOL(b) 布爾數(1或0)
RETVAL_NULL() NULL
RETVAL_DOUBLE(d) 浮點數
RETVAL_STRING(s, dup) 字符串。如果dup為1,引擎會調用estrdup()重復s,使用拷貝。如果dup為0,就使用s
RETVAL_STRINGL(s, l, dup) 長度為l的字符串值。與上一個宏一樣,但因為s的長度被指定,所以速度更快。
RETVAL_TRUE 返回布爾值true。注意到這個宏沒有括號。
RETVAL_FALSE 返回布爾值false。注意到這個宏沒有括號。
RETVAL_RESOURCE(r) 資源句柄。
*/
RETURN_STRINGL(strg, len, 0);
}