(PHP7核心剖析-11) 模組擴充套件

wuaiqiu發表於2019-05-11

1. 編譯工具

(a).ext_skel:這個指令碼主要生成了編譯需要的配置以及擴充套件的基本結構

(b).php-config:這個指令碼主要是獲取PHP的安裝資訊

(c).phpize:用於生成configure檔案

2.編寫擴充套件的基本步驟

a.通過ext目錄下ext_skel指令碼生成擴充套件的基本框架;

./ext_skel --extname=wu

b.修改config.m4配置:設定編譯配置引數、設定擴充套件的原始檔、依賴庫/函式檢查等等;

PHP_ARG_WITH(arg_name,check message,help info): 定義一個--with-feature[=arg]這樣的編譯引數,引數分別為
引數名、執行./configure是展示資訊、執行--help時展示資訊

$PHP_引數名:獲取對應的引數值
PHP_ARG_ENABLE(arg_name,check message,help info): 定義一個--enable-feature[=arg]或--disable-feature參
數,--disable-feature等價於--enable-feature=no,這個巨集與PHP_ARG_WITH類似,通常情況下如果配置的引數需
要額外的arg值會使用PHP_ARG_WITH,而如果不需要arg值,只用於開關配置則會使用PHP_ARG_ENABLE。
./configure時輸出結果,其中error將會中斷configure執行

AC_MSG_CHECKING(message)
AC_MSG_RESULT(message)
AC_MSG_ERROR(message)
PHP_CHECK_LIBRARY(library, function [, action-found [, action-not-found ]]): 檢查依賴的庫中是否存在需要
的function,action-found為存在時執行的動作,action-not-found為不存在時執行的動作

c.編寫擴充套件要實現的功能:按照PHP擴充套件的格式以及PHP提供的API編寫功能;

#1.註冊全域性變數
//php_wu.h
#define MYTEST_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(mytest, v)

//定義全域性變數
ZEND_BEGIN_MODULE_GLOBALS(mytest)
    zend_long   open_cache;
    HashTable   class_table;
ZEND_END_MODULE_GLOBALS(mytest)

//wu.c
ZEND_DECLARE_MODULE_GLOBALS(mytest) 
#2.鉤子函式
PHP_MINIT_FUNCTION(mytest){
    這個階段可以進行內部類的註冊,如果你的擴充套件提供
    了類就可以在此函式中完成註冊;除了類還可以在此
    函式中註冊擴充套件定義的常量
}

PHP_RINIT_FUNCTION(mytest){
    如果你的擴充套件需要針對每一個請求進行處理則可以設
    置這個函式,如:對請求進行filter
}

PHP_RSHUTDOWN_FUNCTION(mytest){
    此函式在請求結束時被呼叫
}

PHP_MSHUTDOWN_FUNCTION(mytest){
    模組關閉階段回撥的函式,與module_startup_func對應,
    此階段主要可以進行一些資源的清理
}
#3.自定義函式
PHP_FUNCTION(my_func_1){
   自定義內部函式1
}

PHP_FUNCTION(my_func_1){
   自定義內部函式2(帶參)
   zval        *arr;
   //l(L)整型,L當資料溢位不報錯
   //(b)布林型,(d)浮點型
   //s(S)字串型,其中"s"將引數解析到char*,且需要額外提供一個size_t型別的變數用於獲取字串長度,“S”為zend_string
   //a(A)陣列型,o(O)物件型,r資源型,z任意型別
   //|: 表示此後的引數為可選引數,可以不傳,比如解析規則為:"al|b",則可以傳2個或3個引數
   //+、* : 用於可變引數,+、*的區別在於 * 表示可以不傳可變引數,而 + 表示可變引數至少有一個。需要額外提供一個int型別的變數用於獲取具體的數量
   if(zend_parse_parameters(ZEND_NUM_ARGS(), "la", &lval, &arr) == FAILURE){
        RETURN_FALSE;
    }
}

PHP_FUNCTION(my_func_3){
    自定義內部函式3(引用傳參)
     zval    *lval; //必須為zval
     zval    *obj;
    //引用引數解析時只能使用"z"解析
    if(zend_parse_parameters(ZEND_NUM_ARGS(), "zo", &lval, &obj) == FAILURE){
        RETURN_FALSE;
    }
}

//引數資訊(引數組名,無意義,返回值是否引用,引數個數)
ZEND_BEGIN_ARG_INFO_EX(arg_info_3, 0, 0, 2)
   //pass_by_ref表示是否引用傳參,name為引數名稱
    ZEND_ARG_INFO(pass_by_ref, name)
    //顯式宣告此引數的型別為指定類的物件,等價於PHP中這樣宣告:MyClass $obj
    ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null)
    //顯式宣告此引數型別為陣列,等價於:array $arr
    ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null)
    //通用巨集,自定義各個欄位
    ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)
    //宣告為可變引數
    ZEND_ARG_VARIADIC_INFO(pass_by_ref, name)
