亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

變量改變時PHP內核做了哪些工作

發布時間:2021-11-25 15:16:06 來源:億速云 閱讀:121 作者:iii 欄目:編程語言

本篇內容主要講解“變量改變時PHP內核做了哪些工作”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“變量改變時PHP內核做了哪些工作”吧!

看下面的內容之前先對zval這個結構體做個了解

typedef struct _zval_struct {     zvalue_value value;     zend_uint refcount;     zend_uchar type;     zend_uchar is_ref; } zval;

zval結構體中共有4個元素,value是一個聯合體,用來真正的存儲zval的值,refcount用來計數該zval被多少個變量使用,type表示zval所存儲的數據類型,is_ref用來標志該zval是否被引用。

引用計數

<php     $a = 'Hello World';     $b = $a;     unset($a); >

我們一起來剖析下上面這段代碼:

  • $a = 'Hello World';首先這句代碼被執行,內核創建一個變量,并分配12字節的內存去存儲字符串'Hello World'和末尾的NULL。

  • $b = $a;接著執行這句代碼,執行這句的時候內核里面發生了什么呢?

    • $a所指向的zval中的refcount進行加1操作。

    • 將變量$b指向$a所指向的zval。
      在內核中大概是這樣的,其中active_symbol_table是當前的變量符號表


    1.     zval *helloval; 

    2.     MAKE_STD_ZVAL(helloval); 

    3.     ZVAL_STRING(helloval, "Hello World", 1); 

    4.     zend_hash_add(EG(active_symbol_table), "a", sizeof("a"), 

    5.                                         &helloval, sizeof(zval*), NULL); 

    6.     ZVAL_ADDREF(helloval); 

    7.     zend_hash_add(EG(active_symbol_table), "b", sizeof("b"), 

    8.                                         &helloval, sizeof(zval*), NULL); 

  • unset($a);這句代碼執行后,內核會將a對應的zval結構體中的refcount計數減一,b還和原來一樣

寫時復制

<?php     $a = 1;     $b = $a;     $b += 5; ?>

上面這段代碼執行完之后,一般肯定希望$a=1,$b=6,但是如果像引用計數那樣,$a$b指向相同的zval,修改$b之后$a不是也變了?
這個具體是怎么實現的呢,我們一起來看下:

  • $a = 1;內核創建一個zval,并分配4個字節存儲數字1。

  • $b = $a;這一步和引用計數中的第二步一樣,將$b指向和$a相同的zval,并將zval中的引用計數值refcount加1。

  • $b += 5;關鍵是這一步,這一步驟發生了什么呢,怎么確保修改之后不影響$a

    • 其實Zend內核在改變zval之前都會去進行get_var_and_separete操作,如果recfount>1,就需要分離就創建新的zval返回,否則直接返回變量所指向的zval,下面看看如何分離產生新的zval。

    • 復制一個和$b所指向zval一樣的zval。

    • $b所指向的zval中的refcount計數減1。

    • 初始化生成的新zval,設置refcount=1,is_ref=0。

    • $b指向新生成的zval。

    • 對新生成的zval進行操作,這就是寫時復制。
      下面看看內核中分離時的主要代碼:


    1. zval *get_var_and_separate(char *varname, int varname_len TSRMLS_DC) 

    2.     zval **varval, *varcopy; 

    3.     if (zend_hash_find(EG(active_symbol_table), 

    4.                     varname, varname_len + 1, (void**)&varval) == FAILURE) { 

    5.     /* Variable doesn't actually exist  fail out */ 

    6.     return NULL; 

    7. if ((*varval)->is_ref || (*varval)->refcount < 2) { 

    8.     /* varname is the only actual reference, 

    9.     * or it's a full reference to other variables 

    10.     * either way: no separating to be done 

    11.     */ 

    12.     return *varval; 

    13. /* Otherwise, make a copy of the zval* value */ 

    14. MAKE_STD_ZVAL(varcopy); 

    15. varcopy = *varval; 

    16. /* Duplicate any allocated structures within the zval* */ 

    17. zval_copy_ctor(varcopy); 

    18.  

    19. /* Remove the old version of varname 

    20. * This will decrease the refcount of varval in the process 

    21. */ 

    22. zend_hash_del(EG(active_symbol_table), varname, varname_len + 1); 

    23.  

    24. /* Initialize the reference count of the 

    25. * newly created value and attach it to 

    26. * the varname variable 

    27. */ 

    28. varcopy->refcount = 1; 

    29. varcopy->is_ref = 0; 

    30. zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, 

    31.                                         &varcopy, sizeof(zval*), NULL); 

    32. /* Return the new zval* */ 

    33. return varcopy; 

寫時改變

<?php     $a = 1;     $b = &$a;     $b += 5; ?>

上面這段代碼執行完之后一般希望是:$a == $b == 1。這個又是怎么實現的呢?

  • $a = 1;這一步驟和寫時復制中的***步一樣。

  • $b = &$a;這一步驟內核會將$b指向$a所指向的zval,將zval中的refcount加1,并將zval中的is_ref置為1。

  • $b += 5;這一步驟和寫時復制中的第三步驟一樣,但是內核中發生的事情卻不一樣。

    • 內核看到$b進行變化的時候,也會執行get_var_and_separate函數,看是否需要分離。

    • 如果(*varval)->is_ref的話也會直接返回$b所指向的zval,不去分離產生新的zval,不管zval的refcount是否>1。

    • 這時候再去修改$b值,$a的值也就改變了,因為他們指向相同的zval。

分離的問題

說道現在聰明的你可能已經看出點問題了,如果一個zval結構體既有refcount計數又有is_ref引用這個時候怎么辦?

<?php     $a = 1;     $b = $a;     $c = &$a; ?>

如果出現上面這種情況的時候,如果$a、$b、$c指向同一個zval結構體,進行改變的時候Zend到底去聽誰的?其實這個地方不會指向同一個zval了。
如果對一個is_ref = 0 && refcount >1的zval進行寫時改變這種賦值形式(就是引用賦值)的時候,Zend會將等號右邊的變量分離出來一個新的zval,
對這個zval進行初始化,對之前的zval的refcount進行減1操作,讓等號左邊的變量指向這個新的zval,refcount進行加1操作,is_ref=1。看看下面這張圖片

變量改變時PHP內核做了哪些工作

<?php     $a = 1;     $b = &$a;     $c = $a; ?>

上面這又是另外一種情況,在is_ref = 1的情況下,試圖單純的進行refcount+1操作的時候會分離出來一個新的zval給等號左邊的變量,并初始化他,看看下面這張圖片

變量改變時PHP內核做了哪些工作

到此,相信大家對“變量改變時PHP內核做了哪些工作”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

php
AI

甘泉县| 涿州市| SHOW| 霍山县| 胶州市| 邹平县| 茌平县| 无锡市| 同仁县| 民权县| 商都县| 浦县| 定兴县| 桐乡市| 镇沅| 武邑县| 桂林市| 平山县| 沛县| 株洲市| 水富县| 荔波县| 日喀则市| 定结县| 久治县| 渭源县| 新晃| 托克逊县| 花莲市| 晴隆县| 巴东县| 锡林浩特市| 乌拉特后旗| 加查县| 通山县| 龙井市| 延吉市| 西吉县| 漳浦县| 白朗县| 军事|