PHP內核探索:函數的參數

取參數的個數與解析參數列表
服務器君一共花費了215.920 ms進行了6次數據庫查詢,努力地為您提供了這個頁面。
試試閱讀模式?希望聽取您的建議

前面介紹了函數的定義,函數的定義只是一個將函數名注冊到函數列表的過程,在了解了函數的定義后,我們來看看函數的參數。 這一小節將包括用戶自定義函數的參數和內部函數的參數兩部分,詳細內容如下:

用戶自定義函數的參數

我們對于參數的類型提示做了分析,這里我們在這一小節的基礎上,進行一些更詳細的說明。 在經過詞語分析,語法分析后,我們知道對于函數的參數檢查是通過 zend_do_receive_arg 函數來實現的。在此函數中對于參數的關鍵代碼如下:

CG(active_op_array)->arg_info = erealloc(CG(active_op_array)->arg_info,
        sizeof(zend_arg_info)*(CG(active_op_array)->num_args));
cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1];
cur_arg_info->name = estrndup(varname->u.constant.value.str.val,
        varname->u.constant.value.str.len);
cur_arg_info->name_len = varname->u.constant.value.str.len;
cur_arg_info->array_type_hint = 0;
cur_arg_info->allow_null = 1;
cur_arg_info->pass_by_reference = pass_by_reference;
cur_arg_info->class_name = NULL;
cur_arg_info->class_name_len = 0;

整個參數的傳遞是通過給中間代碼的arg_info字段執行賦值操作完成。關鍵點是在arg_info字段。arg_info字段的結構如下:

typedef struct _zend_arg_info {
    const char *name;   /* 參數的名稱*/
    zend_uint name_len;     /* 參數名稱的長度*/
    const char *class_name; /* 類名 */
    zend_uint class_name_len;   /* 類名長度*/
    zend_bool array_type_hint;  /* 數組類型提示 */
    zend_bool allow_null;   /* 是否允許為NULL */
    zend_bool pass_by_reference;    /* 是否引用傳遞 */
    zend_bool return_reference; 
    int required_num_args;  
} zend_arg_info;

參數的值傳遞和參數傳遞的區分是通過 pass_by_reference參數在生成中間代碼時實現的。

對于參數的個數,中間代碼中包含的arg_nums字段在每次執行 **zend_do_receive_arg×× 時都會加1.如下代碼:

CG(active_op_array)->num_args++;

并且當前參數的索引為CG(active_op_array)->num_args-1 .如下代碼:

cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-1];

以上的分析是針對函數定義時的參數設置,這些參數是固定的。而在實際編寫程序時可能我們會用到可變參數。 此時我們會使用到函數 func_num_args 和 func_get_args。 它們是以內部函數存在。于是在 Zend\zend_builtin_functions.c 文件中找到這兩個函數的實現。 首先我們來看func_num_args函數的實現。其代碼如下:

/* {{{ proto int func_num_args(void)
   Get the number of arguments that were passed to the function */
ZEND_FUNCTION(func_num_args)
{
    zend_execute_data *ex = EG(current_execute_data)->prev_execute_data;
 
    if (ex && ex->function_state.arguments) {
        RETURN_LONG((long)(zend_uintptr_t)*(ex->function_state.arguments));
    } else {
        zend_error(E_WARNING,
"func_num_args():  Called from the global scope - no function context");
        RETURN_LONG(-1);
    }
}
/* }}} */

在存在 ex->function_state.arguments的情況下,即函數調用時,返回ex->function_state.arguments轉化后的值 ,否則顯示錯誤并返回-1。 這里最關鍵的一點是EG(current_execute_data)。這個變量存放的是當前執行程序或函數的數據。此時我們需要取前一個執行程序的數據,為什么呢? 因為這個函數的調用是在進入函數后執行的。函數的相關數據等都在之前執行過程中。于是調用的是:

zend_execute_data *ex = EG(current_execute_data)->prev_execute_data;

在了解func_num_args函數的實現后,func_get_args函數的實現過程就簡單了,它們的數據源是一樣的, 只是前面返回的是長度,而這里返回了一個創建的數組。數組中存放的是從ex->function_state.arguments轉化后的數據。

內部函數的參數

以上我們所說的都是用戶自定義函數中對于參數的相關內容。下面我們開始講解內部函數是如何傳遞參數的。 以常見的count函數為例。其參數處理部分的代碼如下:

/* {{{ proto int count(mixed var [, int mode])
   Count the number of elements in a variable (usually an array) */
PHP_FUNCTION(count)
{
    zval *array;
    long mode = COUNT_NORMAL;
 
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l",
         &array, &mode) == FAILURE) {
        return;
    }
    ... //省略
}

這包括了兩個操作:一個是取參數的個數,一個是解析參數列表。

1. 取參數的個數

取參數的個數是通過ZEND_NUM_ARGS()宏來實現的。其定義如下:

