### 定義函數
函數會被注冊到EG(function_table),函數被調用時根據函數名稱在這個符號表中查找
內部函數即由C實現邏輯的函數,可以直接供php調用,用戶函數是指PHP腳本中用戶自定義函數;
內部函數結構如下:
```c
typedef struct _zend_internal_function {
/* Common elements */
zend_uchar type;
zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */
uint32_t fn_flags;
zend_string* function_name;
zend_class_entry *scope;
zend_function *prototype;
uint32_t num_args;
uint32_t required_num_args;
zend_internal_arg_info *arg_info;
/* END of common elements */
void (*handler)(INTERNAL_FUNCTION_PARAMETERS); //函數指針,展開:void (*handler)(zend_execute_data *execute_data, zval *return_value)
struct _zend_module_entry *module;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
} zend_internal_function;
```
主要關注的是handler,handler是一個函數指針,由PHP_FUNCTION定義,例如:
```c
// hello.c
PHP_FUNCTION(helloworld)
{
zend_string *strg;
strg = strpprintf(0, "hello world \n");
RETURN_STR(strg);
}
```
宏定義:
```c
// main/php.h
#define PHP_FUNCTION ZEND_FUNCTION
// zend_API.h
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FN(name) zif_##name
// zend.h
#define INTERNAL_FUNCTION_PARAMETERS zend_execute_data *execute_data, zval *return_value
```
展開后:
```c
void zif_helloworld(zend_execute_data *execute_data, zval *return_value)
{
zend_string *strg;
strg = strpprintf(0, "hello world \n");
RETURN_STR(strg);
}
```
以上擴展可以通過PHP_FUNCTION定義函數,接下來需要注冊函數,每個函數需要實現zend_function_entry結構,然后把zend_function_entry保存到擴展zend_module_entry.functions函數列表即可;
zend_function_entry通過PHP_FE()定義,例如:
```c
const zend_function_entry hello_functions[] =
{
PHP_FE(wcl, NULL)
PHP_FE(helloworld, NULL)
PHP_FE_END /* Must be the last line in hello_functions[] */
};
```
zend_function_entry結構:
```c
typedef struct _zend_function_entry {
const char *fname; // 函數名稱
void (*handler)(INTERNAL_FUNCTION_PARAMETERS); // 函數指針
const struct _zend_internal_arg_info *arg_info; // 參數信息
uint32_t num_args; // 參數數目
uint32_t flags;
} zend_function_entry;
```
PHP_FE展開后:
```c
{ 'helloworld', zif_helloworld, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags },
```
上面整個結構展開后如下:
```c
const zend_function_entry hello_functions[] =
{
{ 'wcl', zif_wcl, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags },
{ 'helloworld', zif_helloworld, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags },
{ NULL, NULL, NULL, 0, 0 }
};
```
最后將hello_functions賦值給zend_module_entry.functions即可
### 函數參數解析
通過zend_parse_parameters()解析保存在zend_execute_data的參數;
```c
zend_string *str;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &str) == FAILURE){
...
}
```
- num_args: 通過ZEND_NUM_ARGS()獲得參數個數,TSRMLS_CC用來確保線程安全
- type_spec: 是一個字符串,用來標識解析參數類型
- l 或 L 表示傳入的參數解析為zend_long(l!或L!則會檢測參數是否為null,若為null,則設置為0,同時zend_bool設置為1)
- b表示傳入的參數解析為zend_bool
- d表示傳入的參數解析為double
- s, S, P, p表示傳入的參數解析為string,s解析到char*,且需要一個size_t類型用于獲取字符串長度,S解析到zend_string
- a, A, h, H表示傳入的參數解析為array,aA解析到zval,hH解析到hashTable
- o, O 對象, 解析到zval
- r 資源, 解析到zval
- C 類, 解析到zend_class_entry
- f 回調函數, 解析到zend_fcall_info
- z 任意類型
type_spec標識符:
- | 表示之后的參數為可選,例如al|b可以表示3個參數或2個參數,b為可選
- \* 可變參數,可以不傳遞
- \+ 可變參數,至少一個
### 引用傳參
如果用到參數引用,需要定義參數數組,參數數組定義在ZEND_BEGIN_ARG_INFO_EX和ZEND_END_ARG_INFO兩個宏之間
```c
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)
```
- name參數數組名,對應PHP_FE的第二個參數
- required_num_args 函數有多少個引用參數,就需要在參數數組中定義多少個zend_internal_arg_info
zend_internal_arg_info宏定義如下:
```c
//pass_by_ref表示是否引用傳參,name為參數名稱
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 0 },
//只聲明此參數為引用傳參
#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, NULL, 0, pass_by_ref, 0, 0 },
//顯式聲明此參數的類型為指定類的對象,等價于PHP中這樣聲明:MyClass $obj
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, #classname, IS_OBJECT, pass_by_ref, allow_null, 0 },
//顯式聲明此參數類型為數組,等價于:array $arr
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, NULL, IS_ARRAY, pass_by_ref, allow_null, 0 },
//顯式聲明為callable,將檢查函數、成員方法是否可調
#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, NULL, IS_CALLABLE, pass_by_ref, allow_null, 0 },
//通用宏,自定義各個字段
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, NULL, type_hint, pass_by_ref, allow_null, 0 },
//聲明為可變參數
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 1 },
```
引用參數通過zend_parse_parameters()解析時只能使用"z"解析,不能再直接解析為zend_value了,否則引用將失效,下面是一個引用參數的例子
```c
// 引用參數數組定義
ZEND_BEGIN_ARG_INFO_EX(arginfo_changeName, 0, 0, 1)
ZEND_ARG_INFO(1, name) // zend_internal_arg_info宏定義
ZEND_END_ARG_INFO()
PHP_FUNCTION(changeName)
{
zval *lval;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &lval) == FAILURE) // z表示任意類型,將參數解析到zval地址
{
return;
}
zval *real_val = Z_REFVAL_P(lval); // Z_REFVAL_P展開后&(lval.value->ref.val)
Z_LVAL_P(real_val) = 100; // Z_LVAL_P展開后lval.value->ref.val.value.lval = 100
}
const zend_function_entry hello_functions[] =
{
PHP_FE(changeName, arginfo_changeName) // 第二個參數為參數數組名
PHP_FE_END
};
```
### 函數返回值
#### 返回數組
```c
PHP_FUNCTION(getArray)
{
array_init(return_value);
add_assoc_string(return_value, "name", "wuzhc");
add_assoc_string(return_value, "address", "GD");
add_next_index_string(return_value, "Guangzhou");
add_next_index_string(return_value, "School");
}
```
- add_assoc_* 添加關聯索引數組元素,如key=>value
- add_next_index_* 添加數字索引數組元素
```php
print_r(getArray());
```
php腳本輸出如下:
```bash
Array
(
[name] => wuzhc
[address] => GD
[0] => Guangzhou
[1] => School
)
```
### 調用其他函數
通過call_user_function,內部函數可以調用PHP腳本自定義函數或其他擴展的內部函數
```c
ZEND_API int call_user_function(HashTable *function_table, zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]);
```
- function_table符號表,普通函數邊到EG(function_table),類成員方法保存在zend_class_entry.function
- object(成員方法才需要用到,普通函數設置為NULL)
- function_name 調用函數名
- retval_ptr 返回值地址
- param_count 參數個數
- params 參數數組
以一個例子為說明:
```php
// 調用擴展內部函數my_array_merge,內部函數my_array_merge將調用array_merge函數
$arr1 = array(1,2);
$arr2 = array(3,4);
$arr3 = my_array_merge($arr1, $arr2);
print_r($arr3);
```
my_array_merge實現如下:
```c
PHP_FUNCTION(my_array_merge)
{
zend_array *arr1, *arr2;
zval call_func_name, call_func_ret, call_func_params[2];
zend_string *call_func_str;
char *func_name = "array_merge";
if (zend_parse_parameters(ZEND_NUM_ARGS(), "hh", &arr1, &arr2) == FAILURE)
{
return ;
}
call_func_str = zend_string_init(func_name, strlen(func_name), 0); // 分配zend_string
ZVAL_STR(&call_func_name, call_func_str); // call_func_name.value.str = call_func_str(string字符串指針)
ZVAL_ARR(&call_func_params[0], arr1); // call_func_params[0].value.arr = arr1 (array數組指針)
ZVAL_ARR(&call_func_params[1], arr2); // call_func_params[1].value.arr = arr2 (array數組指針)
if (SUCCESS != call_user_function(EG(function_table), NULL, &call_func_name, &call_func_ret, 2, call_func_params))
{
zend_string_release(call_func_str);
RETURN_FALSE;
}
else
{
zend_string_release(call_func_str);
RETURN_ARR(Z_ARRVAL(call_func_ret)); // 調用array_merge結果地址會存放在call_func_ret,RETURN_ARR參數是一個數組指針
}
}
```
### 回調函數
- php
- 編譯安裝
- 基本概念
- 垃圾回收機制
- 生命周期
- zval底層實現
- c擴展開發
- gdb調試工具
- 自定義擴展簡單demo
- 鉤子函數
- 讀取php.ini配置
- 數組
- 函數
- 類
- yaf擴展底層源碼
- swoole擴展底層源碼
- memoryGlobal內存池
- swoole協程使用記錄
- 單點登錄sso原理
- compser使用
- session實現機制
- c & linux
- gcc
- 指針
- 結構體,聯合和位字段
- 宏定義井號說明
- printf家族函數和可變參數
- 共享函數
- 靜態庫和動態庫
- makefile自動化構建
- 信號一
- 信號二
- inotify監控文件事件
- socket編程
- 簡介
- UNIX DOMAIN
- Internet DOMAIN
- TCP/IP
- 文件IO多路復用
- 內存管理
- 進程組,會話和控制終端
- daemon守護進程
- 多進程
- 多線程
- 常用進制轉換
- go
- 入門知識
- 字節和整數裝換
- python
- redis
- 應用場景
- 消息隊列
- 熱點數據
- 掃碼登錄
- 訂閱發布
- 次數限制
- 搶購超賣
- 持久化機制
- mysql
- 工作流程
- MyISAM和InnoDB區別
- 用戶和權限管理
- 執行計劃
- sql優化
- 事務和鎖
- 慢查詢日志
- case...when...then...end用法
- sql
- 參考
- linux
- 內核參數優化
- 防火墻設置
- docker
- docker入門知識
- 算法
- 多維數組合
- DFA算法
- 紅包金額分配