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

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
使用數據庫存儲會話的優點是什麼?使用數據庫存儲會話的優點是什麼?Apr 24, 2025 am 12:16 AM

使用數據庫存儲會話的主要優勢包括持久性、可擴展性和安全性。 1.持久性:即使服務器重啟,會話數據也能保持不變。 2.可擴展性:適用於分佈式系統,確保會話數據在多服務器間同步。 3.安全性:數據庫提供加密存儲,保護敏感信息。

您如何在PHP中實現自定義會話處理?您如何在PHP中實現自定義會話處理?Apr 24, 2025 am 12:16 AM

在PHP中實現自定義會話處理可以通過實現SessionHandlerInterface接口來完成。具體步驟包括:1)創建實現SessionHandlerInterface的類,如CustomSessionHandler;2)重寫接口中的方法(如open,close,read,write,destroy,gc)來定義會話數據的生命週期和存儲方式;3)在PHP腳本中註冊自定義會話處理器並啟動會話。這樣可以將數據存儲在MySQL、Redis等介質中,提升性能、安全性和可擴展性。

什麼是會話ID?什麼是會話ID?Apr 24, 2025 am 12:13 AM

SessionID是網絡應用程序中用來跟踪用戶會話狀態的機制。 1.它是一個隨機生成的字符串,用於在用戶與服務器之間的多次交互中保持用戶的身份信息。 2.服務器生成並通過cookie或URL參數發送給客戶端,幫助在用戶的多次請求中識別和關聯這些請求。 3.生成通常使用隨機算法保證唯一性和不可預測性。 4.在實際開發中,可以使用內存數據庫如Redis來存儲session數據,提升性能和安全性。

您如何在無狀態環境(例如API)中處理會議?您如何在無狀態環境(例如API)中處理會議?Apr 24, 2025 am 12:12 AM

在無狀態環境如API中管理會話可以通過使用JWT或cookies來實現。 1.JWT適合無狀態和可擴展性,但大數據時體積大。 2.Cookies更傳統且易實現,但需謹慎配置以確保安全性。

您如何防止與會議有關的跨站點腳本(XSS)攻擊?您如何防止與會議有關的跨站點腳本(XSS)攻擊?Apr 23, 2025 am 12:16 AM

要保護應用免受與會話相關的XSS攻擊,需採取以下措施:1.設置HttpOnly和Secure標誌保護會話cookie。 2.對所有用戶輸入進行輸出編碼。 3.實施內容安全策略(CSP)限制腳本來源。通過這些策略,可以有效防護會話相關的XSS攻擊,確保用戶數據安全。

您如何優化PHP會話性能?您如何優化PHP會話性能?Apr 23, 2025 am 12:13 AM

优化PHP会话性能的方法包括:1.延迟会话启动,2.使用数据库存储会话,3.压缩会话数据,4.管理会话生命周期,5.实现会话共享。这些策略能显著提升应用在高并发环境下的效率。

什麼是session.gc_maxlifetime配置設置?什麼是session.gc_maxlifetime配置設置?Apr 23, 2025 am 12:10 AM

theSession.gc_maxlifetimesettinginphpdeterminesthelifespanofsessiondata,setInSeconds.1)它'sconfiguredinphp.iniorviaini_set().2)abalanceisesneededeededeedeedeededto toavoidperformance andunununununexpectedLogOgouts.3)

您如何在PHP中配置會話名?您如何在PHP中配置會話名?Apr 23, 2025 am 12:08 AM

在PHP中,可以使用session_name()函數配置會話名稱。具體步驟如下:1.使用session_name()函數設置會話名稱,例如session_name("my_session")。 2.在設置會話名稱後,調用session_start()啟動會話。配置會話名稱可以避免多應用間的會話數據衝突,並增強安全性,但需注意會話名稱的唯一性、安全性、長度和設置時機。

See all articles

熱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

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

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具