天天看點

PHP類的原理二、通路控制的實作三、繼承,多态及抽象類四、對象

一、類的實作

類的内部存儲結構:

1)類分為5種

a. 正常類:type=0

b. 抽象類:type=ZEND_ACC_EXPLICIT_ABSTRACT_CLASS

c. final類:type=ZEND_ACC_FINAL_CLASS

d. 沒有加abstract關鍵字的抽象類:type=ZEND_ACC_IMPLICIT_ABSTRACT_CLASS

e. 接口:type=ZEND_ACC_INTERFACE

定義類時調用了zend_do_begin_class_declaration和zend_do_end_class_declaration函數。

“begin”函數用來處理類名,類的類别和父類,對傳入的類名作一個轉化,統一成小寫,這也是為什麼類名不區分大小的原因。

“end”函數用來處理接口和類的中間代碼。這兩個函數在Zend/zend_complie.c檔案中可以找到其實作。

2)成員變量

成員變量在編譯時已經注冊到了類的結構中。

調用順序為: [zend_do_begin_class_declaration] --> [zend_initialize_class_data] --> [zend_hash_init_ex]

正常的成員變量最後都會注冊到類的 default_properties 字段。

3)靜态變量

類本身的靜态變量存放在類結構的 default_static_members 字段中。

和前面的成員變量一樣,在類聲明時成員方法也通過調用zend_initialize_class_data方法,初始化了整個方法清單所在的HashTable。

靜态變量更新流程圖:

4)成員方法

以HashTable的資料結構存儲了多個zend_function結構體,放在 function_table 字段中。

前面的成員變量一樣,在類聲明時成員方法也通過調用zend_initialize_class_data方法,初始化了整個方法清單所在的HashTable。

5)靜态成員方法

與靜态成員變量不同,靜态成員方法與成員方法都存儲在類結構的 function_table 字段。

類的靜态成員方法可以通過類名直接通路。

6)方法(Function)與函數(Method)的差别

a. 編譯後“挂載”的位置不同

b. 調用方式的實作

7)魔術方法

與普通方法一樣, 隻不過這些類不是存儲在類的函數表, 而是直接存儲在類結構體中。

由ZendVM自動分情境進行調用。

a. __construct:調用入口是new關鍵字對應的ZEND_NEW_SPEC_HANDLER函數。

b. __destruct:對象被顯示銷毀或者腳本關閉時,一般被用于釋放占用的資源。

c. __call:在對對象不存在的方法進行調用時自動執行。

d. __callStatic:在對對象不存在的靜态方法進行調用時自動執行。

8)self、parent和static

self與parent不是關鍵字,而static是關鍵字。

self,parent和static的指向會在執行時進行擷取。

在 zend_fetch_class 執行函數中:

1. self:傳回EG(scope)類

2. parent:傳回EG(scope)->parent類

3. static:傳回EG(called_scope)類。後面的延遲綁定就是“static”的操作。

9)延遲綁定

“延遲綁定”的意思是說,static::不再被解析為定義目前方法所在的類,而是在實際運作時計算的。

延遲綁定的實作關鍵在于static關鍵字。

面向對象的三大特性(封裝、繼承、多态),PHP提供了public、protected及private三個層次通路控制。

文法解析中對應的标記如下:

1. private:ZEND_ACC_PUBLIC     0x100

2. protected:ZEND_ACC_PROTECTED  0x200

3. public:ZEND_ACC_PRIVATE    0x400

在文法解析過程中,通路控制已經存儲在編譯節點中,在編譯具體的類成員時會傳遞給相關的結構。此變量會作為一個參數傳遞給生成中間代碼的函數。

如在解析成員方法時,PHP核心是通過調用 zend_do_begin_function_declaration 函數實作,此函數的第五個參數表示通路控制。

1)繼承

通過對extends關鍵字的詞法分析和文法分析,在Zend/zend_complie.c檔案中 找到繼承實作的編譯函數zend_do_inheritance()。

其調用順序如下: [zend_do_early_binding] --> [do_bind_inherited_class()] --> [zend_do_inheritance()]。

繼承的過程如下:

a. 以類結構為中心,當繼承發生時,程式會先處理所有的接口。

b. 在接口繼承後,程式會合并類的成員變量、屬性、常量、函數等,這些都是HashTable的merge操作。

c. 除了正常的函數合并後,還有魔法方法的合并。

d. 在PHP中private這個成員是會被繼承下來,隻是無法通路。

2)多态

多态即多種形态,相同方法調用實作不同的實作方式。

3)接口

PHP核心對類和接口一視同仁,它們的内部結構一樣,量都是 zend_class_entry 類型。

a. 類的繼承由于是一對一的關系,每個類都有一個parent字段。

b. 接口實作是一個一對多的關系,每個類都會有一個二維指針存放接口的清單 **interfaces ,還有一個存儲接口數的字段 num_interfaces 。

c. 接口在編譯時調用zend_do_implement_interface函數, 此函數會合并接口中的常量清單和方法清單操作,這就是接口中不能有變量卻可以有常量的實作原因。

d. 在接口繼承的過程中有對目前類的接口中是否存在同樣接口的判斷操作,如果已經存在了同樣的接口,則此接口繼承将不會執行。

4)抽象類

在PHP中,抽象類是被abstract關鍵字修飾的類,或者類沒有被聲明為abstract,但是在類中存在抽象成員的類。

在結構體zend_class_entry.ce_flags中展現了差別:

二者對應的值為ZEND_ACC_EXPLICIT_ABSTRACT_CLASS和ZEND_ACC_IMPLICIT_ABSTRACT_CLASS。

類的具體化就是對象,我們常常也說對象是類的執行個體。

對象擁有方法,對象間的通信是通過方法調用,以一種消息傳遞的方式進行。

我們常說的面向對象程式設計(OOP)使得對象具有互動能力的主要模型就是消息傳遞模型。

1)對象的結構

對象的結構體如下:

PHP核心會将所有的對象存放在一個對象清單容器中,這個清單容器是儲存在EG(objects_store)裡的一個全局變量。

上面的handle字段就是這個清單中object_buckets的索引。

當我們需要在PHP中存儲對象的時候, PHP核心會根據handle索引從對象清單中擷取相對應的對象。而擷取的對象有其獨立的結構:

ce是存儲該對象的類結構,properties是一個HashTable,用來存放對象的屬性。

2)對象的建立

在PHP代碼中,對象的建立是通過關鍵字 new 進行的。 一個new操作最終會産生三個opcode:

a. 根據類名擷取存儲類的變量

b. 初始化對象

1. 首先會判斷對象所對應的類是否為可執行個體化的類

2. 如果一切正常,程式會給需要建立的對象存放的ZVAL容器配置設定記憶體

3. 在設定了類型之後,程式會執行zend_object類型的對象的初始化工作,此時調用的函數是zend_objects_new

4. zend_objects_new函數會初始化對象自身的相關資訊,包括對象歸屬于的類,對象實體的存儲索引,對象的相關處理函數

5. 在将對象放入對象池,傳回對象的存放索引後,程式設定對象的處理函數為标準對象處理函數:std_object_handlers

c. 調用構造函數

3)對象池

将PHP核心在運作中存儲所有對象的清單稱之為對象池,即EG(objects_store)。

這個對象池的作用是存儲PHP中間代碼運作階段所有生成的對象。

4)成員變量

對象的成員變量存儲在 properties 參數中。

通過__set來設定變量,通過__get來擷取變量。

參考資料:

    本文轉自 咖啡機(K.F.J)   部落格園部落格,原文連結http://www.cnblogs.com/strick/p/5061794.html:,如需轉載請自行聯系原作者