#define ZEND_NUM_ARGS()     (ht)

ht是在 Zend/zend.h文件中定義的宏 INTERNAL_FUNCTION_PARAMETERS 中的ht,如下:

#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value,
zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC

2. 解析參數列表

PHP內部函數在解析參數時使用的是 zend_parse_parameters。 它可以大大簡化參數的接收處理工作,雖然它在處理可變參數時還有點弱。

其聲明如下:

ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...)
  • 第一個參數num_args表明表示想要接收的參數個數,我們經常使用ZEND_NUM_ARGS() 來表示對傳入的參數“有多少要多少”。
  • 第二參數應該總是宏 TSRMLS_CC 。
  • 第三個參數 type_spec 是一個字符串,用來指定我們所期待接收的各個參數的類型,有點類似于 printf 中指定輸出格式的那個格式化字符串。
  • 剩下的參數就是我們用來接收PHP參數值的變量的指針。

zend_parse_parameters() 在解析參數的同時會盡可能地轉換參數類型,這樣就可以確保我們總是能得到所期望的類型的變量。 任何一種標量類型都可以轉換為另外一種標量類型,但是不能在標量類型與復雜類型(比如數組、對象和資源等)之間進行轉換。 如果成功地解析和接收到了參數并且在轉換期間也沒出現錯誤,那么這個函數就會返回 SUCCESS,否則返回 FAILURE。 如果這個函數不能接收到所預期的參數個數或者不能成功轉換參數類型時就會拋出一些錯誤信息。

第三個參數指定的各個參數類型列表如下所示:

  • l - 長整形
  • d - 雙精度浮點類型
  • s - 字符串 (也可能是空字節)和其長度
  • b - 布爾型
  • r - 資源,保存在 zval*
  • a - 數組,保存在 zval*
  • o - (任何類的)對象,保存在 zval *
  • O - (由class entry 指定的類的)對象,保存在 zval *
  • z - 實際的 zval*

除了各個參數類型,第三個參數還可以包含下面一些字符,它們的含義如下:

  • | - 表明剩下的參數都是可選參數。如果用戶沒有傳進來這些參數值,那么這些值就會被初始化成默認值。
  • / - 表明參數解析函數將會對剩下的參數以 SEPARATE_ZVAL_IF_NOT_REF() 的方式來提供這個參數的一份拷貝,除非這些參數是一個引用。
  • ! - 表明剩下的參數允許被設定為 NULL(僅用在 a、o、O、r和z身上)。如果用戶傳進來了一個 NULL 值,則存儲該參數的變量將會設置為 NULL。

延伸閱讀

