首頁 後端開發 php教程 王帥:深入PHP核心(一)-弱型變項原理探究

王帥:深入PHP核心(一)-弱型變項原理探究

Aug 08, 2016 am 09:21 AM
nbsp php type zval

PHP是一門簡單而強大的語言,提供了許多Web適用的語言特性,其中就包含了變數弱型,在弱型別機制下,你能夠給一個變數賦任意型別的值。 
PHP的執行是透過Zend Engine(以下簡稱ZE),ZE是使用C編寫,在底層實現了一套弱型機制。 ZE的記憶體管理使用寫入時拷貝、引用計數等最佳化策略,減少再變量賦值時候的記憶體拷貝。

下面不光帶你探索PHP弱類型的原理,也會在寫PHP擴展角度,介紹如何操作PHP的變數。

1. PHP的變數型別

PHP的變數型別有8種:

  • 標準型別:布林boolen,整數型integer,浮點float,字元string
  • 複雜型別:陣列array,特殊類型:資源resource  
  • PHP不會嚴格檢驗變數類型,變數可以不顯示的宣告其類型,而在運作期間直接賦值。也可以將變數自由的轉換類型。如下例,沒有實作宣告的情況下,$i可以賦任意型別的值。

[php] view plaincopy

  1.  php  $i = 1;     // float $i = array(1 , 2, 3);  // array $i = new Exception('test', 123); // object $i = fopen('/tmp/aaa.txt', object $i = fopen('/tmp/aaa. 如果你對弱類型原理理解不深刻,在變數比較時候,會出現「超出預期」的驚喜。