ZEND_END_ARG_INFO()

PHP_FUNCITON(my_func_3){
    自定義內部函式4(返回值) 
    //返回布林型,b:IS_FALSE、IS_TRUE
    RETURN_BOOL(b) 
    //返回false
    RETURN_FALSE  
    //返回true
    RETURN_TRUE
    //返回NULL
    RETURN_NULL()
    //返回整形,l型別:zend_long    
    RETURN_LONG(l)
    //返回浮點值,d型別:double
    RETURN_DOUBLE(d)
    //返回字串,內部字串,s型別為:zend_string *
    RETURN_STR(s)
    //返回char *型別的字串,s型別為char *
    RETURN_STRING(s)
    //返回空字串
    RETURN_EMPTY_STRING()
    //返回資源,r型別:zend_resource *
    RETURN_RES(r) 
    //返回陣列,r型別:zend_array *
    RETURN_ARR(r)      
    //返回物件,r型別:zend_object *
    RETURN_OBJ(r)    
}

const zend_function_entry mytest_functions[] = {
    PHP_FE(my_func_1,NULL)
    PHP_FE(my_func_2,NULL)
    PHP_FE(my_func_3,arg_info_3)
    PHP_FE(my_func_4,NULL)
    PHP_FE_END //末尾必須加這個
};

zend_module_entry mytest_module_entry = {
    STANDARD_MODULE_HEADER, //巨集統一設定
    "mytest", //模組名
    mytest_functions, //自定義函式陣列
    PHP_MINIT(mytest), //擴充套件初始化回撥函式
    PHP_MSHUTDOWN(mytest), //擴充套件關閉時回撥函式
    PHP_RINIT(mytest), //請求開始前回撥函式
    PHP_RSHUTDOWN(mytest), //請求結束時回撥函式
    NULL, //PHP_MINFO(mytest),php_info展示的擴充套件資訊處理函式
    "1.0.0",
    STANDARD_MODULE_PROPERTIES //巨集統一設定
};

ZEND_GET_MODULE(mytest) //讀取mytest_module_entry結構體
#4.zval操作工具類
//建立(這些巨集第一個引數z均為要設定的zval的指標,後面為要設定的zend_value)
ZVAL_UNDEF(z): 表示zval被銷燬
ZVAL_NULL(z): 設定為NULL
ZVAL_FALSE(z): 設定為false
ZVAL_TRUE(z): 設定為true
ZVAL_BOOL(z, b): 設定為布林型,b為IS_TRUE、IS_FALSE,與上面兩個等價
ZVAL_LONG(z, l): 設定為整形,l型別為zend_long,如:zval z; ZVAL_LONG(&z, 88);
ZVAL_DOUBLE(z, d): 設定為浮點型,d型別為double
ZVAL_STR(z, s): 設定字串,將z的value設定為s,s型別為zend_string*,不會增加s的refcount
ZVAL_ARR(z, a): 設定為陣列,a型別為zend_array*
ZVAL_OBJ(z, o): 設定為物件,o型別為zend_object*
ZVAL_RES(z, r): 設定為資源,r型別為zend_resource*
ZVAL_REF(z, r): 設定為引用,r型別為zend_reference*
ZVAL_NEW_EMPTY_REF(z): 新建立一個空引用,沒有設定具體引用的value

//獲取值及型別
Z_LVAL(zval)、Z_LVAL_P(zval_p): 返回zend_long
Z_DVAL(zval)、Z_DVAL_P(zval_p): 返回double
Z_STR(zval)、Z_STR_P(zval_p): 返回zend_string*
Z_STRVAL(zval)、Z_STRVAL_P(zval_p): 返回char*,即:zend_string->val
Z_STRLEN(zval)、Z_STRLEN_P(zval_p): 獲取字串長度
Z_STRHASH(zval)、Z_STRHASH_P(zval_p): 獲取字串的雜湊值
Z_ARR(zval)、Z_ARR_P(zval_p)、Z_ARRVAL(zval)、Z_ARRVAL_P(zval_p): 返回zend_array*
Z_OBJ(zval)、Z_OBJ_P(zval_p): 返回zend_object*
Z_OBJCE(zval)、Z_OBJCE_P(zval_p): 返回物件的zend_class_entry*
Z_OBJPROP(zval)、Z_OBJPROP_P(zval_p): 獲取物件的成員陣列
Z_RES(zval)、Z_RES_P(zval_p): 返回zend_resource*
Z_RES_HANDLE(zval)、Z_RES_HANDLE_P(zval_p): 返回資源handle
Z_RES_TYPE(zval)、Z_RES_TYPE_P(zval_p): 返回資源type
Z_RES_VAL(zval)、Z_RES_VAL_P(zval_p): 返回資源ptr
Z_REF(zval)、Z_REF_P(zval_p): 返回zend_reference*
Z_REFVAL(zval)、Z_REFVAL_P(zval_p): 返回引用的zval*

