咱們首先找到快速上手文章裏面關於函數定義的代碼,以此說明而後開發PHP的函數
//php_myext.h PHP_FUNCTION(myext_hello);//函數申明,全部在myext.c文件定義的函數,都會在這個文件裏有一個申明 //myext.c static zend_function_entry myext_functions[] = { PHP_FE(myext_hello, NULL)//每一個函數一行,第一個參數與PHP_FUNCTION(name)的name同樣 {NULL, NULL, NULL}//固定語法 };
PHP_FUNCTION(myext_hello)
{
php_printf("hello you are success"); }
從上面的代碼能夠看出,開發一個php的函數須要3個步驟:php
1,申明函數的原型數組
2,定義函數,書寫函數的代碼函數
3,把函數註冊到本擴展模塊裏ui
咱們先來看2點。this
1 PHP_FUNCTION(myext_hello) 2 { 3 php_printf("hello you are success"); 4 } 5 6 #define PHP_FUNCTION ZEND_FUNCTION 7 #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) 8 #define ZEND_FN(name) zif_##name 9 #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) 10 #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC 11 12 //根據Zend/zend.h 和 Zend/zend_API.h中的宏定義,逐步展開 13 14 PHP_FUNCTION(myext_hello) 15 ZEND_FUNCTION(myext_hello) 16 ZEND_NAMED_FUNCTION(ZEND_FN(myext_hello)) 17 ZEND_NAMED_FUNCTION(zif_myext_hello_name) 18 void zif_myext_hello_name(INTERNAL_FUNCTION_PARAMETERS) 19 void zif_myext_hello_name( int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC)
INTERNAL_FUNCTION_PARAMETERS 代替的參數說明spa
名稱和類型 | 說明 | 使用方法 |
---|---|---|
int ht |
傳遞的參數數量 | ZEND_NUM_ARGS() |
zval *return_value |
函數的返回值,默認是IS_NULL . |
RETVAL_* ,RETURN_* |
zval **return_value_ptr |
當你須要返回引用的時候,把它賦值你要返回的變量,可是官方文檔不建議返回引用. | |
zval *this_ptr |
若是當期函數是一個類方法,該指針指向對應的class,至關於PHP代碼中的$this. | getThis() |
int return_value_used |
函數的調用者是否使用了返回值. |
經過編寫myext_example_args函數,瞭解了int ht , zval *return_value , int return_value_used三個參數的使用方法。指針
PHP_FUNCTION(myext_example_args);//php_myext.h PHP_FE(myext_example_args, NULL)//static zend_function_entry myext_functions[] 在裏面增長一個註冊函數 //函數定義myext.c PHP_FUNCTION(myext_example_args){
php_printf("zend_num_args() => %d,ht=>%d\n",ZEND_NUM_ARGS(),ht);//zend_num_args() => 3,ht=>3code
if(return_value_used){ php_printf("返回值已經使用到了\n"); //$result = myext_example_args() }else{ php_printf("返回值沒有被使用\n"); //myext_example_args() } RETURN_LONG(33);//返回值 }
咱們來看函數註冊的代碼,orm
PHP_FE(myext_hello, NULL)//第二個參數是用來對函數的傳參進行控制的,好比參數總數控制,是否傳遞引用,是否容許NULL,是否指定對象類,是否指定數組類型等,傳遞NULL表示不作控制
//相關的宏定義 #define PHP_FE ZEND_FE #define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0) #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, //arg_info的結構體 typedef struct _zend_arg_info { const char *name;//參數名稱 zend_uint name_len;//名稱長度 const char *class_name;//參數類名稱 zend_uint class_name_len;//參數類名稱長度 zend_uchar type_hint;//不知道 zend_uchar pass_by_reference;//是否傳遞引用 zend_bool allow_null;//是否容許NULL值 zend_bool is_variadic;//不知道 } zend_arg_info;//每個參數的具體規則#define ZEND_ARG_INFO(pass_by_ref, name) { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 0 },
#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, NULL, 0, 0, pass_by_ref, 0, 0 }, #define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, IS_OBJECT, pass_by_ref, allow_null, 0 }, #define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, sizeof(#name)-1, NULL, 0, IS_ARRAY, pass_by_ref, allow_null, 0 }, #define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, sizeof(#name)-1, NULL, 0, type_hint, pass_by_ref, allow_null, 0 }, #define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, sizeof(#name)-1, NULL, 0, 0, pass_by_ref, 0, 1 },
//開始和結束的固定用法
//四個參數說明(變量名稱,沒有用到,是否返回引用,要求的參數數量)
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \
static const zend_arg_info name[] = { \
{ NULL, 0, NULL, required_num_args, 0, return_reference, 0, 0 },
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
#define ZEND_END_ARG_INFO() };
//咱們來看一個demo,str_replace函數的參數定義
ZEND_BEGIN_ARG_INFO_EX(arginfo_str_replace, 0, 0, 3)
ZEND_ARG_INFO(0, search)
ZEND_ARG_INFO(0, replace)
ZEND_ARG_INFO(0, subject)
ZEND_ARG_INFO(1, replace_count)
ZEND_END_ARG_INFO()
咱們編寫myext_example_arginfo函數,來演示一下arginfo變量的使用對象
PHP_FUNCTION(myext_example_arginfo);// php_myext.h ZEND_BEGIN_ARG_INFO_EX(arginfo_arginfo, 0,0,1)//定義arginfo_arginfo ZEND_ARG_INFO(1, str) //使用引用傳參 ZEND_END_ARG_INFO() PHP_FE(myext_example_arginfo, arginfo_arginfo)//每一個函數一行,第一個參數與PHP_FUNCTION(name)的name同樣 PHP_FUNCTION(myext_example_arginfo){ zval *z; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z) == FAILURE) { return; } convert_to_string(z);
ZVAL_STRING(z,"引用參數的值已經改變",1); return ;
}
/*php 代碼*/
$ref = "我是初始值";
myext_example_arginfo($ref);
var_dump($ref);//string(30) "引用參數的值已經改變"
在剛纔的例子裏,咱們接受了一個php參數,咱們來認真瞭解一個參數的接受
//有兩個主要的函數用於在擴展函數中結果傳遞過來的參數,二者的區別就是ex結尾的多了一個flag參數 ZEND_API int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, const char *type_spec, ...) //flags 是一個標識位,當參數接收出錯的時候,是否抑制警告,目前只有一個常亮能夠起做用,ZEND_PARSE_PARAMS_QUIET //num_args 參數數量 //變量類型 ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, const char *type_spec, ...) //num_args 參數數量 //變量類型
type_spec參數有點相似於printf的格式化字符參數
參數 | 類型 | 變量類型 |
---|---|---|
a | 數組 |
zval* |
A | 數組或對象 |
zval* |
b | 布爾值 |
zend_bool |
C | 類 |
zend_class_entry* |
d | 浮點型 |
double |
f | 函數 |
zend_fcall_info* , zend_fcall_info_cache* |
h | 數組 |
HashTable* |
H | 數組或對象 |
HashTable* |
l | 長整型 | long |
L | 超出範圍的長整型 |
long |
o | 對象 |
zval* |
O | 對象或指定類的對象 |
zval* , zend_class_entry* |
p | 字符串(一個有效的路徑) |
char* , int |
r | 資源類型 |
zval* |
s | 字符串 |
char* , int |
z | zval*變量 |
zval* |
Z | zval**變量 | zval** |
在這些參數列表裏,大部分的參數都是對應着一個變量,也就是你前面寫了一個參數,後面對應的位置用一個變量去接受,有幾個特殊的(f,O,p,s),每個參數對應兩個變量。
除了參數類型,還有幾個特殊用途的字符能夠實現參數接收的特殊功能。
* 一共>=0個參數
+ 一共>=1個參數
| 在|以前的參數爲必填參數,在|以後的參數爲可選參數
/ 用來修飾前面一個參數,若是不是引用,則從新拷貝一個新的變量傳遞進來
! 用來修飾前面一個參數,若是傳遞過來的參數值爲NULL,則直接轉化成C語言的NULL,而不是zval的IS_NULL變量,區別在於後者增長了計算和賦值,浪費了更多的資源
看到這裏,你極可能一頭霧水,徹底不明白這些到底怎麼使用,沒有關係,接着來咱們來看幾個例子,都是php標準擴展裏面用到的,結合你在php中使用的經驗,你應該一會兒就會有所領悟。
//mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] ) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &search, &replace, &subject, &zcount) == FAILURE) //在str_replace中,內核用了前3個ZZZ來接受必要的三個參數,接收到的變量是zval**類型,而後有一個|,表示後面的參數是可選的,若是傳遞了,仍是用一個Z(zval**)變量接受它 //int array_push ( array &$array , mixed $var [, mixed $... ] ) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) //在array_push中,內核用了a(zval*)來接受第一個參數,而後用了一個+表示後面至少得有一個參數傳遞,或者更多。 //array range ( mixed $start , mixed $limit [, number $step = 1 ] ) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/|z/", &zlow, &zhigh, &zstep) == FAILURE) //在range中,一共出現了3個z/,每一個z/表示一個參數,說明是用z(zval*)來接受變量,同時對非引用的變量作強制拷貝後再傳參
//mixed sscanf ( string $str , string $format [, mixed &$... ] ) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss*", &str, &str_len, &format, &format_len,&args, &num_args) == FAILURE) //在sscanf中,每一個s表示一個字符串,得用兩個變量來接受,一個char*類型,指向字符串,另外一個是int型,等於字符串的長度。&str, &str_len接受第一個s,&format, &format_len接受第二個s,最後一個*表示後面還能夠有0到多個參數
看了以上這些,你應該能瞭解zend_parse_parameters函數的使用了,咱們繼續經過幾個函數例子,讓你完整的理解參數的接受和使用的完整過程,因爲*和+涉及到多參數的使用,會有一點點複雜,在這裏咱們先跳過,往後再來進一步理解。
//一個相似strlen的函數,接受一個字符串,返回字符串的長度 PHP_FUNCTION(myext_example_strlen){ char *str; int len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str,&len) == FAILURE) { return; } RETURN_LONG(len); } /* $str = "1234567"; var_dump(myext_example_strlen($str)); int(7) */
//輸入兩個參數,返回最大值 PHP_FUNCTION(myext_example_max_num){ long i1; long i2; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &i1 ,&i2) == FAILURE) { return; } if(i1 > i2){ RETURN_LONG(i1); }else{ RETURN_LONG(i2); } } /* var_dump(myext_example_max_num(5,4)); var_dump(myext_example_max_num(4,5)); var_dump(myext_example_max_num("3","2"));//當輸入參數不是long型時,zend_parse_parameters會作自動轉換 var_dump(myext_example_max_num(1,false)); int(5) int(5) int(3) int(1) */