借鑑了 TIPI, 對 php 源碼進行學習 歡迎你們給予意見, 互相溝通學習
全部變量用同一結構表示, 既表示變量值, 也表示變量類型php
struct _zval_struct { // PHP存儲變量的結構 zvalue_value value; // 值 zend_uint refcount__gc; // 引用計數 zend_uchar type; // 類型標識, IS_NULL, IS_BOOL, IS_STRING ... zend_uchar is_ref__gc; //是否爲引用 }; // 變量的數據類型 #define IS_NULL 0 // null #define IS_LONG 1 // long #define IS_DOUBLE 2 // float, double #define IS_BOOL 3 // boolean #define IS_ARRAY 4 // array #define IS_OBJECT 5 // object #define IS_STRING 6 // string #define IS_RESOURCE 7 // resource #define IS_CONSTANT 8 // constant #define IS_CONSTANT_ARRAY 9 #define IS_CALLABLE 10
不一樣類型的變量, 存儲的屬性字段不一樣算法
typedef union _zvalue_value { // 變量存儲的值結構 long lval; // 存儲整形或布爾型的值 double dval; // 存儲浮點型的值 struct { // 存儲字符串 char *val; // 字符串指針 int len; // 字符串長度 } str; HashTable *ht; // 存儲數組,參見zend_hash.h zend_object_value obj; // 存儲對象,參見zend_types.h } zvalue_value;
對象類型相對特殊, zval 結構只是定義了對象的操做函數和對象所在位置 (在對象池中的位置)數組
object 結構數據結構
對象存儲在一個對象容器中app
每一個對象自帶操做函數函數
typedef unsigned int zend_object_handle; // 對象的存儲結構 typedef struct _zend_object_value { /* * php 內核會將全部對象存放在一個對象容器中: EG(objects_store).object_buckets * handle 參數是對象在這個容器中的索引, 無符號整數 */ zend_object_handle handle; // 對象屬性, 方法的操做函數: Zend/zend_object_handlers.h const zend_object_handlers *handlers; } zend_object_value;
// 對象池, 存放 php 中間代碼運行過程當中生成的全部對象 typedef struct _zend_objects_store { // 對象容器 zend_object_store_bucket *object_buckets; zend_uint top; zend_uint size; int free_list_head; } zend_objects_store; /* * 對象哈希表桶結構 * 解決衝突的哈希算法爲連接法 * 哈希衝突解決辦法有兩種: * 1. 連接法 * 2. 開放尋址法 */ typedef struct _zend_object_store_bucket { zend_bool destructor_called; zend_bool valid; zend_uchar apply_count; union _store_bucket { struct _store_object { // 對象數據, zend_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;
// 能夠理解爲對象方法的父類 struct _zend_object_handlers { /* general object functions */ zend_object_add_ref_t add_ref; zend_object_del_ref_t del_ref; zend_object_clone_obj_t clone_obj; /* individual object functions */ zend_object_read_property_t read_property; zend_object_write_property_t write_property; zend_object_read_dimension_t read_dimension; zend_object_write_dimension_t write_dimension; zend_object_get_property_ptr_ptr_t get_property_ptr_ptr; zend_object_get_t get; zend_object_set_t set; zend_object_has_property_t has_property; zend_object_unset_property_t unset_property; zend_object_has_dimension_t has_dimension; zend_object_unset_dimension_t unset_dimension; zend_object_get_properties_t get_properties; zend_object_get_method_t get_method; zend_object_call_method_t call_method; zend_object_get_constructor_t get_constructor; zend_object_get_class_entry_t get_class_entry; zend_object_get_class_name_t get_class_name; zend_object_compare_t compare_objects; zend_object_cast_t cast_object; zend_object_count_elements_t count_elements; zend_object_get_debug_info_t get_debug_info; zend_object_get_closure_t get_closure; zend_object_get_gc_t get_gc; }; // 以 php 標準庫爲例調用的方法以下: ZEND_API zend_object_handlers std_object_handlers = { zend_objects_store_add_ref, /* add_ref */ zend_objects_store_del_ref, /* del_ref */ zend_objects_clone_obj, /* clone_obj */ zend_std_read_property, /* read_property */ zend_std_write_property, /* write_property */ zend_std_read_dimension, /* read_dimension */ zend_std_write_dimension, /* write_dimension */ zend_std_get_property_ptr_ptr, /* get_property_ptr_ptr */ NULL, /* get */ NULL, /* set */ zend_std_has_property, /* has_property */ zend_std_unset_property, /* unset_property */ zend_std_has_dimension, /* has_dimension */ zend_std_unset_dimension, /* unset_dimension */ zend_std_get_properties, /* get_properties */ zend_std_get_method, /* get_method */ NULL, /* call_method */ zend_std_get_constructor, /* get_constructor */ zend_std_object_get_class, /* get_class_entry */ zend_std_object_get_class_name, /* get_class_name */ zend_std_compare_objects, /* compare_objects */ zend_std_cast_object_tostring, /* cast_object */ NULL, /* count_elements */ NULL, /* get_debug_info */ zend_std_get_closure, /* get_closure */ zend_std_get_gc, /* get_gc */ };
// 最終存儲在對象哈希表中的對象結構 typedef struct _zend_object { // 對象的類信息 zend_class_entry *ce; // 屬性信息 HashTable *properties; zval **properties_table; HashTable *guards; /* protects from __get/__set ... recursion */ } zend_object;
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; }
常量結構相對特殊, 它除了 zval 結構外, 還包含了其餘的一些屬性學習
#define CONST_CS (1<<0) /* Case Sensitive */ #define CONST_PERSISTENT (1<<1) /* Persistent */ #define CONST_CT_SUBST (1<<2) /* Allow compile-time substitution */ // 常量結構 typedef struct _zend_constant { // zval 結構 zval value; // 標誌位 int flags; // 常量名稱 char *name; // 常量長度 uint name_len; // 常量模塊號 int module_number; } zend_constant;
隱式轉換ui
在不一樣的操做函數自定義, 以字符串鏈接爲例debug
ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */ { zval op1_copy, op2_copy; int use_copy1 = 0, use_copy2 = 0; if (Z_TYPE_P(op1) != IS_STRING) { zend_make_printable_zval(op1, &op1_copy, &use_copy1); } if (Z_TYPE_P(op2) != IS_STRING) { zend_make_printable_zval(op2, &op2_copy, &use_copy2); } // 省略 } ZEND_API void zend_make_printable_zval(zval *expr, zval *expr_copy, int *use_copy) /* {{{ */ { if (Z_TYPE_P(expr)==IS_STRING) { *use_copy = 0; return; } switch (Z_TYPE_P(expr)) { case IS_NULL: Z_STRLEN_P(expr_copy) = 0; Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC(); break; case IS_BOOL: if (Z_LVAL_P(expr)) { Z_STRLEN_P(expr_copy) = 1; Z_STRVAL_P(expr_copy) = estrndup("1", 1); } else { Z_STRLEN_P(expr_copy) = 0; Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC(); } break; case IS_RESOURCE: Z_STRVAL_P(expr_copy) = (char *) emalloc(sizeof("Resource id #") - 1 + MAX_LENGTH_OF_LONG); Z_STRLEN_P(expr_copy) = snprintf(Z_STRVAL_P(expr_copy), sizeof("Resource id #") - 1 + MAX_LENGTH_OF_LONG, "Resource id #%ld", Z_LVAL_P(expr)); break; case IS_ARRAY: zend_error(E_NOTICE, "Array to string conversion"); Z_STRLEN_P(expr_copy) = sizeof("Array") - 1; Z_STRVAL_P(expr_copy) = estrndup("Array", Z_STRLEN_P(expr_copy)); break; case IS_OBJECT: { TSRMLS_FETCH(); // 直接轉換成字符串 if (zend_std_cast_object_tostring(expr, expr_copy, IS_STRING TSRMLS_CC) == SUCCESS) { break; } // 是否認義了 cast_object 函數 if (Z_OBJ_HANDLER_P(expr, cast_object)) { zval *val; ALLOC_ZVAL(val); INIT_PZVAL_COPY(val, expr); zval_copy_ctor(val); // 調用轉換函數 if (Z_OBJ_HANDLER_P(expr, cast_object)(val, expr_copy, IS_STRING TSRMLS_CC) == SUCCESS) { zval_ptr_dtor(&val); break; } zval_ptr_dtor(&val); } // 是否認義了 get 函數 if (!Z_OBJ_HANDLER_P(expr, cast_object) && Z_OBJ_HANDLER_P(expr, get)) { zval *z = Z_OBJ_HANDLER_P(expr, get)(expr TSRMLS_CC); Z_ADDREF_P(z); if (Z_TYPE_P(z) != IS_OBJECT) { zend_make_printable_zval(z, expr_copy, use_copy); if (*use_copy) { zval_ptr_dtor(&z); } else { ZVAL_ZVAL(expr_copy, z, 0, 1); *use_copy = 1; } return; } zval_ptr_dtor(&z); } zend_error(EG(exception) ? E_ERROR : E_RECOVERABLE_ERROR, "Object of class %s could not be converted to string", Z_OBJCE_P(expr)->name); Z_STRLEN_P(expr_copy) = 0; Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC(); } break; case IS_DOUBLE: *expr_copy = *expr; zval_copy_ctor(expr_copy); zend_locale_sprintf_double(expr_copy ZEND_FILE_LINE_CC); break; default: *expr_copy = *expr; zval_copy_ctor(expr_copy); convert_to_string(expr_copy); break; } Z_TYPE_P(expr_copy) = IS_STRING; *use_copy = 1; }
顯式轉換指針
一系列的 "convert_to_" 函數實現, 以 convert_to_boolean 爲例
ZEND_API void convert_to_boolean(zval *op) /* {{{ */ { int tmp; switch (Z_TYPE_P(op)) { case IS_BOOL: break; case IS_NULL: Z_LVAL_P(op) = 0; break; case IS_RESOURCE: { TSRMLS_FETCH(); zend_list_delete(Z_LVAL_P(op)); } /* break missing intentionally */ case IS_LONG: Z_LVAL_P(op) = (Z_LVAL_P(op) ? 1 : 0); break; case IS_DOUBLE: Z_LVAL_P(op) = (Z_DVAL_P(op) ? 1 : 0); break; case IS_STRING: { char *strval = Z_STRVAL_P(op); if (Z_STRLEN_P(op) == 0 || (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) { Z_LVAL_P(op) = 0; } else { Z_LVAL_P(op) = 1; } STR_FREE(strval); } break; case IS_ARRAY: tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0); zval_dtor(op); Z_LVAL_P(op) = tmp; break; case IS_OBJECT: { zend_bool retval = 1; TSRMLS_FETCH(); convert_object_to_type(op, IS_BOOL, convert_to_boolean); if (Z_TYPE_P(op) == IS_BOOL) { return; } zval_dtor(op); ZVAL_BOOL(op, retval); break; } default: zval_dtor(op); Z_LVAL_P(op) = 0; break; } Z_TYPE_P(op) = IS_BOOL; }
php 內核底層全部的操做都是基於 zval 的結構, 能夠將其稱之爲
弱變量容器
弱類型的實現都是對於 zval 內的屬性判斷來區分類型, 調用不一樣的邏輯
爲了方便, php 內核對於這些操做定義了一批操做宏
#define Z_LVAL(zval) (zval).value.lval #define Z_BVAL(zval) ((zend_bool)(zval).value.lval) #define Z_DVAL(zval) (zval).value.dval #define Z_STRVAL(zval) (zval).value.str.val #define Z_STRLEN(zval) (zval).value.str.len #define Z_ARRVAL(zval) (zval).value.ht #define Z_OBJVAL(zval) (zval).value.obj #define Z_OBJ_HANDLE(zval) Z_OBJVAL(zval).handle #define Z_OBJ_HT(zval) Z_OBJVAL(zval).handlers #define Z_OBJCE(zval) zend_get_class_entry(&(zval) TSRMLS_CC) #define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(&(zval) TSRMLS_CC) #define Z_OBJ_HANDLER(zval, hf) Z_OBJ_HT((zval))->hf // 省略 #define Z_TYPE(zval) (zval).type #define Z_TYPE_P(zval_p) Z_TYPE(*zval_p) #define Z_TYPE_PP(zval_pp) Z_TYPE(**zval_pp)簡而言之, php 實現弱類型的核心在於:
全部變量使用同一種數據結構保存, 這個結構不只表示變量的值, 也表示變量類型