今天小編就為大家?guī)硪黄榻BPHP內(nèi)核Object的文章。小編覺得挺實(shí)用的,為此分享給大家做個參考。一起跟隨小編過來看看吧。
站在用戶的角度思考問題,與客戶深入溝通,找到嘉興網(wǎng)站設(shè)計(jì)與嘉興網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站建設(shè)、成都做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、空間域名、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋嘉興地區(qū)。
PHP5中,對象的定義如下:
typedef struct _zend_object { zend_class_entry *ce; HashTable *properties; zval **properties_table; HashTable *guards; } zend_object;
其中ce存儲了這個對象所屬的類, 關(guān)于properties_table和properties, properties_table是申明的屬性,properties是動態(tài)屬性,也就是比如:
<?php class Foo { public $a = 'defaul property'; } $a = New Foo(); $a->b = 'dynamic property';
因?yàn)樵贔oo的定義中,我們申明了public $a, 那么$a就是已知的申明屬性,它的可見性,包括在properties_table中存儲的位置都是在申明后就確定的。
而$a->b, 是我們動態(tài)給添加的屬性,它不屬于已經(jīng)申明的屬性,這個會存儲在properties中。
其實(shí)從類型上也能看出來, properties_table是zval*的數(shù)組,而properties是Hashtable。
guards主要用在魔術(shù)方法調(diào)用的時候嵌套保護(hù), 比如__isset/__get/__set。
總體來說, zend_object(以下簡稱object)在PHP5中其實(shí)是一種相對特殊的存在, 在PHP5中,只有resource和object是引用傳遞,也就是說在賦值,傳遞的時候都是傳遞的本身,也正因?yàn)槿绱耍琌bject和Resource除了使用了Zval的引用計(jì)數(shù)以外,還采用了一套獨(dú)立自身的計(jì)數(shù)系統(tǒng)。
這個我們從zval中也能看出object和其他的類似字符串的的不同:
typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value;
對于字符串和數(shù)組,zval中都直接保存它們的指針,而對于object卻是一個zend_object_value的結(jié)構(gòu)體:
typedef unsigned int zend_object_handle;
typedef struct _zend_object_value { zend_object_handle handle; const zend_object_handlers *handlers; } zend_object_value;
真正獲取對象是需要通過這個zend_object_handle,也就是一個int的索引去全局的object buckets中查找:
ZEND_API void *zend_object_store_get_object_by_handle(zend_object_handle handle TSRMLS_DC) { return EG(objects_store).object_buckets[handle].bucket.obj.object; }
而EG(objects_store).object_buckets則是一個數(shù)組,保存著:
typedef struct _zend_object_store_bucket { zend_bool destructor_called; zend_bool valid; zend_uchar apply_count; union _store_bucket { struct _store_object { void *object; zend_objects_store_dtor_t dtor; zend_objects_free_object_storage_t free_storage; zend_objects_store_clone_t clone; const zend_object_handlers *handlers; zend_uint refcount; gc_root_buffer *buffered; } obj; struct { int next; } free_list; } bucket; } zend_object_store_bucket;
其中,zend_object_store_bucket.bucket.obj.object才保存著真正的zend_object的指針,注意到此處是void *, 這是因?yàn)槲覀兒芏鄶U(kuò)展的自定義對象,也是可以保存在這里的。
另外我們也注意到zend_object_store_bueckt.bucket.obj.refcount, 這個既是我剛剛講的object自身的引用計(jì)數(shù),也就是zval有一套自己的引用計(jì)數(shù),object也有一套引用計(jì)數(shù)。
<?php $o1 = new Stdclass(); //o1.refcount == 1, object.refcount == 1 $o2 = $o1; //o1.refcount == o2.refcoun == 2; object.refcount = 1; $o3 = &$o2; //o3.isref == o2.isref==1 //o3.refcount == o2.refcount == 2 //o1.isref == 0; o1.refcount == 1 //object.refcount == 2
這樣,可以讓object可以保證不同于普通的zval的COW機(jī)制,可以保證object可以全局傳引用。
可見,從一個zval到取到實(shí)際的object,我們需要首先獲取zval.value.obj.handle, 然后拿著這個索引再去EG(objects_store)查詢,效率比較低下。
對于另外一個常見的操作,就是獲取一個zval對象的類的時候,我們也需要需要調(diào)用一個函數(shù):
#define Z_OBJCE(zval) zend_get_class_entry(&(zval) TSRMLS_CC)
PHP7
到了PHP7, zval中直接保存了zend_object對象的指針:
struct _zend_object { zend_refcounted_h gc; uint32_t handle; zend_class_entry *ce; const zend_object_handlers *handlers; HashTable *properties; zval properties_table[1]; };
而EG(objects_store)也只是簡單的保存了一個zend_object**等指針:
typedef struct _zend_objects_store { zend_object **object_buckets; uint32_t top; uint32_t size; int free_list_head; } zend_objects_store;
而對于前面的COW的例子,對于IS_OBJECT來說, 用IS_TYPE_COPYABLE來區(qū)分,也就是,當(dāng)發(fā)生COW的時候,如果這個類型沒有設(shè)置 IS_TYPE_COPYABLE,那么就不會發(fā)生"復(fù)制".
#define IS_ARRAY_EX (IS_ARRAY | ((IS_TYPE_REFCOUNTED | IS_TYPE_COLLECTABLE | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT)) #define IS_OBJECT_EX (IS_OBJECT | ((IS_TYPE_REFCOUNTED | IS_TYPE_COLLECTABLE) << Z_TYPE_FLAGS_SHIFT))
如上,大家可以看到對于ARRAY來說定義了IS_TYPE_REFCOUNTED, IS_TYPE_COLLECTABLE和IS_TYPE_COPYABLE, 但是對于OBJECT, 則缺少了IS_TYPE_COPYABLE.
在SEPARATE_ZVAL中:
#define SEPARATE_ZVAL(zv) do { \ zval *_zv = (zv); \ if (Z_REFCOUNTED_P(_zv) || \ Z_IMMUTABLE_P(_zv)) { \ if (Z_REFCOUNT_P(_zv) > 1) { \ if (Z_COPYABLE_P(_zv) || \ Z_IMMUTABLE_P(_zv)) { \ if (!Z_IMMUTABLE_P(_zv)) { \ Z_DELREF_P(_zv); \ } \ zval_copy_ctor_func(_zv); \ } else if (Z_ISREF_P(_zv)) { \ Z_DELREF_P(_zv); \ ZVAL_DUP(_zv, Z_REFVAL_P(_zv)); \ } \ } \ } \ } while (0)
如果不是Z_COPYABLE_P, 那么就不發(fā)生寫時分離。
這里有的同學(xué)會問,那既然已經(jīng)在zval中直接保存了zend_object*了,那為啥還需要EG(objects_store)呢?
這里有2個主要原因:
1. 我們需要在PHP請求結(jié)束的時候保證所有的對象的析構(gòu)函數(shù)都被調(diào)用,因?yàn)閛bject存在循環(huán)引用的情況,那如何快速的遍歷所有存活的對象呢? EG(objects_store)是一個很不錯的選擇。
2. 在PHPNG開發(fā)的時候,為了保證最大向后兼容,我們還是需要保證獲取一個對象的handle的接口, 并且這個handle還是要保證原有的語義。
但實(shí)際上來說呢, 其實(shí)EG(objects_store)已經(jīng)沒啥太大的用處了, 我們是可以在將來去掉它的。
好,接下來出現(xiàn)了另外一個問題,我們再看看zend_object的定義, 注意到末尾的properties_table[1], 也就是說,我們現(xiàn)在會把object的屬性跟對象一起分配內(nèi)存。這樣做對緩存友好。 但帶來一個變化就是, zend_object這個結(jié)構(gòu)體現(xiàn)在是可能變長的。
那在當(dāng)時寫PHPNG的時候就給我?guī)砹艘粋€問題, 在PHP5時代,很多的自定義對象是這么定義的(MySQLi為例):
typedef struct _mysqli_object { zend_object zo; void *ptr; HashTable *prop_handler; } mysqli_object; /* extends zend_object */
也就是說zend_object都在自定義的內(nèi)部類的頭部,這樣當(dāng)然有一個好處是可以很方便的做cast, 但是因?yàn)槟壳皕end_object變成變長了,并且更嚴(yán)重的是你并不知道用戶在PHP繼承了你這個類以后,他新增了多少屬性的定義。
于是沒有辦法,在寫PHPNG的時候,我做了大量的調(diào)整如下(體力活):
typedef struct _mysqli_object { void *ptr; HashTable *prop_handler; zend_object zo; } mysqli_object; /* extends zend_object */
也就是把zend_object從頭部,挪到了尾部,那為了可以從zend_object取得自定義對象,我們需要新增定義:
static inline mysqli_object *php_mysqli_fetch_object(zend_object *obj) { return (mysqli_object *)((char*)(obj) - XtOffsetOf(mysqli_object, zo)); }
這樣類似的代碼大家應(yīng)該可以在很多使用了自定義對象的擴(kuò)展中看到。
這樣一來就規(guī)避了這個問題, 而在實(shí)際的分配自定義對象的時候,我們也需要采用如下的方法:
obj = ecalloc(1, sizeof(mysqli_object) + zend_object_properties_size(class_type));
這塊,大家在寫擴(kuò)展的時候,如果用到自定義的類,一定要注意。
而之前在PHP5中的guard, 我們也知道并不是所有的類都會申明魔術(shù)方法,在PHP5中把guard放在object中會在大部分情況下都是浪費(fèi)內(nèi)存, 所以在PHP7中會,我們會根據(jù)一個類是否申明了魔術(shù)方法(IS_OBJ_HAS_GUARDS)來決定要不要分配,而具體的分配地方也放在了properties_table的末尾:
if (GC_FLAGS(zobj) & IS_OBJ_HAS_GUARDS) { guards = Z_PTR(zobj->properties_table[zobj->ce->default_properties_count]); .... }
從而可以在大部分情況下,節(jié)省一個指針的內(nèi)存分配。
PHP7中在取一個對象的類的時候,就會非常方便了, 直接zvalu.value.obj->ce即可,一些類所自定的handler也就可以很便捷的訪問到了, 性能提升明顯。
以上就是PHP內(nèi)核Object的詳細(xì)介紹內(nèi)容了,看完之后是否有所收獲呢?如果想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊!
文章題目:PHP內(nèi)核Object介紹
路徑分享:http://jinyejixie.com/article48/ipjjep.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、微信小程序、App開發(fā)、App設(shè)計(jì)、網(wǎng)站營銷、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)