首頁  >  文章  >  後端開發  >  PHP7核心剖析3之 變數

PHP7核心剖析3之 變數

不言
不言原創
2018-04-13 14:54:041492瀏覽

本篇文章的內容介紹的是關於PHP7核心剖析1之CGI與FastCGI ,現在分享給大家,有需要的朋友可以參考一下

1.變數結構

typedef struct _zval_struct     zval;

typedef union _zend_value {
    zend_long         lval;    //int整形
    double            dval;    //浮点型
    zend_string      *str;     //string字符串
    zend_array       *arr;     //array数组
    zend_object      *obj;     //object对象
    zend_resource    *res;     //resource资源类型
    zend_reference   *ref;     //引用类型,通过&$var_name定义的
} zend_value;

struct _zval_struct {
    zend_value        value; //变量实际的value
    union {
        struct {
            ZEND_ENDIAN_LOHI_4( 
                zend_uchar    type,         //变量类型
                zend_uchar    type_flags,  //类型掩码,不同的类型会有不同的几种属性,内存管理会用到
                zend_uchar    const_flags,
                zend_uchar    reserved)
        } v;
        uint32_t type_info; //上面4个值的组合值,可以直接根据type_info取到4个对应位置的值
    } u1;
    union {
        uint32_t     var_flags;
        uint32_t     next;  //哈希表中解决哈希冲突时用到   
        uint32_t     cache_slot;   
        uint32_t     lineno;    
        uint32_t     num_args;    
        uint32_t     fe_pos;  
        uint32_t     fe_iter_idx;
    } u2;
};


2.變數類型

#define IS_UNDEF                    0
#define IS_NULL                     1
#define IS_FALSE                    2
#define IS_TRUE                     3
#define IS_LONG                     4
#define IS_DOUBLE                   5
#define IS_STRING                   6
#define IS_ARRAY                    7
#define IS_OBJECT                   8
#define IS_RESOURCE                 9
#define IS_REFERENCE                10
其中undef、true、false、null沒有value,直接依照type區分,而long 、double的值則直接存在value中,其他型別為指標


#3.字串##

typedef struct _zend_string   zend_string;

struct _zend_string {
    zend_refcounted_h gc;  //变量引用信息,比如当前value的引用数
    size_t            len;  //字符串长度,通过这个值保证二进制安全
    char              val[1]; //字符串内容,变长struct,分配时按len长度申请内存
};


4.陣列

typedef struct _zend_array HashTable;
typedef struct _zend_array zend_array;

typedef struct _Bucket {
    zval              val; //存储的具体value,这里嵌入了一个zval,而不是一个指针
    zend_ulong        h;   //哈希值
    zend_string      *key; //key值
} Bucket;

struct _zend_array {
    zend_refcounted_h gc; //引用计数信息
    uint32_t          nTableMask;  //计算bucket索引时的掩码,用于散列表的计算nIndex
    Bucket           *arData;     //bucket数组
    uint32_t          nNumUsed;   //已用bucket数
    uint32_t          nNumOfElements; //已有元素数,nNumOfElements <= nNumUsed,因为删除的并不是直接从arData中移除
    uint32_t          nTableSize; //数组的大小,为2^n,默认为8
    uint32_t          nInternalPointer; //数值索引,用于HashTable遍历
    zend_long         nNextFreeElement;//下一个空闲可用位置的数字索引
    dtor_func_t       pDestructor;//析构函数,销毁时调用的函数指针
};

HashTable主要依賴arData實作元素的儲存、索引。插入一個元素時先將元素依先後順序插入Bucket數組,位置是idx,再根據key的雜湊值映射到散列表中的某個位置nIndex,將idx存入這個位置;查找時先在散列表中映射到nIndex,得到value在Bucket數組的位置idx,再從Bucket數組中取出元素。
$arr["a"] = 1;
$arr["b"] = 2;
$arr["c"] = 3;
$arr["d"] = 4;

unset($arr["c"]);

PHP7核心剖析3之 變數

哈希碰撞:當發生衝突時將原value的位置儲存到新value的zval.u2.next中,然後將新value代替原value位置
擴容:PHP散列表的大小為2^n,插入時如果容量不夠則首先檢查已刪除元素所佔比例,如果達到閾值,則將已刪除元素移除,重建索引,如果未到閾值則進行擴容操作,擴大為目前大小的2倍,將目前Bucket數組複製到新的空間,然後重建索引。
重建散列表:當刪除元素達到一定數量或擴容後都需要重建散列表,因為value在Bucket位置移動了或哈希數組nTableSize變化了導致key與value的映射關係改變,重建過程實際上就是遍歷Bucket數組中的value,然後重新計算映射值更新到散列表,移除已刪除的value,將後面未刪除的value依次前移


5.引用

引用是PHP中比較特殊的一種類型,它實際上是指向另外一個PHP變量,對它的修改會直接改動實際指向的zval,可以簡單的理解為C中的指針,在PHP中透過&運算子產生一個引用變量,也就是說不管以前的型別是什麼,&首先會建立一個zend_reference結構,其內嵌了一個zval,這個zval的value指向原來zval的value(如果是布林、整形、浮點則直接複製原來的值),然後將原zval的型別修改為IS_REFERENCE,原zval的value指向新建立的zend_reference結構。
typedef struct _zend_reference  zend_reference;

struct _zend_reference {
    zend_refcounted_h gc;
    zval              val;
};


6.引用計數#

typedef struct _zend_refcounted_h {
    uint32_t         refcount;         
    union {
        struct {
            ZEND_ENDIAN_LOHI_3(
                zend_uchar    type,
                zend_uchar    flags,   
                uint16_t      gc_info)  
        } v;
        uint32_t type_info;
    } u;
} zend_refcounted_h;
$a = "time:" . time();   //$a       ->  zend_string_1(refcount=1)
$b = $a;                 //$a,$b    ->  zend_string_1(refcount=2)
$c = $b;                 //$a,$b,$c ->  zend_string_1(refcount=3)

unset($b);               //$b = IS_UNDEF  $a,$c ->  zend_string_1(refcount=2)

並不是所有的資料型別都會用到引用計數,long、double直接都是硬拷貝,只有value是指標的那幾種型別(除interned string,immutable array)才能使用到引用計數。可由zval.u1.type_flag判斷


7.寫入時複製

$a = array(1,2);
$b = &$a;
$c = $a;

//发生分离
$b[] = 3;

PHP7核心剖析3之 變數


PHP7核心剖析3之 變數 ##事實上只有string、array兩種支援,

PHP7核心剖析3之 變數#8.垃圾回收

PHP變數的回收主要有兩種:主動銷毀、自動銷毀。主動銷毀指的是unset ,而自動銷毀就是PHP的自動管理機制,在return時減掉局部變數的refcount,即使沒有明確的return,PHP也會自動給加上這個操作,另外一個就是寫時複製時會斷開原來value的指向,這時候也會檢查斷開後舊value的refcount。

$a = [1];
$a[] = &$a;

unset($a);
unset($a)之前引用關係:

unset($a)之後:

############相關推薦:################PHP7核心剖析1之CGI與FastCGI####### #########PHP7核心剖析2之I/O模型######

以上是PHP7核心剖析3之 變數的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn