有极速快乐十分吗|极速快乐十分走势图|

PHP內核探索:變量存儲與類型

存儲結構與變量類型
服務器君一共花費了175.296 ms進行了6次數據庫查詢,努力地為您提供了這個頁面。
試試閱讀模式?希望聽取您的建議

先回答前面一節的那個問題吧。

<?php
$foo = 10;
$bar = 20;
 
function change() {
    global $foo;
	//echo '函數內部$foo = '.$foo.'<br />';
	//如果不把$bar定義為global變量,函數體內是不能訪問$bar的
    $bar = 0;
    $foo++;
}
 
change();
echo $foo, ' ', $bar;
?>

程序輸出 11 20。原因是,方法內部無法訪問$bar變量,所以它的值還是20。使用global之后,可以取得$foo的值,自增后$foo的值就是11。

Global的作用是定義全局變量,但是這個全局變量不是應用于整個網站,而是應用于當前頁面,包括include或require的所有文件。

前言中提到變量的三個基本特性,其中的有一個特性為變量的類型,變量都有特定的類型, 如:字符串、數組、對象等等。編程語言的類型系統可以分為強類型和弱類型兩種:

強類型語言是一旦某個變量被申明為某個類型的變量,則在程序運行過程中,該不能將該變量的類型以外的值賦予給它 (當然并不完全如此,這可能會涉及到類型的轉換,后面的小節會有相應介紹),C/C++/Java等語言就屬于這類。

PHP及Ruby,JavaScript等腳本語言屬于弱類型語言:一個變量可以表示任意的數據類型。

PHP之所以成為一個簡單而強大的語言,很大一部分的原因是它擁有弱類型的變量。 但是有些時候這也是一把雙刃劍,使用不當也會帶來一些問題。就像儀器一樣,越是功能強大, 出現錯誤的可能性也就越大。

在官方的PHP實現內部,所有變量使用同一種數據結構(zval)來保存,而這個結構同時表示PHP中的各種數據類型。 它不僅僅包含變量的值,也包含變量的類型。這就是PHP弱類型的核心。

那zval結構具體是如何實現弱類型的呢,下面我們一起來揭開面紗。

變量存儲結構

PHP在聲明或使用變量的時候,并不需要顯式指明其數據類型。

PHP是弱類型語言,這并不表示PHP沒有類型,在PHP中,存在8種變量類型,可以分為三類

  • * 標量類型:booleanintegerfloat(double)string?
  • * 復合類型:?arrayobject?
  • * 特殊類型:?resourceNULL

官方PHP是用C實現的,而C是強類型的語言,那這是怎么實現PHP中的弱類型的呢?

變量的值存儲到以下所示zval結構體中。 zval結構體定義在Zend/zend.h文件,其結構如下:

typedef struct _zval_struct zval;
...
struct _zval_struct {
    /* Variable information */
    zvalue_value value;     /* value */
    zend_uint refcount__gc;
    zend_uchar type;    /* active type */
    zend_uchar is_ref__gc;
};

PHP使用這個結構來存儲變量的所有數據。和其他編譯性靜態語言不同, PHP在存儲變量時將PHP用戶空間的變量類型也保存在同一個結構體中。這樣我們就能通過這些信息獲取到變量的類型。

zval結構體中有四個字段,其含義分別為:

屬性名含義默認值
refcount__gc表示引用計數1
is_ref__gc表示是否為引用0
value存儲變量的值
type變量具體的類型

在PHP5.3之后,引入了新的垃圾收集機制,引用計數和引用的字段名改為refcount__gc和is_ref__gc。在此之前為refcount和is__ref。

而變量的值則存儲在另外一個結構體zvalue_value中。值存儲見下面的介紹。

PHP用戶空間指的在PHP語言這一層面,而本書中大部分地方都在探討PHP的實現。 這些實現可以理解為內核空間。由于PHP使用C實現,而這個空間的范疇就會限制在C語言。 而PHP用戶空間則會受限于PHP語法及功能提供的范疇之內。 例如有些PHP擴展會提供一些PHP函數或者類,這就是向PHP用戶空間導出了方法或類。

變量類型

zval結構體的type字段就是實現弱類型最關鍵的字段了,type的值可以為: IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一。 從字面上就很好理解,他們只是類型的唯一標示,根據類型的不同將不同的值存儲到value字段。 除此之外,和他們定義在一起的類型還有IS_CONSTANT和IS_CONSTANT_ARRAY。

這和我們設計數據庫時的做法類似,為了避免重復設計類似的表,使用一個標示字段來記錄不同類型的數據。

變量的值存儲

前面提到變量的值存儲在zvalue_value聯合體中,結構體定義如下:

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;

這里使用聯合體而不是用結構體是出于空間利用率的考慮,因為一個變量同時只能屬于一種類型。 如果使用結構體的話將會不必要的浪費空間,而PHP中的所有邏輯都圍繞變量來進行的,這樣的話, 內存浪費將是十分大的。這種做法成本小但收益非常大。

