摘要:父類方法為錯(cuò)誤,成員方法不得被重寫。父子類方法靜態(tài)屬性不一致父類方法為非靜態(tài)而子類的是靜態(tài)或相反,錯(cuò)誤。
1.類的結(jié)構(gòu)
類是編譯階段的產(chǎn)物,而對(duì)象是運(yùn)行時(shí)產(chǎn)生的,它們歸屬于不同階段。編譯完成后我們定義的每個(gè)類都會(huì)生成一個(gè)zend_class_entry,它保存著類的全部信息,在執(zhí)行階段所有類相關(guān)的操作都是用的這個(gè)結(jié)構(gòu),
struct _zend_class_entry { char type; //類的類型:內(nèi)部類ZEND_INTERNAL_CLASS(1)、用戶自定義類ZEND_USER_CLASS(2) zend_string *name; //類名,PHP類不區(qū)分大小寫,統(tǒng)一為小寫 struct _zend_class_entry *parent; //父類 uint32_t ce_flags; //類掩碼,如普通類、抽象類、接口, int default_properties_count; //普通屬性數(shù),包括public、private int default_static_members_count; //靜態(tài)屬性數(shù),static HashTable properties_info; //成員屬性基本信息哈希表,key為成員名,value為zend_property_info zval *default_properties_table; //普通屬性值數(shù)組 zval *default_static_members_table; //靜態(tài)屬性值數(shù)組 HashTable function_table; //成員方法哈希表 HashTable constants_table; //常量哈希表,通過constant定義的 //以下是構(gòu)造函授、析構(gòu)函數(shù)、魔術(shù)方法的指針 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 *__callstatic; union _zend_function *__tostring; union _zend_function *__debugInfo; union _zend_function *serialize_func; union _zend_function *unserialize_func; }
類的編譯:首先為類分配一個(gè)zend_class_entry結(jié)構(gòu),如果沒有繼承類則生成一條類聲明的opcode(ZEND_DECLARE_CLASS),有繼承類則生成兩條opcode(ZEND_FETCH_CLASS、ZEND_DECLARE_INHERITED_CLASS),然后再繼續(xù)編譯常量、成員屬性、成員方法注冊(cè)到zend_class_entry中,最后編譯完成后調(diào)用zend_do_early_binding()進(jìn)行 父子類關(guān)聯(lián) 以及 注冊(cè)到EG(class_table)符號(hào)表。
2.類常量
PHP中可以把在類中始終保持不變的值定義為常量,在定義和使用常量的時(shí)候不需要使用 $ 符號(hào),常量的值必須是一個(gè)定值,它們通過zend_class_entry.constants_table進(jìn)行存儲(chǔ),這是一個(gè)哈希結(jié)構(gòu)
常量的讀取: class my_class { const A1 = "hi"; } echo my_class::A1; 編譯到echo my_class::A1這行時(shí)首先會(huì)嘗試檢索下是否已經(jīng)編譯了my_class,如果能在CG(class_table)中找到,則進(jìn)一步從類的contants_table查找對(duì)應(yīng)的常量,找到的話則會(huì)復(fù)制其value替換常量,簡(jiǎn)單的講就是類似C語言中的宏,編譯時(shí)替換為實(shí)際的值了,而不是在運(yùn)行時(shí)再去檢索。 echo my_class::A1; class my_class { const A1 = "hi"; } 在運(yùn)行時(shí)再去檢索。替換成為實(shí)際的值
3.成員屬性
屬性中的變量可以初始化,但是初始化的值必須是常數(shù),這里的常數(shù)是指PHP腳本在編譯階段時(shí)就可以得到其值,而不依賴于運(yùn)行時(shí)的信息才能求值,比如public $time = time();這樣定義一個(gè)屬性就會(huì)觸發(fā)語法錯(cuò)誤。成員屬性又分為兩類:普通屬性、靜態(tài)屬性,與常量的存儲(chǔ)方式不同,成員屬性的初始化值并不是直接用以"屬性名"作為索引的哈希表存儲(chǔ)的,而是通過數(shù)組保存的
實(shí)際只是成員屬性的VALUE通過數(shù)組存儲(chǔ)的,訪問時(shí)仍然是根據(jù)以"屬性名"為索引的散列表查找具體VALUE的,而這個(gè)散列表是zend_class_entry.properties_info
typedef struct _zend_property_info { uint32_t offset; //普通成員變量的內(nèi)存偏移值,靜態(tài)成員變量的數(shù)組索引 uint32_t flags; //屬性掩碼,如public、private、protected及是否為靜態(tài)屬性 zend_string *name; //屬性名:并不是原始屬性名,private會(huì)在原始屬性名前加上類名,protected則會(huì)加上*作為前綴 zend_class_entry *ce; //所屬類 } zend_property_info;
成員屬性在類編譯階段就已經(jīng)分配了zval,靜態(tài)與普通的區(qū)別在于普通屬性在創(chuàng)建一個(gè)對(duì)象時(shí)還會(huì)重新分配zval,對(duì)象對(duì)普通屬性的操作都是在其自己的空間進(jìn)行的,各對(duì)象隔離,而靜態(tài)屬性的操作始終是在類的空間內(nèi),各對(duì)象共享。
4.成員方法
每個(gè)類可以定義若干屬于本類的函數(shù)(稱之為成員方法),這種函數(shù)與普通的function相同,只是以類的維度進(jìn)行管理,不是全局性的,所以成員方法保存在類中而不是EG(function_table)
成員方法也有靜態(tài)、非靜態(tài)之分,靜態(tài)方法中不能使用$this,因?yàn)槠洳僮鞯淖饔糜蛉慷际穷惖亩皇菍?duì)象的,而非靜態(tài)方法中可以通過$this訪問屬于本對(duì)象的成員屬性
5.對(duì)象的數(shù)據(jù)結(jié)構(gòu)
typedef struct _zend_object zend_object; struct _zend_object { zend_refcounted_h gc; //引用計(jì)數(shù) uint32_t handle; //對(duì)象編號(hào) zend_class_entry *ce; //所屬類 const zend_object_handlers *handlers; //對(duì)象操作處理函數(shù) HashTable *properties; //普通成員屬性哈希表,用于動(dòng)態(tài)屬性 zval properties_table[1]; //普通屬性值數(shù)組 };
對(duì)象的創(chuàng)建:首先是根據(jù)類名在EG(class_table)中查找對(duì)應(yīng)zend_class_entry、然后是創(chuàng)建并初始化一個(gè)對(duì)象、最后是初始化調(diào)用構(gòu)造函數(shù)的zend_execute_data
實(shí)例化一個(gè)對(duì)象: step1: 首先根據(jù)類名去EG(class_table)中找到具體的類,即zend_class_entry step2: 分配zend_object結(jié)構(gòu),一起分配的還有普通非靜態(tài)屬性值的內(nèi)存 step3: 初始化對(duì)象的非靜態(tài)屬性,將屬性值從zend_class_entry淺復(fù)制(寫時(shí)分離)到對(duì)象中 step4: 查找當(dāng)前類是否定義了構(gòu)造函數(shù),如果沒有定義則跳過執(zhí)行構(gòu)造函數(shù)的opcode,否則為調(diào)用構(gòu)造函數(shù)的執(zhí)行進(jìn)行一些準(zhǔn)備工作(分配zend_execute_data) step5: 實(shí)例化完成,返回新實(shí)例化的對(duì)象(如果返回的對(duì)象沒有變量使用則直接釋放掉了)
6.繼承
(a).繼承屬性
屬性從父類復(fù)制到子類 。子類會(huì)將父類的公共、受保護(hù)的屬性值數(shù)組全部合并到子類中,然后將全部屬性的zend_property_info哈希表也合并到子類中
(b).繼承常量
常量的合并策略比較簡(jiǎn)單,如果父類與子類沖突時(shí)用子類的,不沖突時(shí)則將父類的常量合并到子類。
(c).繼承方法
與屬性一樣,子類可以繼承父類的公有、受保護(hù)的方法,方法的繼承比較復(fù)雜,因?yàn)闀?huì)有訪問控制、抽象類、接口、Trait等多種限制條件。實(shí)現(xiàn)上與前面幾種相同,即父類的function_table合并到子類的function_table中。
如果父類是用戶自定義的類,且繼承的方法沒有靜態(tài)變量則不會(huì)硬拷貝,而是增加zend_function的引用計(jì)數(shù)(zend_op_array.refcount)。
子類重寫了父類的方法的檢查規(guī)則 (1)抽象子類的抽象方法與抽象父類的抽象方法沖突: 無法重寫,F(xiàn)atal錯(cuò)誤。 (2)父類方法為final: Fatal錯(cuò)誤,final成員方法不得被重寫。 (3)父子類方法靜態(tài)屬性不一致: 父類方法為非靜態(tài)而子類的是靜態(tài)(或相反),F(xiàn)atal錯(cuò)誤。 (4)抽象子類的抽象方法覆蓋父類非抽象方法: Fatal錯(cuò)誤。 (5)子類方法限制父類方法訪問權(quán)限: Fatal錯(cuò)誤,不允許派生類限制父類方法的訪問權(quán)限,如父類方法為public, 而子類試圖重寫為protected/private。 6)剩余檢查情況: 除了上面5中情形下無法重寫方法,剩下還有一步對(duì)函數(shù)參數(shù)的檢查
7. 動(dòng)態(tài)屬性
class my_class { public $id = 123; public function test($name, $value){ $this->$name = $value; } }
非靜態(tài)成員屬性值在實(shí)例化時(shí)保存到了對(duì)象中,屬性的操作按照編譯時(shí)按順序編好的序號(hào)操作,各對(duì)象對(duì)其非靜態(tài)成員屬性的操作互不干擾,而動(dòng)態(tài)屬性是在運(yùn)行時(shí)創(chuàng)建的,動(dòng)態(tài)創(chuàng)建的屬性保存在zend_object->properties哈希表中
屬性查找:首先按照普通屬性在zend_class_entry.properties_info找,沒有找到再去zend_object->properties繼續(xù)查找
首次創(chuàng)建動(dòng)態(tài)屬性將通過rebuild_object_properties()初始化zend_object->properties哈希表,后面再創(chuàng)建動(dòng)態(tài)屬性直接插入此哈希表,rebuild_object_properties()過程并不僅僅是創(chuàng)建一個(gè)HashTable,還會(huì)將普通成員屬性值插入到這個(gè)數(shù)組中,與動(dòng)態(tài)屬性不同,這里的插入并不是增加原zend_value的refcount,而是創(chuàng)建了一個(gè)IS_INDIRECT類型的zval,指向原屬性值z(mì)val
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/28585.html
1.EG(executor_globals/zend_executor_globals) PHP整個(gè)生命周期中最主要的一個(gè)結(jié)構(gòu),是一個(gè)全局變量,在main執(zhí)行前分配(非ZTS下),直到PHP退出,它記錄著當(dāng)前請(qǐng)求全部的信息 showImg(https://segmentfault.com/img/bV8fW0?w=960&h=777); 2.EX(execute_data/zend_execut...
摘要:引擎中定義了很多內(nèi)部函數(shù)供用戶在中使用,比如等等,除了引擎中定義的內(nèi)部函數(shù),擴(kuò)展中也提供了大量?jī)?nèi)部函數(shù),我們也可以靈活的通過擴(kuò)展自行定制。頭部是一個(gè)與完全相同的結(jié)構(gòu)函數(shù)指針,展開 1.函數(shù)的存儲(chǔ)結(jié)構(gòu) typedef union _zend_function zend_function; union _zend_function { zend_uchar typ...
摘要:但在多線程模式下會(huì)有多個(gè),也就是說每個(gè)線程都有一個(gè)獨(dú)立的內(nèi)存池內(nèi)存分配分配超過內(nèi)存的申請(qǐng),與通用的內(nèi)存申請(qǐng)沒有太大差別,只是將申請(qǐng)的內(nèi)存塊通過單鏈表進(jìn)行了管理。的分配實(shí)際就是分配多個(gè),的分配也是內(nèi)存分配的基礎(chǔ),它是向系統(tǒng)申請(qǐng)內(nèi)存的唯一粒度。 1.Zend內(nèi)存池 內(nèi)存池是內(nèi)核中最底層的內(nèi)存操作,定義了三種粒度的內(nèi)存塊:chunk、page、slot,每個(gè)chunk的大小為2M,page大...
摘要:代碼的編譯的解析過程任務(wù)就是將代碼轉(zhuǎn)化為數(shù)組,代碼里的所有信息都保存在數(shù)組中,然后將數(shù)組交給引擎執(zhí)行,就是內(nèi)核具體執(zhí)行的命令,比如賦值加減操作函數(shù)調(diào)用等,每一條都對(duì)應(yīng)一個(gè)處理,這些是提前定義好的函數(shù)。 1.PHP代碼的編譯 PHP的解析過程任務(wù)就是將PHP代碼轉(zhuǎn)化為opcode數(shù)組,代碼里的所有信息都保存在opcode數(shù)組中,然后將opcode數(shù)組交給zend引擎執(zhí)行,opcode就是...
摘要:插入一個(gè)元素時(shí)先將元素按先后順序插入數(shù)組,位置是,再根據(jù)的哈希值映射到散列表中的某個(gè)位置,將存入這個(gè)位置查找時(shí)先在散列表中映射到,得到在數(shù)組的位置,再從數(shù)組中取出元素。目前只有兩種類型會(huì)使用這種機(jī)制。 1.變量結(jié)構(gòu) typedef struct _zval_struct zval; typedef union _zend_value { zend_long ...
閱讀 576·2023-04-26 02:58
閱讀 2316·2021-09-27 14:01
閱讀 3625·2021-09-22 15:57
閱讀 1185·2019-08-30 15:56
閱讀 1055·2019-08-30 15:53
閱讀 803·2019-08-30 15:52
閱讀 658·2019-08-26 14:01
閱讀 2175·2019-08-26 13:41