//型別轉換
convert_to_long(zval *op);
convert_to_double(zval *op);
convert_to_long_base(zval *op, int base);
convert_to_null(zval *op);
convert_to_boolean(zval *op);
convert_to_array(zval *op);
convert_to_object(zval *op);
zval_get_long(op):獲取格式化為long的值,返回值為zend_long
zval_get_double(op):獲取格式化為double的值,返回值double
zval_get_string(op):獲取格式化為string的值,返回值zend_string *

//字串操作
zend_string_init(const char *str, size_t len, int persistent);建立zend_string
zend_string_copy(zend_string *s);字串複製,只增加引用
zend_string_dup(zend_string *s, int persistent);字串拷貝,硬拷貝
zend_string_realloc(zend_string *s, size_t len, int persistent);將字串按len大小重新分配,會減少s的refcount,返回新的字串
zend_string_extend(zend_string *s, size_t len, int persistent);延長字串,與zend_string_realloc()類似,不同的是len不能小於s的長度
zend_string_refcount(const zend_string *s);獲取字串refcount
zend_string_addref(zend_string *s);增加字串refcount
zend_string_delref(zend_string *s);減少字串refcount
zend_string_release(zend_string *s);釋放字串,減少refcount,為0時銷燬
zend_string_free(zend_string *s);銷燬字串,不管引用計數是否為0
zend_string_equals(zend_string *s1, zend_string *s2);比較兩個字串是否相等,區分大小寫
zend_string_equals_ci(s1, s2);比較兩個字串是否相等,不區分大小寫

//陣列操作
ZVAL_NEW_ARR(z): 新分配一個陣列,主動分配一個zend_array
zend_hash_init(Z_ARRVAL(array), size, NULL, ZVAL_PTR_DTOR, 0);初始化陣列

1) key為zend_string
zend_hash_update(ht, key, pData):插入或更新元素,會增加key的refcount
zend_hash_add(ht, key, pData):新增元素,與zend_hash_update()類似,不同的地方在於如果元素已經存在則不會更新
2) key為普通字串:char*
zend_hash_str_update(ht, key, len, pData)
#define zend_hash_str_add(ht, key, len, pData) 
3) key為數值索引
zend_hash_index_update(ht, h, pData):更新第h個元素
zend_hash_index_add(ht, h, pData):插入元素,h為數值

zend_hash_find(const HashTable *ht, zend_string *key);根據zend_string key查詢陣列元素
zend_hash_str_find(const HashTable *ht, const char *key, size_t len);根據普通字串key查詢元素
zend_hash_index_find(const HashTable *ht, zend_ulong h);獲取數值索引元素
zend_hash_exists(const HashTable *ht, zend_string *key);判斷元素是否存在
zend_hash_str_exists(const HashTable *ht, const char *str, size_t len);判斷元素是否存在
zend_hash_index_exists(const HashTable *ht, zend_ulong h);判斷元素是否存在
zend_hash_num_elements(ht):獲取陣列元素數

zend_hash_del(HashTable *ht, zend_string *key);刪除key

//遍歷
ZEND_HASH_FOREACH_VAL(ht, val) {
    ...
} ZEND_HASH_FOREACH_END();

ZEND_HASH_FOREACH_NUM_KEY(ht, _h):遍歷獲取所有的數值索引
ZEND_HASH_FOREACH_STR_KEY(ht, _key):遍歷獲取所有的key
ZEND_HASH_FOREACH_KEY(ht, _h, _key):上面兩個的聚合
ZEND_HASH_FOREACH_NUM_KEY_VAL(ht, _h, _val) :遍歷獲取數值索引key及value
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _key, _val):遍歷獲取key及valu
ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val):上面兩個的聚合

zend_array_destroy(HashTable *ht):銷燬陣列
#5.常量
//註冊NULL常量
REGISTER_NULL_CONSTANT(name, flags) 
//註冊bool常量
REGISTER_BOOL_CONSTANT(name, bval, flags) 
//註冊整形常量
REGISTER_LONG_CONSTANT(name, lval, flags)
//註冊浮點型常量
REGISTER_DOUBLE_CONSTANT(name, dval, flags)
//註冊字串常量,str型別為char*
REGISTER_STRING_CONSTANT(name, str, flags) 
//註冊字串常量,擷取指定長度,str型別為char*
REGISTER_STRINGL_CONSTANT(name, str, len, flags)

d.生成configure:擴充套件編寫完成後執行phpize指令碼生成configure及其它配置檔案;

phpsize

e.編譯&安裝:./configure、make、make install,然後將擴充套件的.so路徑新增到php.ini中。

./configure
make
make install

相關文章