此文章所在專題列表如下:

  1. PHP內核探索:從SAPI接口開始
  2. PHP內核探索:一次請求的開始與結束
  3. PHP內核探索:一次請求生命周期
  4. PHP內核探索:單進程SAPI生命周期
  5. PHP內核探索:多進程/線程的SAPI生命周期
  6. PHP內核探索:Zend引擎
  7. PHP內核探索:再次探討SAPI
  8. PHP內核探索:Apache模塊介紹
  9. PHP內核探索:通過mod_php5支持PHP
  10. PHP內核探索:Apache運行與鉤子函數
  11. PHP內核探索:嵌入式PHP
  12. PHP內核探索:PHP的FastCGI
  13. PHP內核探索:如何執行PHP腳本
  14. PHP內核探索:PHP腳本的執行細節
  15. PHP內核探索:操作碼OpCode
  16. PHP內核探索:PHP里的opcode
  17. PHP內核探索:解釋器的執行過程
  18. PHP內核探索:變量概述
  19. PHP內核探索:變量存儲與類型
  20. PHP內核探索:PHP中的哈希表
  21. PHP內核探索:理解Zend里的哈希表
  22. PHP內核探索:PHP哈希算法設計
  23. PHP內核探索:翻譯一篇HashTables文章
  24. PHP內核探索:哈希碰撞攻擊是什么?
  25. PHP內核探索:常量的實現
  26. PHP內核探索:變量的存儲
  27. PHP內核探索:變量的類型
  28. PHP內核探索:變量的值操作
  29. PHP內核探索:變量的創建
  30. PHP內核探索:預定義變量
  31. PHP內核探索:變量的檢索
  32. PHP內核探索:變量的類型轉換
  33. PHP內核探索:弱類型變量的實現
  34. PHP內核探索:靜態變量的實現
  35. PHP內核探索:變量類型提示
  36. PHP內核探索:變量的生命周期
  37. PHP內核探索:變量賦值與銷毀
  38. PHP內核探索:變量作用域
  39. PHP內核探索:詭異的變量名
  40. PHP內核探索:變量的value和type存儲
  41. PHP內核探索:全局變量Global
  42. PHP內核探索:變量類型的轉換
  43. PHP內核探索:內存管理開篇
  44. PHP內核探索:Zend內存管理器
  45. PHP內核探索:PHP的內存管理
  46. PHP內核探索:內存的申請與銷毀
  47. PHP內核探索:引用計數與寫時復制
  48. PHP內核探索:PHP5.3的垃圾回收機制
  49. PHP內核探索:內存管理中的cache
  50. PHP內核探索:寫時復制COW機制
  51. PHP內核探索:數組與鏈表
  52. PHP內核探索:使用哈希表API
  53. PHP內核探索:數組操作
  54. PHP內核探索:數組源碼分析
  55. PHP內核探索:函數的分類
  56. PHP內核探索:函數的內部結構
  57. PHP內核探索:函數結構轉換
  58. PHP內核探索:定義函數的過程
  59. PHP內核探索:函數的參數
  60. PHP內核探索:zend_parse_parameters函數
  61. PHP內核探索:函數返回值
  62. PHP內核探索:形參return value
  63. PHP內核探索:函數調用與執行
  64. PHP內核探索:引用與函數執行
  65. PHP內核探索:匿名函數及閉包
  66. PHP內核探索:面向對象開篇
  67. PHP內核探索:類的結構和實現
  68. PHP內核探索:類的成員變量
  69. PHP內核探索:類的成員方法
  70. PHP內核探索:類的原型zend_class_entry
  71. PHP內核探索:類的定義
  72. PHP內核探索:訪問控制
  73. PHP內核探索:繼承,多態與抽象類
  74. PHP內核探索:魔術函數與延遲綁定
  75. PHP內核探索:保留類與特殊類
  76. PHP內核探索:對象
  77. PHP內核探索:創建對象實例
  78. PHP內核探索:對象屬性讀寫
  79. PHP內核探索:命名空間
  80. PHP內核探索:定義接口
  81. PHP內核探索:繼承與實現接口
  82. PHP內核探索:資源resource類型
  83. PHP內核探索:Zend虛擬機
  84. PHP內核探索:虛擬機的詞法解析
  85. PHP內核探索:虛擬機的語法分析
  86. PHP內核探索:中間代碼opcode的執行
  87. PHP內核探索:代碼的加密與解密
  88. PHP內核探索:zend_execute的具體執行過程
  89. PHP內核探索:變量的引用與計數規則
  90. PHP內核探索:新垃圾回收機制說明

本文地址:http://www.824886.live/librarys/veda/detail/1466,歡迎訪問原出處。

不打個分嗎?

轉載隨意,但請帶上本文地址:

http://www.824886.live/librarys/veda/detail/1466

如果你認為這篇文章值得更多人閱讀,歡迎使用下面的分享功能。
小提示:您可以按快捷鍵 Ctrl + D,或點此 加入收藏。

大家都在看

閱讀一百本計算機著作吧,少年

很多人覺得自己技術進步很慢,學習效率低,我覺得一個重要原因是看的書少了。多少是多呢?起碼得看3、4、5、6米吧。給個具體的數量,那就100本書吧。很多人知識結構不好而且不系統,因為在特定領域有一個足夠量的知識量+足夠良好的知識結構,系統化以后就足以應對大量未曾遇到過的問題。

奉勸自學者:構建特定領域的知識結構體系的路徑中再也沒有比學習該專業的專業課程更好的了。如果我的知識結構體系足以囊括面試官的大部分甚至吞并他的知識結構體系的話,讀到他言語中的一個詞我們就已經知道他要表達什么,我們可以讓他坐“上位”畢竟他是面試官,但是在知識結構體系以及心理上我們就居高臨下。

所以,閱讀一百本計算機著作吧,少年!

《致加西亞的信》 阿爾伯特·哈伯德(Hubbard.E.) (作者), 趙立光 (譯者), 艾柯 (譯者)

《致加西亞的信(經典盒裝版)》內容簡介:美西戰爭爆發以后,美國必須立即與古巴起義軍首領加西亞取得聯系,并獲得他的合作。但當時,加西亞身在古巴的深山里——沒有人知道他的確切地點,所以沒法與他取得聯系。這時,有人向總統推薦一個名叫羅文的人,說他有辦法找到加西亞,而且也只有他才能找得到。他們找來羅文,交給他一封寫給加西亞的信。三周后,羅文徒步走過一個危機四伏的國家,最終把那封信交給了加西亞。 此后,羅文的事跡被傳為佳話,“送信”成為了敬業、忠誠、勤奮的象征,羅文便成了每個領導都想找到的人和每個員工都應該學習和效仿的榜樣。

更多計算機寶庫...

云南快乐十分走势一定牛 天津快乐十分开奖结果查 买股票的软件 浙江体彩6十1开奖结果 云南时时彩是官网 福建11选5APP 东商期货配资 快乐十二中奖规则奖金 上海时时乐历史开奖 上海十一选五开奖走势 福建31选7今天开奖号码