[php] view plaincopy

 PHP 
    $str1
  1.  = null;  $str2 $str1==$str2 ? '相等' : '不相等';  $str3 = '' echo $str3== $str4 ? '相等' : '不相等';   = '0';   echo $str5==$str6 ? '相等'🎠 以上三個結果全部是相等,因為變數比較的時候,PHP內部做了變數轉換。如果希望值和類型同時判斷,請使用三個=(如,$a===0)來判斷。也許你會覺得司空見慣,也許你會覺得很神奇,那請跟我一起深入PHP內核,探索PHP變數原理。 2. 變量的存儲及標準類型介紹PHP的所有變量,都是以結構體zval來實現,在Zend/zend.h中我們能看到zval的定義:[php] view plaincopy
    1. typedef union _zvalue_value {     long lval;             double dval;               /* double v         char *val;         int len;             be set for strings */     } str;                     /* string (always has length) */     HashTable *ht;             /* an array */     zend_object_value obj;     /* stores an object store handle, and handlers */  } zvalue_value;   refcount__gc
    表示引用計數
    1valuetype
    is_ref__gc 表示是否為引用 0
    儲存變數的值  
     

    其中refcount__gc和is_ref__gc表示變數是否為一個引用。 type欄位標識變數的類型,type的值可以是:IS_NULL,IS_BOOL,IS_LONG,IS_FLOAT,IS_STRING,IS_ARRAY,IS_OBJECT,IS_RESOURCE。 PHP根據type的類型,來選擇如何儲存到zvalue_value。 
    zvalue_value能夠實現變數弱型態的核心,定義如下:

    [php] view plaincopy

    1. typedef union _zvalue_value {     long lval;             double dval;               /* doub le value                char *val;         int len;          will always be set for strings */     } str;                     /* string (always has length) */     HashTable *ht;             /* an array */     zend_object_value obj;     /* stores an object store handle, and handlers */  } zvalue_value;   布林型,zval.type
    2. 布爾型,zval.type如果是字串,zval.type=IS_STRING,會讀取zval.value.str,這是一個結構體,儲存了字串指標和長度。
    C語言中,用" plaincopy

    typedefstruct_zend_rsrc_list_entry {     void *ptr;     int 

    其中,ptr是一個指向資源的最終實現的指針,例如一個文件句柄,或者一個資料庫連接結構。 type是一個類型標記,用來區分不同的資源類型。 refcount用於資源的參考計數。

    核心中,資源類型是透過函數ZEND_FETCH_RESOURCE取得的。

    [php] view plaincopy

    1. ZEND_FETCH_RESOURCE(con, type, zval *, default, resource_name, resource_type); 按照現在我們對PHP語言的了解,變數的類型依賴zval.type欄位指示,變數的內容依照zval.type儲存到zval.value。當PHP中需要變數的時候,只需要兩個步驟:把zval.value的值或指標改變,再改變zval.type的類型。不過對於PHP的一些高階變數Array/Object/Resource,變數轉換要進行更多操作。
    變數轉換原理分為3種:

    5.1 標準型別相互轉換

    比較簡單,依照上述的步驟轉換即可。

    5.2 標準型別與資源型別轉換

    資源型別可以理解為是int,較方便轉換標準型別。轉換後資源會被close或回收。

    [php] view plaincopy

     php 

    $var
    1.  = fopen()caaa/ // 資源 # 1 $var = (int) $var; var_dump($var);  // 輸出1 ?>  5.3 305.3 類型標準與複合型態轉換型/電腦型態轉換型/電腦型標準差35.3 305.300002 型式轉換類型/電腦類型。傳回元素個數;轉換bool回傳Array中是否有元素;轉換成string回傳'Array',並拋出warning。 詳情取決於經驗,請閱讀PHP手冊: http://php.net/manual/en/language.types.type-juggling.php 5.4 複雜型別相互轉換array和object可以互轉。如果其它任何類型的值被轉換成對象,將會建立一個內建類別stdClass的實例。
    2. 在我們寫PHP擴充的時候,PHP核心提供了一組函數用於型別轉換:  


    void convert_to_long(zval* p void convert_to_long_base(zval* pzval, int base)

    void convert_to_null(zval* pzval)

    void convert_to_leanf(Fvalzpvalp(Fpvalzp

    void convert_to_object( zval* pzval)void convert_object_to_type(zval* pzval, convert_func_t converter)PHP核提供的一組蛋白質來更容易的存取valval, 內核存取zval容器的API宏存取變數Z_DVAL(zval)
    (zval).value.dval
    Z_STRVAL(zval)

    (zval).value.str.val
    ). lenZ_ARRVAL(zval)Z_TYPE(zval) LVAL_P(zval)( *zval).value.lvalZ_DVAL_P(zval)(*zval).value.dvalZ_STRLEN_P(zval_p)毫(*zval).value.obj.handlers(**zval).value.lval_pp)(**zval).value.lval_pp)Z_STRVAL_PP(zval_pp)
    (zval). value.ht
    .
    (*zval).value.str.len
    Z_OBJ_HT_P(zval_p)
    Z_LVAL_PP(zval_pp)
    (**zval ).value.dval
    (**zval).value.str.val len
    Z_ARRVAL_PP(zval_pp) (**zval). value.ht

    6. 變數的符號表與作用域

    PHP的變數符號表與zval值的映射,是透過HashTable(哈希表,又叫做散列表,下面簡稱HT),HashTable在ZE中廣泛使用,包括常數、變數、函數等語言特性都是HT來組織,在PHP的陣列類型也是透過HashTable來實現。 
    舉例:

    [php] view plaincopy

    1.  php $var = 'Hello World';符號表中,代表$var的類型和值的zval結構儲存在哈希表中。核心透過變數符號表與zval位址的哈希映射,來實現PHP變數的存取。 為什麼要提作用域呢?因為函數內部變數保護。依照作用域PHP的變數分為全域變數和局部變量,每個作用域PHP都會維護一個符號表的HashTable。當在PHP中建立一個函數或類別的時候,ZE會建立一個新的符號表,表示函數或類別中的變數是局部變量,這樣就實現了局部變數的保護--外部無法存取函數內部的變數。當建立一個PHP變數的時候,ZE會分配一個zval,並設定對應type和初始值,把這個變數加入目前作用域的符號表,這樣使用者才能使用這個變數。 
    2. 核心中使用ZEND_SET_SYMBOL來設定變數:

    [php] view plaincopy


    ZEND_SET_SYMBOL( EG(active_symbol_table), 

    "foo"
    1. , foo); [php] view plaincopy
    2. Zend/zend_globals.h  

     struct _zend_   HashTable symbol_table;//全域變項的符號表        HashTable *active_symbol_table;//局部變項的符號表        ///略為@);        //略為略);

      

    1. 在寫PHP擴充時候,可以透過EG宏來存取PHP的變數符號表。 EG(symbol_table)存取全域作用域的變數符號表,EG(active_symbol_table)存取目前作用域的變數符號表,局部變數儲存的是指針,在對HashTable進行操作的時候傳遞給對應函數。 為了更好的理解變數的雜湊表與作用域,舉個簡單的例子:
    2. [php] view plaincopy
    3.  php 
    $temp

     = 

    'global'

    ;

    $temp

     = 
    1. 'active'; } test (); var_dump($temp); ?>   建立函數外的變數$temp,就會將這個它加入全域符號表,同時在全域符號表的HashTable中建立函數外的變數$temp,會把這個它加入全域符號表,同時在全域符號表的HashTable中類型的zval,值為'global'。建立函數test內部變數$temp,會把它加入屬於函數test的符號表,分配字元型zval,值為’active' 。 7. PHP擴充中變數操作建立PHP變數我們可以在擴充中呼叫函數MAKE_STD_ZVAL(pzv)來建立一個PHP可呼叫的變量,MAKE_STD_ZVAL應用到的宏有:] view plaincopy
      1. #define     MAKE_STD_ZVAL(zv)                               ZEND_FAST_ALLOC(z, zval, ZVAL_CACHE_LIST)   ZEND) (type * ) emalloc(sizeof(type))   #define     INIT_PZVAL(z)               0;   

      MAKE_STD_ZVAL(foo)展開後得到:

      [php] view plaincopy

      1. (foo) = (zval *) emalloc(sizeof(zval));   (foo)->refcount__gc =__1;
      可以看出, MAKE_STD_ZVAL做了三件事:分配記憶體、初始化zval結構中的refcount、is_ref。 

      核心中提供一些巨集來簡化我們的操作,可以只用一步便設定好zval的類型和值。

      API Macros for Accessing zval 宏 Z_TYPE_P(pzv) = IS_BOOL; Z_BVAL_P(pzv) = b ? 1 : 0;ZVAL_TRUE(pvz) ZVAL_BOOL(pzv, 0);ZVAL_LONG(pvz, l)(l 是值)Z_TYPE_P(pzv) = IS_LONGZ_LLet z, d )ZVAL_STRINGL(pvz, stret, len,d,nid; pzv) = len; if (dup) { ZVAL_STRINGL(pzv, str,strlen(str), dup);ZVAL_RESOURCE(pvz, res)
      實作方法
      ZVAL_BOOL(pvz)
      FALSE(pvz)
      Z_TYPE_P(pzv) = IS_DOUBLE;Z_LVAL_P(pzv) = d;
          {Z_STRVAL_P(pzv) =estrndup(str, len + 1);}  }else {    pvz, str, len)
      ZVAL_RESOURCE(pvz,res)


      ZVAL_STRINGL(pzv,str,len,dup)中的dup參數

      先闡述一下ZVAL_STRINGL(pzv,str,len,dup); str和len兩個參數很好理解,因為我們知道內核中保存了字串的位址和它的長度,後面的dup的意思其實很簡單,它指明了該字串是否需要被複製。值為 1 將先申請一塊新記憶體並賦值該字串,然後把新記憶體的位址複製給pzv,為 0 時則是直接把str的位址賦值給zval。

      ZVAL_STRINGL與ZVAL_STRING的區別

      如果你想在某一位置截取該字串或已經知道了這個字串的長度,那麼可以使用宏ZVAL_STRINGL(zval, string, length, duplicate) ,它顯式的指定字串長度,而不是使用strlen()。這個宏該字串長度作為參數。但它是二進位安全的,而且速度也比ZVAL_STRING快,因為少了個strlen。 
      ZVAL_RESOURCE約等於ZVAL_LONG

      在章節4中我們說過,PHP中的資源類型的值是一個整數,所以ZVAL_RESOURCE和ZVAL_LONG的工作差不多,只不過它會把zval的類型設為 IS_RESOURCE。

      8. 總結

      PHP的弱類型是透過ZE的zval容器轉換完成,透過哈希表來儲存變數名稱和zval數據,在運作效率方面有一定犧牲。另外因為變數類型的隱性轉換,在開發過程中對變數類型檢測力度不夠,可能會導致問題出現。 

      不過PHP的弱型別、陣列、記憶體託管、擴充等語言特性,非常適合Web開發場景,開發效率很高,能夠加快產品迭代週期。在海量服務中,通常瓶頸存在於資料存取層,而不是語言本身。在實際使用PHP不僅擔任邏輯層和展現層的任務,我們甚至用PHP開發的UDPServer/TCPServer作為資料和cache的中間層。

      以上就介紹了王帥:深入PHP核心(一)-弱型變項原理探究,包含了面向的內容,希望對PHP教學有興趣的朋友有幫助。

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

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

