php内核分析之zval
学习PHP的同学对php内核方面的知识也许了解的还不是很清楚,那么下面小编就php内核之zval展开分析,希望对大家有用,更多内容请关注应届毕业生网!
这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux
实际上,从这个函数开始,就已经进入到了zend引擎的范围了。
zend_eval_string_ex(exec_direct, NULL, "Command line code", 1)
实际上是调用Zend/zend_execute_API.c
zend_eval_stringl_ex(str, strlen(str), retval_ptr, string_name, handle_exceptions);
再进去是调用
result = zend_eval_stringl(str, str_len, retval_ptr, string_name);
这里的retval_ptr为NULL,string_name为"Command line code", str为"echo 12;"
zend_eval_stringl
其实这个函数主流程并不复杂。简化下来就如下
ZEND_API int zend_eval_stringl(char *str, size_t str_len, zval *retval_ptr, char *string_name) /* {{{ */
{
...
new_op_array = zend_compile_string(&pv, string_name); /pic/p>
...
zend_execute(new_op_array, &local_retval); /pic/p>
...
retval = SUCCESS;
return retval;
}
先把php编译为opcode,然后执行这个opcode。只是这个函数有一些关键的'结构需要理一下。
zval
我们会看到
zval local_retval;
这样的变量,然后会对这个变量进行如下操作:
ZVAL_UNDEF(&local_retval);
ZVAL_NULL(z)
ZVAL_FALSE(z)
ZVAL_TRUE(z)
ZVAL_BOOL(z, b)
ZVAL_LONG(z, l)
ZVAL_DOUBLE(z, d)
ZVAL_STR(z, s)
ZVAL_INTERNED_STR(z, s)
ZVAL_NEW_STR(z, s)
ZVAL_STR_COPY(z, s)
ZVAL_ARR(z, a)
ZVAL_NEW_ARR(z)
ZVAL_NEW_PERSISTENT_ARR(z)
ZVAL_OBJ(z, o)
ZVAL_RES(z, r)
ZVAL_NEW_RES(z, h, p, t)
ZVAL_NEW_PERSISTENT_RES(z, h, p, t)
ZVAL_REF(z, r)
ZVAL_NEW_EMPTY_REF(z)
ZVAL_NEW_REF(z, r)
ZVAL_NEW_PERSISTENT_REF(z, r)
ZVAL_NEW_AST(z, a)
ZVAL_INDIRECT(z, v)
ZVAL_PTR(z, p)
ZVAL_FUNC(z, f)
ZVAL_CE(z, c)
ZVAL_ERROR(z)
php是一个弱类型的语言,它可以用一个$var来代表string,int,array,object等。这个就是归功于zval_struct结构
/pic/p>
struct _zval_struct {
zend_value value; /pic/p>
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /pic/IS_INT)
zend_uchar type_flags, /pic/p>
zend_uchar const_flags, /pic/p>
zend_uchar reserved) /pic/p>
} v;
uint32_t type_info; /pic/p>
} u1; /pic/p>
union {
uint32_t next; /pic/p>
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
uint32_t access_flags; /* class constant access flags */
uint32_t property_guard; /* single property guard */
} u2; /pic/p>
};
这个接口最重要的两个字段是 value,存储变量的值。另一个是u1.v.type 存储变量的类型。这里,value也是一个结构
typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */
zend_refcounted *counted;
zend_string *str; /pic/p>
zend_array *arr; /pic/p>
zend_object *obj; /pic/p>
zend_resource *res; /pic/p>
zend_reference *ref; /pic/p>
zend_ast_ref *ast; /pic/p>
zval *zv;
void *ptr;
zend_class_entry *ce; /pic/p>
zend_function *func; /pic/p>
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
如果u1.v.type == IS_STRING, 那么value.str就是指向了zend_string结构。好了,php的'垃圾回收是通过引用计数来进行的,这个引用计数的计数器就放在zval.value.counted里面。
我们对zval设置的时候设置了一些宏来进行设置,比如:ZVAL_STRINGL是设置string,我们仔细看下调用堆栈:
ZVAL_STRINGL(&pv, str, str_len); /pic/p>
这个函数就是把pv设置为zend_string类型
/pic/p>
#define ZVAL_STRINGL(z, s, l) do { \
ZVAL_NEW_STR(z, zend_string_init(s, l, 0)); \
} while (0)
注意到,这里使用了一个写法,do {} while(0) 来设置一个宏,这个是C里面比较好的写法,这样写,能保证宏中定义的东西在for,if,等各种流程语句中不会出现语法错误。不过其实我们学习代码的时候,可以忽略掉这个框框写法。
zend_string_init(s, l, 0)
...
/pic/p>
static zend_always_inline zend_string *zend_string_init(const char *str, size_t len, int persistent)
{
zend_string *ret = zend_string_alloc(len, persistent); /pic/p>
memcpy(ZSTR_VAL(ret), str, len);
ZSTR_VAL(ret)[len] = '\0';
return ret;
}
这个函数可以看的点有几个:
persistent
这个参数是用来代表申请的空间是不是“临时”的。这里说的临时是zend提供的一种内存管理器,相关请求数据只服务于单个请求,最迟会在请求结束的时候释放。
临时内存申请对应的函数为:
void *emalloc(size_t size)
而永久内存申请对应的函数为:
malloc
zend_string_alloc
static zend_always_inline zend_string *zend_string_alloc(size_t len, int persistent)
{
zend_string *ret = (zend_string *)pemalloc(ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), persistent);
GC_REFCOUNT(ret) = 1;
GC_TYPE_INFO(ret) = IS_STRING | ((persistent ? IS_STR_PERSISTENT : 0) << 8);
zend_string_forget_hash_val(ret);
ZSTR_LEN(ret) = len;
return ret;
}
我们先看看zend_string的结构:
/pic/p>
struct _zend_string {
zend_refcounted_h gc; /pic/p>
zend_ulong h; /pic/p>
size_t len; /pic/p>
char val[1]; /pic/p>
};
_ZSTR_STRUCT_SIZE(len) gc+h+len的空间,最后给了val留了len+1的长度
#define _ZSTR_STRUCT_SIZE(len) (_ZSTR_HEADER_SIZE + len + 1)
## GC_REFCOUNT(ret) = 1;
#define GC_REFCOUNT(p) (p)->gc.refcount
这里就看到一个结构zend_refcounted_h
typedef struct _zend_refcounted_h {
uint32_t refcount; /pic/p>
union {
struct {
ZEND_ENDIAN_LOHI_3(
zend_uchar type, /pic/p>
zend_uchar flags, /pic/p>
uint16_t gc_info) /pic/p>
} v;
uint32_t type_info; /pic/p>
} u; /pic/p>
} zend_refcounted_h;
回到我们的实例,我们调用的是
zend_string_init(s, l, 0) /pic/p>
返回的zend_string实际值为:
struct _zend_string {
struct {
uint32_t refcount; /pic/p>
union {
struct {
ZEND_ENDIAN_LOHI_3(
zend_uchar type, /pic/p>
zend_uchar flags,
uint16_t gc_info)
} v;
uint32_t type_info; /pic/p>
} u;
} gc;
zend_ulong h; /pic/p>
size_t len; /pic/p>
char val[1]; /pic/p>
};
结合到zval里面,那么ZVAL_STRINGL(&pv, str, str_len);返回的zval为
/pic/p>
struct _zval_struct {
union _zend_value {
zend_long lval;
double dval;
zend_refcounted *counted;
zend_string *str; /pic/p>
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} value;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type,
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved)
} v;
uint32_t type_info; /pic/p>
} u1;
union {
uint32_t next;
uint32_t cache_slot;
uint32_t lineno;
uint32_t num_args;
uint32_t fe_pos;
uint32_t fe_iter_idx;
uint32_t access_flags;
uint32_t property_guard;
} u2;
};
这里,就对zval结构有初步了解了。
另外建议记住几个常用的类型,后续调试的时候会很有用
/* regular data types */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#define IS_LONG 4
#define IS_DOUBLE 5
#define IS_STRING 6
#define IS_ARRAY 7
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10
/* constant expressions */
#define IS_CONSTANT 11
#define IS_CONSTANT_AST 12
【php内核分析之zval】相关文章:
php内核分析之扩展01-31
php内核分析之opcode02-15
php内核分析之do-cli01-09
php内核分析之zend-compile10-17
php内核分析之全局变量12-14
php内核分析之sapi-module-struct10-04
php内核分析之ZTS和zend-try08-24
php学习之php配置03-11
php学习之php预定义变量11-02