大粨兔奶糖

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

PHP弱类型语法的实现

前言

借鉴了 TIPI, 对 php 源码进行学习
欢迎大家给予意见, 互相沟通学习

弱类型语法实现方式 (弱变量容器 zval)

所有变量用同一结构表示, 既表示变量值, 也表示变量类型

  • zval 结构
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

不同类型的变量, 存储的属性字段不同

  • value 结构
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 结构

  • 对象存储在一个对象容器中

  • 每个对象自带操作函数

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;
  • 函数方法定义, 不同的扩展创建的对象, 实现的方法不同 (以 php 标准库为例)
// 可以理解为对象方法的父类
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 */
};
  • 最终存放在桶中的对象是经过封装的, 包含类信息 (zend_class_entry) 的对象实例
// 最终存储在对象哈希表中的对象结构
typedef struct _zend_object {
// 对象的类信息
zend_class_entry *ce;
// 属性信息
HashTable *properties;
zval **properties_table;
HashTable *guards; /* protects from __get/__set ... recursion */
} zend_object;
  • 对象在 new 的时候都做了哪些事情
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;

弱类型语言的实现举例 (类型转换)

  • 隐式转换

  • 在不同的操作函数自定义, 以字符串连接为例

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

posted on 2017-04-01 11:43  大粨兔奶糖  阅读(220)  评论(0编辑  收藏  举报