各種類型的數據會使用不同的方法來進行變量值的存儲,其對應賦值方式如下:

1. 一般類型

變量類型?
booleanZVAL_BOOL布爾型/整型的變量值存儲于(zval).value.lval中,其類型也會以相應的IS_*進行存儲。
 Z_TYPE_P(z)=IS_BOOL/LONG;  Z_LVAL_P(z)=((b)!=0); 
integerZVAL_LONG
floatZVAL_DOUBLE
nullZVAL_NULLNULL值的變量值不需要存儲,只需要把(zval).type標為IS_NULL。
 Z_TYPE_P(z)=IS_NULL; 
resourceZVAL_RESOURCE資源類型的存儲與其他一般變量無異,但其初始化及存取實現則不同。
 Z_TYPE_P(z) = IS_RESOURCE;  Z_LVAL_P(z) = l; 

2. 字符串Sting

字符串的類型標示和其他數據類型一樣,不過在存儲字符串時多了一個字符串長度的字段。

struct {
    char *val;
    int len;
} str;

C中字符串是以\0結尾的字符數組,這里多存儲了字符串的長度,這和我們在設計數據庫時增加的冗余字段異曲同工。 因為要實時獲取到字符串的長度的時間復雜度是O(n),而字符串的操作在PHP中是非常頻繁的,這樣能避免重復計算字符串的長度, 這能節省大量的時間,是空間換時間的做法。 這么看在PHP中strlen()函數可以在常數時間內獲取到字符串的長度。 計算機語言中字符串的操作都非常之多,所以大部分高級語言中都會存儲字符串的長度。

3. 數組Array

數組是PHP中最常用,也是最強大變量類型,它可以存儲其他類型的數據,而且提供各種內置操作函數。數組的存儲相對于其他變量要復雜一些, 數組的值存儲在zvalue_value.ht字段中,它是一個HashTable類型的數據。 PHP的數組使用哈希表來存儲關聯數據。哈希表是一種高效的鍵值對存儲結構。PHP的哈希表實現中使用了兩個數據結構HashTable和Bucket。 PHP所有的工作都由哈希表實現,在下節HashTable中將進行哈希表基本概念的介紹以及PHP的哈希表實現。

4. 對象Object

在面向對象語言中,我們能自己定義自己需要的數據類型,包括類的屬性,方法等數據。而對象則是類的一個具體實現。 對象有自身的狀態和所能完成的操作。

PHP的對象是一種復合型的數據,使用一種zend_object_value的結構體來存放。其定義如下:

typedef struct _zend_object_value {
    zend_object_handle handle;  //  unsigned int類型,EG(objects_store).object_buckets的索引
    zend_object_handlers *handlers;
} zend_object_value;

PHP的對象只有在運行時才會被創建,前面的章節介紹了EG宏,這是一個全局結構體用于保存在運行時的數據。 其中就包括了用來保存所有被創建的對象的對象池,EG(objects_store),而object對象值內容的zend_object_handle域就是當前 對象在對象池中所在的索引,handlers字段則是將對象進行操作時的處理函數保存起來。 這個結構體及對象相關的類的結構_zend_class_entry,后面會介紹到。

PHP的弱變量容器的實現方式是兼容并包的形式體現,針對每種類型的變量都有其對應的標記和存儲空間。 使用強類型的語言在效率上通常會比弱類型高,因為很多信息能在運行之前就能確定,這也能幫助排除程序錯誤。 而這帶來的問題是編寫代碼相對會受制約。

PHP主要的用途是作為Web開發語言,在普通的Web應用中瓶頸通常在業務和數據訪問這一層。不過在大型應用下語言也會是一個關鍵因素。 facebook因此就使用了自己的php實現。將PHP編譯為C++代碼來提高性能。不過facebook的hiphop并不是完整的php實現, 由于它是直接將php編譯為C++,有一些PHP的動態特性比如eval結構就無法實現。當然非要實現也是有方法的, hiphop不實現應該也是做了一個權衡。

延伸閱讀

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

  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.bavugt.tw/librarys/veda/detail/1327,歡迎訪問原出處。

不打個分嗎?

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

http://www.bavugt.tw/librarys/veda/detail/1327

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

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

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

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

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

《C程序設計語言(第2版新版)》 克尼漢 (作者), 等 (作者, 譯者), 徐寶文 (譯者)

《C程序設計語言》(第2版新版)是由C語言的設計者Brian W.Kernighan和Dennis M.Ritchie編寫的一部介紹標準C語言及其程序設計方法的權威性經典著作。全面、系統地講述了C語言的各個特性及程序設計的基本方法,包括基本概念,類型和表達式、控制流、函數與程序結構、指針與數組、結構、輸入與輸出、UNIX系統接口、標準庫等內容。

更多計算機寶庫...

有极速快乐十分吗
赤赢配资 极速11选5送彩金 贵州十一选五* 658配资 广东十一选五遗漏 互联网信贷产品有哪些 篮球专家竞彩推荐分 正规股票配资平台 股市上证指数走势图 正规配资平台