(PHP7內核剖析-11) 模塊擴展

1. 編譯工具php

(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
相關文章
相關標籤/搜索