適用於 Ubuntu 和 Debian 的 PHP 8.4 安裝和升級指南 適用於 Ubuntu 和 Debian 的 PHP 8.4 安裝和升級指南 Dec 24, 2024 pm 04:42 PM

PHP 8.4 帶來了多項新功能、安全性改進和效能改進,同時棄用和刪除了大量功能。 本指南介紹如何在 Ubuntu、Debian 或其衍生版本上安裝 PHP 8.4 或升級到 PHP 8.4

我後悔之前不知道的 7 個 PHP 函數 我後悔之前不知道的 7 個 PHP 函數 Nov 13, 2024 am 09:42 AM

如果您是經驗豐富的PHP 開發人員,您可能會感覺您已經在那裡並且已經完成了。操作

如何設定 Visual Studio Code (VS Code) 進行 PHP 開發 如何設定 Visual Studio Code (VS Code) 進行 PHP 開發 Dec 20, 2024 am 11:31 AM

Visual Studio Code,也稱為 VS Code,是一個免費的原始碼編輯器 - 或整合開發環境 (IDE) - 可用於所有主要作業系統。 VS Code 擁有大量針對多種程式語言的擴展,可以輕鬆編寫

在PHP API中說明JSON Web令牌(JWT)及其用例。 在PHP API中說明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

php程序在字符串中計數元音 php程序在字符串中計數元音 Feb 07, 2025 pm 12:12 PM

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

您如何在PHP中解析和處理HTML/XML? 您如何在PHP中解析和處理HTML/XML? Feb 07, 2025 am 11:57 AM

本教程演示瞭如何使用PHP有效地處理XML文檔。 XML(可擴展的標記語言)是一種用於人類可讀性和機器解析的多功能文本標記語言。它通常用於數據存儲

解釋PHP中的晚期靜態綁定(靜態::)。 解釋PHP中的晚期靜態綁定(靜態::)。 Apr 03, 2025 am 12:04 AM

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

什麼是PHP魔術方法(__ -construct,__destruct,__call,__get,__ set等)並提供用例? 什麼是PHP魔術方法(__ -construct,__destruct,__call,__get,__ set等)並提供用例? Apr 03, 2025 am 12:03 AM

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。

See all articles