借鉴了 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 实现弱类型的核心在于:
全部变量使用同一种数据结构保存, 这个结构不只表示变量的值, 也表示变量类型