PHP类的原理
一、類的實現
類的內部存儲結構:
struct _zend_class_entry {char type; // 類型:ZEND_INTERNAL_CLASS / ZEND_USER_CLASSchar *name;// 類名稱zend_uint name_length; // 即sizeof(name) - 1struct _zend_class_entry *parent; // 繼承的父類int refcount; // 引用數 zend_bool constants_updated;zend_uint ce_flags; // ZEND_ACC_IMPLICIT_ABSTRACT_CLASS: 類存在abstract方法// ZEND_ACC_EXPLICIT_ABSTRACT_CLASS: 在類名稱前加了abstract關鍵字// ZEND_ACC_FINAL_CLASS// ZEND_ACC_INTERFACEHashTable function_table; // 方法HashTable default_properties; // 默認屬性HashTable properties_info; // 屬性信息HashTable default_static_members;// 類本身所具有的靜態變量HashTable *static_members; // type == ZEND_USER_CLASS時,取&default_static_members;// type == ZEND_INTERAL_CLASS時,設為NULLHashTable constants_table; // 常量struct _zend_function_entry *builtin_functions;// 方法定義入口union _zend_function *constructor;union _zend_function *destructor;union _zend_function *clone;/* 魔術方法 */union _zend_function *__get;union _zend_function *__set;union _zend_function *__unset;union _zend_function *__isset;union _zend_function *__call;union _zend_function *__tostring;union _zend_function *serialize_func;union _zend_function *unserialize_func;zend_class_iterator_funcs iterator_funcs;// 迭代/* 類句柄 */zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC);zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object,intby_ref TSRMLS_DC);/* 類聲明的接口 */int(*interface_gets_implemented)(zend_class_entry *iface,zend_class_entry *class_type TSRMLS_DC);/* 序列化回調函數指針 */int(*serialize)(zval *object, unsignedchar**buffer, zend_uint *buf_len,zend_serialize_data *data TSRMLS_DC);int(*unserialize)(zval **object, zend_class_entry *ce, constunsignedchar*buf,zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);zend_class_entry **interfaces; // 類實現的接口zend_uint num_interfaces; // 類實現的接口數char *filename; // 類的存放文件地址 絕對地址zend_uint line_start; // 類定義的開始行zend_uint line_end; // 類定義的結束行char *doc_comment;zend_uint doc_comment_len;struct _zend_module_entry *module; // 類所在的模塊入口:EG(current_module) };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自動分情境進行調用。
struct _zend_class_entry {...//構造方法 __constructunion _zend_function *constructor;//析構方法 __destructunion _zend_function *destructor;//克隆方法 __cloneunion _zend_function *clone;union _zend_function *__get;union _zend_function *__set;union _zend_function *__unset;union _zend_function *__isset;union _zend_function *__call;union _zend_function *__callstatic;union _zend_function *__tostring;//序列化union _zend_function *serialize_func;//反序列化union _zend_function *unserialize_func;... }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關鍵字。
class ParentBase {static $property = 'Parent Value';public static function render() {//return self::$property;return static::$property;} }class Descendant extends ParentBase {static $property = 'Descendant Value'; } echo Descendant::render(); //Descendant Value?
二、訪問控制的實現
面向對象的三大特性(封裝、繼承、多態),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?函數實現,此函數的第五個參數表示訪問控制。
if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) {//公有方法,可以訪問 } else if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {// 私有方法,報錯 } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {// 保護方法,報錯 }?
三、繼承,多態及抽象類
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)多態
多態即多種形態,相同方法調用實現不同的實現方式。
interface Animal {public function run(); }class Dog implements Animal {public function run() {echo 'dog run';} }class Cat implements Animal{public function run() {echo 'cat run';} }?
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)對象的結構
對象的結構體如下:
typedef struct _zend_object_value {zend_object_handle handle;// unsigned int類型,EG(objects_store).object_buckets的索引zend_object_handlers *handlers; } zend_object_value;PHP內核會將所有的對象存放在一個對象列表容器中,這個列表容器是保存在EG(objects_store)里的一個全局變量。
上面的handle字段就是這個列表中object_buckets的索引。
當我們需要在PHP中存儲對象的時候,?PHP內核會根據handle索引從對象列表中獲取相對應的對象。而獲取的對象有其獨立的結構:
typedef struct _zend_object {zend_class_entry *ce;HashTable *properties;HashTable *guards; /* protects from __get/__set ... recursion */ } zend_object;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來獲取變量。
?
?
參考資料:
http://php-internals.com/book/?p=chapt05/05-01-class-struct????類的結構和實現
http://php-internals.com/book/?p=chapt05/05-02-class-member-variables-and-methods???? 類的成員變量及方法
http://php-internals.com/book/?p=chapt05/05-04-class-inherit-abstract???? 類的繼承,多態及抽象類
http://php-internals.com/book/?p=chapt05/05-05-class-magic-methods-latebinding???? 魔術方法,延遲綁定及靜態成員
http://php-internals.com/book/?p=chapt05/05-07-class-object????對象
http://php-internals.com/book/?p=chapt05/05-06-class-reserved-and-special-classes???? PHP保留類及特殊類
本文轉自 咖啡機(K.F.J) ? 博客園博客,原文鏈接http://www.cnblogs.com/strick/p/5061794.html:,如需轉載請自行聯系原作者
總結
- 上一篇: 分布式缓存系统之Memcached
- 下一篇: 图片无损压缩