久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      簡(jiǎn)單對(duì)比一下PHP 7 和 PHP 5 中的對(duì)象

      本篇文章帶大家了解一下PHP 7 和 PHP 5 中對(duì)象,并比較一下,看看它們之間的差異!

      簡(jiǎn)單對(duì)比一下PHP 7 和 PHP 5 中的對(duì)象

      一、 class 介紹

      ?? PHP 中的 class、interface、trait 在底層均以 zend_class_entry 結(jié)構(gòu)體實(shí)現(xiàn)

      struct _zend_class_entry { 	char type; 	const char *name; 	zend_uint name_length; 	struct _zend_class_entry *parent; 	int refcount; 	zend_uint ce_flags;  	HashTable function_table; 	HashTable properties_info; 	zval **default_properties_table; 	zval **default_static_members_table; 	zval **static_members_table; 	HashTable constants_table; 	int default_properties_count; 	int default_static_members_count;  	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 *serialize_func; 	union _zend_function *unserialize_func;  	zend_class_iterator_funcs iterator_funcs;  	/* handlers */ 	zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC); 	zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC); 	int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* a class implements this interface */ 	union _zend_function *(*get_static_method)(zend_class_entry *ce, char* method, int method_len TSRMLS_DC);  	/* serializer callbacks */ 	int (*serialize)(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); 	int (*unserialize)(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);  	zend_class_entry **interfaces; 	zend_uint num_interfaces; 	 	zend_class_entry **traits; 	zend_uint num_traits; 	zend_trait_alias **trait_aliases; 	zend_trait_precedence **trait_precedences;  	union { 		struct { 			const char *filename; 			zend_uint line_start; 			zend_uint line_end; 			const char *doc_comment; 			zend_uint doc_comment_len; 		} user; 		struct { 			const struct _zend_function_entry *builtin_functions; 			struct _zend_module_entry *module; 		} internal; 	} info; };

      ??zend_class_entry 結(jié)構(gòu)體中包含大量的指針以及 hashtable,這就導(dǎo)致結(jié)構(gòu)體本身會(huì)占用不小的內(nèi)存空間。另外,結(jié)構(gòu)體中的指針還需要單獨(dú)分配相應(yīng)的內(nèi)存空間,這又會(huì)消耗一部分內(nèi)存空間。

      ⒈ 開發(fā)者自定義的 class 與 PHP 內(nèi)部定義的 class 的比較

      ??所謂開發(fā)者自定義的 class 即使用 PHP 語(yǔ)言定義的 class,而 PHP 內(nèi)部定義的 class 是指 PHP 源代碼中定義的 class 或 PHP 擴(kuò)展中定義的 class。二者最本質(zhì)的區(qū)別在于生命周期不同:

      • 以 php-fpm 為例,當(dāng)請(qǐng)求到來時(shí),PHP 會(huì)解析開發(fā)者定義的 class 并為其分配相應(yīng)的內(nèi)存空間。其后在處理請(qǐng)求的過程中,PHP 會(huì)對(duì)這些 class 進(jìn)行相應(yīng)的調(diào)用,最后在處理完請(qǐng)求之后銷毀這些 class,釋放之前為其分配的內(nèi)存空間。

      為了節(jié)約內(nèi)存空間,不要在代碼中定義一些實(shí)際并不使用的 class??梢允褂?autoload 來屏蔽這些實(shí)際并不使用的 class,因?yàn)?autoload 只有在一個(gè) class 被用到時(shí)才加載和解析,但這樣就會(huì)把 class 的解析和加載過程由代碼的編譯階段延后到代碼的執(zhí)行階段,影響性能

      另外需要注意的是,即使開啟了 OPCache 擴(kuò)展,開發(fā)者自定義的 class 還是會(huì)隨著請(qǐng)求的到來而解析和加載,隨著請(qǐng)求的完成而銷毀,OPCache 只是提高了這兩個(gè)階段的速度

      • PHP 內(nèi)部定義的 class 則不同。仍然以 php-fpm 為例,當(dāng)一個(gè) php-fpm 進(jìn)程啟動(dòng)時(shí),PHP 會(huì)為這些 class 一次性永久分配內(nèi)存空間,直到此 php-fpm 進(jìn)程消亡(為避免內(nèi)存泄漏,php-fpm 會(huì)在處理完一定數(shù)量的請(qǐng)求之后銷毀然后重啟)
      if (EG(full_tables_cleanup)) { 	zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC); 	zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC); } else { 	zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function TSRMLS_CC); 	zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class TSRMLS_CC); }  static int clean_non_persistent_class(zend_class_entry **ce TSRMLS_DC) { 	return ((*ce)->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_STOP : ZEND_HASH_APPLY_REMOVE; }

      ??由以上代碼可以看出,在請(qǐng)求結(jié)束時(shí),PHP 內(nèi)部定義的 class 并不會(huì)被銷毀。另外,由于 PHP 擴(kuò)展中定義的 class 也屬于 PHP 內(nèi)部定義的 class 的范疇,所以,從節(jié)省內(nèi)存空間的角度出發(fā),不要開啟一些自己并不使用的擴(kuò)展。因?yàn)?,如果擴(kuò)展一旦開啟,擴(kuò)展中定義的 class 就會(huì)在 php-fpm 進(jìn)程啟動(dòng)時(shí)被解析和加載。

      很多時(shí)候,為了處理方便,我們會(huì)通過繼承 Exception 來自定義 exception。但由于 zend_class_entry 結(jié)構(gòu)體非常龐大,這就導(dǎo)致在提高便利的同時(shí)耗費(fèi)了大量的內(nèi)存

      ⒉ class 綁定

      ?? class 綁定指的是 class 數(shù)據(jù)的準(zhǔn)備過程

      ??對(duì)于 PHP 內(nèi)部定義的 class,綁定過程在 class 注冊(cè)時(shí)就已經(jīng)完成。此過程發(fā)生在 PHP 腳本運(yùn)行之前,并且在整個(gè) php-fpm 進(jìn)程的生命周期中只發(fā)生一次。

      ??對(duì)于既沒有繼承 parent class,也沒有實(shí)現(xiàn) interface,也沒有使用 trait 的 class,綁定過程發(fā)生在 PHP 代碼的編輯階段,并且不會(huì)消耗太多資源。此種 class 的綁定通常只需要將 class 注冊(cè)到 class_table 中,并檢查 class 是否包含了抽象方法但沒有被申明為 abstract 類型。

      void zend_do_early_binding(TSRMLS_D) /* {{{ */ { 	zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1]; 	HashTable *table;  	while (opline->opcode == ZEND_TICKS && opline > CG(active_op_array)->opcodes) { 		opline--; 	}  	switch (opline->opcode) { 		case ZEND_DECLARE_FUNCTION: 			if (do_bind_function(CG(active_op_array), opline, CG(function_table), 1) == FAILURE) { 				return; 			} 			table = CG(function_table); 			break; 		case ZEND_DECLARE_CLASS: 			if (do_bind_class(CG(active_op_array), opline, CG(class_table), 1 TSRMLS_CC) == NULL) { 				return; 			} 			table = CG(class_table); 			break; 		case ZEND_DECLARE_INHERITED_CLASS: 			{ 				/*... ...*/ 			} 		case ZEND_VERIFY_ABSTRACT_CLASS: 		case ZEND_ADD_INTERFACE: 		case ZEND_ADD_TRAIT: 		case ZEND_BIND_TRAITS: 			/* We currently don't early-bind classes that implement interfaces */ 			/* Classes with traits are handled exactly the same, no early-bind here */ 			return; 		default: 			zend_error(E_COMPILE_ERROR, "Invalid binding type"); 			return; 	}  /*... ...*/ }  void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC) { 	zend_abstract_info ai;  	if ((ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) && !(ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) { 		memset(&ai, 0, sizeof(ai));  		zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC);  		if (ai.cnt) { 			zend_error(E_ERROR, "Class %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", 				ce->name, ai.cnt, 				ai.cnt > 1 ? "s" : "", 				DISPLAY_ABSTRACT_FN(0), 				DISPLAY_ABSTRACT_FN(1), 				DISPLAY_ABSTRACT_FN(2) 				); 		} 	} }

      ??對(duì)于實(shí)現(xiàn)了 interface 的 class 的綁定過程非常復(fù)雜,大致流程如下:

      • 檢查 interface 是否已經(jīng)實(shí)現(xiàn)
      • 檢查實(shí)現(xiàn)該 interface 的確實(shí)是一個(gè) class,而不是 interface 自身(class、interface、trait 的底層數(shù)據(jù)結(jié)構(gòu)都是 zend_class_entry)
      • 復(fù)制常量,并檢查可能存在的沖突
      • 復(fù)制方法,并檢查可能存在的沖突,除此之外還需要檢查訪問控制
      • 將 interface 加入到 zend_class_entry 的 **interfaces

      需要注意的是,所謂的復(fù)制只是將常量、屬性、方法的引用計(jì)數(shù)加 1

      ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) { 	/* ... ... */ 	 	} else { 		if (ce->num_interfaces >= current_iface_num) { /* resize the vector if needed */ 			if (ce->type == ZEND_INTERNAL_CLASS) { 				/*對(duì)于內(nèi)部定義的 class,使用 realloc 分配內(nèi)存,所分配的內(nèi)存在進(jìn)程的生命周期中永久有效*/ 				ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); 			} else { 				/*對(duì)于開發(fā)者定義的 class,使用 erealloc 分配內(nèi)存,所分配的內(nèi)存只在請(qǐng)求的生命周期中有效*/ 				ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); 			} 		} 		ce->interfaces[ce->num_interfaces++] = iface; /* Add the interface to the class */  		/* Copy every constants from the interface constants table to the current class constants table */ 		zend_hash_merge_ex(&ce->constants_table, &iface->constants_table, (copy_ctor_func_t) zval_add_ref, sizeof(zval *), (merge_checker_func_t) do_inherit_constant_check, iface); 		/* Copy every methods from the interface methods table to the current class methods table */ 		zend_hash_merge_ex(&ce->function_table, &iface->function_table, (copy_ctor_func_t) do_inherit_method, sizeof(zend_function), (merge_checker_func_t) do_inherit_method_check, ce);  		do_implement_interface(ce, iface TSRMLS_CC); 		zend_do_inherit_interfaces(ce, iface TSRMLS_CC); 	} }

      ??對(duì)于常量的復(fù)制,zval_add_ref 用于將常量的引用計(jì)數(shù)加1;而對(duì)于方法的復(fù)制,do_inherit_method 除了將相應(yīng)方法的引用計(jì)數(shù)加 1 之外,還將方法中定義的靜態(tài)變量的引用計(jì)數(shù)加 1。

      static void do_inherit_method(zend_function *function) { 	function_add_ref(function); }  ZEND_API void function_add_ref(zend_function *function) { 	if (function->type == ZEND_USER_FUNCTION) { 		zend_op_array *op_array = &function->op_array;  		(*op_array->refcount)++; 		if (op_array->static_variables) { 			HashTable *static_variables = op_array->static_variables; 			zval *tmp_zval;  			ALLOC_HASHTABLE(op_array->static_variables); 			zend_hash_init(op_array->static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0); 			zend_hash_copy(op_array->static_variables, static_variables, (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *)); 		} 		op_array->run_time_cache = NULL; 	} }

      ??對(duì)于實(shí)現(xiàn)了 interface 的 class 的綁定,由于要進(jìn)行多次的循環(huán)遍歷以及檢查,通常非常消耗 CPU 資源,但卻節(jié)省了內(nèi)存空間。

      現(xiàn)階段,PHP 將 interface 的綁定推遲到了代碼執(zhí)行階段進(jìn)行,以為這每次請(qǐng)求都會(huì)進(jìn)行這些操作

      ??對(duì)于 class 繼承的綁定,過程與 interface 的綁定類似,但更為復(fù)雜。另外有一個(gè)值得注意的地方,如果 class 在綁定時(shí)已經(jīng)解析到了父類,則綁定發(fā)生在代碼編譯階段;否則發(fā)生在代碼執(zhí)行階段。

      // A 在 B 之前申明,B 的綁定發(fā)生在編譯階段 class A { } class B extends A { }  // A 在 B 之后申明,綁定 B 時(shí)編譯器無法知道 A 情況,此時(shí) B 的綁定只能延后到代碼執(zhí)行時(shí) class B extends A { } class A { }  // 這種情況會(huì)報(bào)錯(cuò):Class B doesn't exist // 在代碼執(zhí)行階段綁定 C,需要解析 B,但此時(shí) B 有繼承了 A,而 A 此時(shí)還是未知狀態(tài) class C extends B { } class B extends A { } class A { }

      如果使用 autoload,并且采用一個(gè) class 對(duì)應(yīng)一個(gè)文件的模式,則所有 class 的綁定都只會(huì)發(fā)生在代碼執(zhí)行階段

      二、PHP 5 中的 object

      ⒈ object 中的方法

      ??方法與函數(shù)的底層數(shù)據(jù)結(jié)構(gòu)均為 zend_function。PHP 編譯器在編譯時(shí)將方法編譯并添加到 zend_class_entry 的 function_table 屬性中。所以,在 PHP 代碼運(yùn)行時(shí),方法已經(jīng)編譯完成,PHP 要做的只是通過指針找到方法并執(zhí)行。

      typedef union _zend_function { 	zend_uchar type;  	struct { 		zend_uchar type; 		const char *function_name; 		zend_class_entry *scope; 		zend_uint fn_flags; 		union _zend_function *prototype; 		zend_uint num_args; 		zend_uint required_num_args; 		zend_arg_info *arg_info; 	} common;  	zend_op_array op_array; 	zend_internal_function internal_function; } zend_function;

      ??當(dāng) object 嘗試調(diào)用方法時(shí),首先會(huì)在其對(duì)應(yīng)的 class 的 function_table 中查找該方法,同時(shí)還會(huì)檢查方法的訪問控制。如果方法不存在或方法的訪問控制不符合要求,object 會(huì)嘗試調(diào)用莫屬方法 __call。

      static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, const char *method_name, int method_len)  { 	zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); 	call_user_call->type = ZEND_INTERNAL_FUNCTION; 	call_user_call->module = (ce->type == ZEND_INTERNAL_CLASS) ? ce->info.internal.module : NULL; 	call_user_call->handler = zend_std_call_user_call; 	call_user_call->arg_info = NULL; 	call_user_call->num_args = 0; 	call_user_call->scope = ce; 	call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; 	call_user_call->function_name = estrndup(method_name, method_len);  	return (union _zend_function *)call_user_call; }  static union _zend_function *zend_std_get_method(zval **object_ptr, char *method_name, int method_len, const zend_literal *key TSRMLS_DC) { 	zend_function *fbc; 	zval *object = *object_ptr; 	zend_object *zobj = Z_OBJ_P(object); 	ulong hash_value; 	char *lc_method_name; 	ALLOCA_FLAG(use_heap)  	if (EXPECTED(key != NULL)) { 		lc_method_name = Z_STRVAL(key->constant); 		hash_value = key->hash_value; 	} else { 		lc_method_name = do_alloca(method_len+1, use_heap); 		/* Create a zend_copy_str_tolower(dest, src, src_length); */ 		zend_str_tolower_copy(lc_method_name, method_name, method_len); 		hash_value = zend_hash_func(lc_method_name, method_len+1); 	}  	if (UNEXPECTED(zend_hash_quick_find(&zobj->ce->function_table, lc_method_name, method_len+1, hash_value, (void **)&fbc) == FAILURE)) { 		if (UNEXPECTED(!key)) { 			free_alloca(lc_method_name, use_heap); 		} 		if (zobj->ce->__call) { 			return zend_get_user_call_function(zobj->ce, method_name, method_len); 		} else { 			return NULL; 		} 	}  	/* Check access level */ 	if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) { 		zend_function *updated_fbc;  		/* Ensure that if we're calling a private function, we're allowed to do so. 		* If we're not and __call() handler exists, invoke it, otherwise error out. 		*/ 		updated_fbc = zend_check_private_int(fbc, Z_OBJ_HANDLER_P(object, get_class_entry)(object TSRMLS_CC), lc_method_name, method_len, hash_value TSRMLS_CC); 		if (EXPECTED(updated_fbc != NULL)) { 			fbc = updated_fbc; 		} else { 			if (zobj->ce->__call) { 				fbc = zend_get_user_call_function(zobj->ce, method_name, method_len); 			} else { 				zend_error_noreturn(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name : ""); 			} 		} 	} else { 		/* Ensure that we haven't overridden a private function and end up calling 		* the overriding public function... 		*/ 		if (EG(scope) && 		    is_derived_class(fbc->common.scope, EG(scope)) && 		    fbc->op_array.fn_flags & ZEND_ACC_CHANGED) { 			zend_function *priv_fbc;  			if (zend_hash_quick_find(&EG(scope)->function_table, lc_method_name, method_len+1, hash_value, (void **) &priv_fbc)==SUCCESS 				&& priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE 				&& priv_fbc->common.scope == EG(scope)) { 				fbc = priv_fbc; 			} 		} 		if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) { 			/* Ensure that if we're calling a protected function, we're allowed to do so. 			* If we're not and __call() handler exists, invoke it, otherwise error out. 			*/ 			if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), EG(scope)))) { 				if (zobj->ce->__call) { 					fbc = zend_get_user_call_function(zobj->ce, method_name, method_len); 				} else { 					zend_error_noreturn(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name : ""); 				} 			} 		} 	}  	if (UNEXPECTED(!key)) { 		free_alloca(lc_method_name, use_heap); 	} 	return fbc; }

      ??這里需要指出的是:

      • 由于 PHP 對(duì)大小寫不敏感,所以所有的方法名稱都會(huì)被轉(zhuǎn)為小寫(zend_str_tolower_copy())
      • 為了避免不必要的資源消耗,PHP 5.4 開始引入了 zend_literal 結(jié)構(gòu)體,即參數(shù) key
      typedef struct _zend_literal { 	zval       constant; 	zend_ulong hash_value; 	zend_uint  cache_slot; } zend_literal;

      ??其中,constant 記錄了轉(zhuǎn)為小寫后的字符串,hash_value 則是預(yù)先計(jì)算好的 hash。這樣就避免了 object 每次調(diào)用方法都要將方法名稱轉(zhuǎn)為小寫并計(jì)算 hash 值。

      class Foo { public function BAR() { } } $a = new Foo; $b = 'bar';  $a->bar(); /* good */ $a->$b(); /* bad */

      ??在上例中,在代碼編譯階段,方法 BAR 被轉(zhuǎn)換成 bar 并添加到 zend_class_entry 的 function_table 中。當(dāng)發(fā)生方法調(diào)用時(shí):

      • 第一種情形,在代碼編譯階段,方法名稱 bar 確定為字符串常量,編譯器可以預(yù)先計(jì)算好其對(duì)應(yīng)的 zend_literal 結(jié)構(gòu),即 key 參數(shù)。這樣,代碼在執(zhí)行時(shí)相對(duì)會(huì)更快。
      • 第二種情形,由于在編譯階段編譯器對(duì) $b 一無所知,這就需要在代碼執(zhí)行階段現(xiàn)將方法名稱轉(zhuǎn)為小寫,然后計(jì)算 hash 值。

      ⒉ object 中的屬性

      ??當(dāng)對(duì)一個(gè) class 進(jìn)行實(shí)例化時(shí),object 中的屬性只是對(duì) class 中屬性的引用。這樣,object 的創(chuàng)建操作就會(huì)相對(duì)輕量化,并且會(huì)節(jié)省一部分內(nèi)存空間。

      簡(jiǎn)單對(duì)比一下PHP 7 和 PHP 5 中的對(duì)象

      ??如果要對(duì) object 中的屬性進(jìn)行修改,zend 引擎會(huì)單獨(dú)創(chuàng)建一個(gè) zval 結(jié)構(gòu),只對(duì)當(dāng)前 object 的當(dāng)前屬性產(chǎn)生影響。

      簡(jiǎn)單對(duì)比一下PHP 7 和 PHP 5 中的對(duì)象

      ??class 的實(shí)例化對(duì)應(yīng)的會(huì)在底層創(chuàng)建一個(gè) zend_obejct 數(shù)據(jù)結(jié)構(gòu),新創(chuàng)建的 object 會(huì)注冊(cè)到 zend_objects_store 中。zend_objects_store 是一個(gè)全局的 object 注冊(cè)表,同一個(gè)對(duì)象在該注冊(cè)表中只能注冊(cè)一次。

      typedef struct _zend_object { 	zend_class_entry *ce; 	HashTable *properties; 	zval **properties_table; 	HashTable *guards; /* protects from __get/__set ... recursion */ } zend_object;  typedef struct _zend_objects_store {/*本質(zhì)上是一個(gè)動(dòng)態(tài) object_bucket 數(shù)組*/ 	zend_object_store_bucket *object_buckets; 	zend_uint top; /*下一個(gè)可用的 handle,handle 取值從 1 開始。對(duì)應(yīng)的在 *object_buckets 中的 index 為 handle - 1*/ 	zend_uint size; /*當(dāng)前分配的 *object_buckets 的最大長(zhǎng)度*/ 	int free_list_head; /*當(dāng) *object_bucket 中的 bucket 被銷毀后,該 bucket 在 *object_buckets 中的 index 會(huì)被有序加入 free_list 鏈表。free_list_head 即為該鏈表中的第一個(gè)值*/ } zend_objects_store;  typedef struct _zend_object_store_bucket { 	zend_bool destructor_called; 	zend_bool valid; /*值為 1 表示當(dāng)前 bucket 被使用,此時(shí) store_bucket 中的 store_object 被使用;值為 0 表示當(dāng)前 bucket 并沒有存儲(chǔ)有效的 object,此時(shí) store_bucket 中的 free_list 被使用*/ 	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; /*第一個(gè)未被使用的 bucket 的 index 永遠(yuǎn)存儲(chǔ)在 zend_object_store 的 free_list_head 中,所以 next 只需要記錄當(dāng)前 bucket 之后第一個(gè)未被使用的 bucket 的 index*/ 		} free_list; 	} bucket; } zend_object_store_bucket;  ZEND_API zend_object_value zend_objects_new(zend_object **object, zend_class_entry *class_type TSRMLS_DC) { 	zend_object_value retval;  	*object = emalloc(sizeof(zend_object)); 	(*object)->ce = class_type; 	(*object)->properties = NULL; 	(*object)->properties_table = NULL; 	(*object)->guards = NULL; 	retval.handle = zend_objects_store_put(*object, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC); 	retval.handlers = &std_object_handlers; 	return retval; }

      ?? 將 object 注冊(cè)到 zend_objects_store 中以后,將會(huì)為 object 創(chuàng)建屬性(對(duì)相應(yīng) class 屬性的引用)

      ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type)  { 	int i;  	if (class_type->default_properties_count) { 		object->properties_table = emalloc(sizeof(zval*) * class_type->default_properties_count); 		for (i = 0; i < class_type->default_properties_count; i++) { 			object->properties_table[i] = class_type->default_properties_table[i]; 			if (class_type->default_properties_table[i]) { #if ZTS 				ALLOC_ZVAL( object->properties_table[i]); 				MAKE_COPY_ZVAL(&class_type->default_properties_table[i], object->properties_table[i]); #else 				Z_ADDREF_P(object->properties_table[i]); #endif 			} 		} 		object->properties = NULL; 	} }

      ??需要指出的是,在創(chuàng)建屬性時(shí),如果是非線程安全模式的 PHP,僅僅是增加相應(yīng)屬性的引用計(jì)數(shù);但如果是線程安全模式的 PHP,則需要對(duì)屬性進(jìn)行深度復(fù)制,將 class 的屬性全部復(fù)制到 object 中的 properties_table 中。

      這也說明,線程安全的 PHP 比非線程安全的 PHP 運(yùn)行慢,并且更耗費(fèi)內(nèi)存

      每個(gè)屬性在底層都對(duì)應(yīng)一個(gè) zend_property_info 結(jié)構(gòu):

      typedef struct _zend_property_info {     zend_uint flags;     const char *name;     int name_length;     ulong h;     int offset;     const char *doc_comment;     int doc_comment_len;     zend_class_entry *ce; } zend_property_info;

      ??class 中聲明的每個(gè)屬性,在 zend_class_entry 中的 properties_table 中都有一個(gè)zend_property_info 與之相對(duì)應(yīng)。properties_table 可以幫助我們快速確定一個(gè) object 所訪問的屬性是否存在:

      • 如果屬性不存在,并且我們嘗試向 object 寫入該屬性:如果 class 定義了 __set 方法,則使用 __set 方法寫入該屬性;否則會(huì)向 object 添加一個(gè)動(dòng)態(tài)屬性。但無論以何種方式寫入該屬性,寫入的屬性都將添加到 object 的 properties_table 中。
      • 如果屬性存在,則需要檢查相應(yīng)的訪問控制;對(duì)于 protected 和 private 類型,則需要檢查當(dāng)前的作用域。

      在創(chuàng)建完 object 之后,只要我們不向 object 中寫入新的屬性或更新 object 對(duì)應(yīng)的 class 中的屬性的值,則 object 所占用的內(nèi)存空間不會(huì)發(fā)生變化。

      屬性的存儲(chǔ)/訪問方式:
      zend_class_entry->properties_info 中存儲(chǔ)的是一個(gè)個(gè)的 zend_property_info。而屬性的值實(shí)際以 zval 指針數(shù)組的方式存儲(chǔ)在 zend_class_entry->default_properties_table 中。object 中動(dòng)態(tài)添加的屬性只會(huì)以 property_name => property_value 的形式存儲(chǔ)在 zend_object->properties_table 中。而在創(chuàng)建 object 時(shí),zend_class_entry->properties_table 中的值會(huì)被逐個(gè)傳遞給 zend_object->properties_table。
      zend_literal->cache_slot 中存儲(chǔ)的 int 值為 run_time_cache 中的索引 index。run_time_cache 為數(shù)組結(jié)構(gòu),index 對(duì)應(yīng)的 value 為訪問該屬性的 object 對(duì)應(yīng)的 zend_class_entry;index + 1 對(duì)應(yīng)的 value 為該屬性對(duì)應(yīng)的 zend_property_info 。在訪問屬性時(shí),如果 zend_literal->cache_slot 中的值不為空,則可以通過 zend_literal->cache_slot 快速檢索得到 zend_property_info 結(jié)構(gòu);如果為空,則在檢索到 zend_property_info 的信息之后會(huì)初始化 zend_literal->cache_slot。

      屬性名稱的存儲(chǔ)方式
      private 屬性:"