当前位置:学者斋 >

计算机 >php语言 >

php内核分析之zval

php内核分析之zval

学习PHP的同学对php内核方面的知识也许了解的还不是很清楚,那么下面小编就php内核之zvAL展开分析,希望对大家有用,更多内容请关注应届毕业生网!

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); // 这个是把php代码编译成为opcode的过程

...

zend_execute(new_op_array, &local_retval); // 这个是具体的执行过程,执行opcode,把结果存储到local_retval中

...

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

// zval的结构

struct _zval_struct {

zend_value value; // 存储具体值,它的结构根据类型不同而不同

union {

struct {

ZEND_ENDIAN_LOHI_4(

zend_uchar type, // 这个位置标记了这个val是什么类型的(IS_STRING/IS_INT)

zend_uchar type_flags, // 这个位置标记了这个val是什么属性 (IS_CALLABLE等)

zend_uchar const_flags, // 常量的一些属性 (IS_CONSTANT_CLASS)

zend_uchar reserved) // 保留的一些字段

} v;

uint32_t type_info; // 类型的一些额外信息

} u1; // 保存类型的一些关键信息

union {

uint32_t next; // 如果是在hash链表中,这个指针代表下一个元素的index

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; // 一些附属字段

};

这个接口最重要的两个字段是 value,存储变量的值。另一个是 存储变量的类型。这里,value也是一个结构

typedef union _zend_value {

zend_long lval; /* long value */

double dval; /* double value */

zend_refcounted *counted;

zend_string *str; // string

zend_array *arr; // array

zend_object *obj; // object

zend_resource *res; // resource

zend_reference *ref; // 指针

zend_ast_ref *ast; // ast指针

zval *zv;

void *ptr;

zend_class_entry *ce; // class实体

zend_function *func; // 函数实体

struct {

uint32_t w1;

uint32_t w2;

} ww;

} zend_value;

如果 == IS_STRING, 那么就是指向了zend_string结构。好了,php的垃圾回收是通过引用计数来进行的,这个引用计数的计数器就放在ted里面。

我们对zval设置的时候设置了一些宏来进行设置,比如:ZVAL_STRINGL是设置string,我们仔细看下调用堆栈:

ZVAL_STRINGL(&pv, str, str_len); // 把pv设置为string类型,值为str

这个函数就是把pv设置为zend_string类型

// 带字符串长度的设置zend_sting类型的zval

#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)

...

// 从char* + 长度 + 是否是临时变量(persistent为0表示最迟这个申请的空间在请求结束的时候就进行释放),转变为zend_string*

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); // 申请空间,申请的大小为zend_string结构大小(除了val)+ len + 1

memcpy(ZSTR_VAL(ret), str, len);

ZSTR_VAL(ret)[len] = '';

return ret;

}

标签: PHP zval 内核
  • 文章版权属于文章作者所有,转载请注明 https://xuezhezhai.com/jsj/php/mkdx3.html