PHP匿点源码之变量赋值与引用
2016-09-02 10:09
666 查看
首先需要知道的是:
拿$a=10举例
PHP变量的名称和值在内核中是保存在两个不同的地方的,
值[10]是通过一个与名字毫无关系的zval结构来保存,
名字[a]则保存在符号表里,
两者之间通过指针联系着。
我们来看下zval的结构
当一个变量被第一次创建的时候,它对应的zval结构体的refcount__gc成员的值会被初始化为1,理由很简单,因为只有这个变量自己在用它。但是当你把这个变量赋值给别的变量时,refcount__gc属性便会加1变成2,因为现在有两个变量在用这个zval结构了!
这个时候当我们再用unset删除$a的时候,它删除符号表里的$a的信息,然后清理它的值部分,这时它发现$a的值对应的zval结构的refcount值是2,也就是有另外一个变量在一起用着这个zval,所以unset只需把这个zval的refcount减去1就行了!
好了变量的介绍先说到这儿, 更多可以参看这篇文章:http://blog.sina.com.cn/s/blog_75a2f94f0101gygh.html
现在我们就例子来分析:
方法介绍
void xdebug_debug_zval( [string varname [, ...]] )
用于打印一个或多个变量结构的相关信息。包括变量类型,值以及变量引用次数。如果传递的是一个数组,则会递归数组中的所有元素。
结果
注释掉xdebug_debug_zval('a'); 查看opcode
由此我们可以推断出此处的处理函数:
拿$a=10举例
PHP变量的名称和值在内核中是保存在两个不同的地方的,
值[10]是通过一个与名字毫无关系的zval结构来保存,
名字[a]则保存在符号表里,
两者之间通过指针联系着。
我们来看下zval的结构
/* 变量存储结构 */ struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; /* 引用计数 */ zend_uchar type; /* active type 变量的类型 [IS_NULL, IS_BOOL, IS_LONG, IS_DOUBLE, IS_STRING, IS_ARRAY, IS_OBJECT, IS_RESOURCE]之一*/ zend_uchar is_ref__gc; /* 是否被引用 */ };其中 is_ref__gc、refcount__gc 在变量共用内存空间是后发挥着巨大的作用, 例如:
<?php $a = "hello"; $b = $a; unset($a); ?>
当一个变量被第一次创建的时候,它对应的zval结构体的refcount__gc成员的值会被初始化为1,理由很简单,因为只有这个变量自己在用它。但是当你把这个变量赋值给别的变量时,refcount__gc属性便会加1变成2,因为现在有两个变量在用这个zval结构了!
这个时候当我们再用unset删除$a的时候,它删除符号表里的$a的信息,然后清理它的值部分,这时它发现$a的值对应的zval结构的refcount值是2,也就是有另外一个变量在一起用着这个zval,所以unset只需把这个zval的refcount减去1就行了!
好了变量的介绍先说到这儿, 更多可以参看这篇文章:http://blog.sina.com.cn/s/blog_75a2f94f0101gygh.html
现在我们就例子来分析:
<?php $a = 1; xdebug_debug_zval('a'); $b = &$a; xdebug_debug_zval('a'); $c = 20; $a = &$c; xdebug_debug_zval('a'); var_dump('a:'.$a, 'b:'.$b, 'c:'.$c); unset($a); xdebug_debug_zval('a'); var_dump('a:'.$a, 'b:'.$b, 'c:'.$c); ?>
方法介绍
void xdebug_debug_zval( [string varname [, ...]] )
用于打印一个或多个变量结构的相关信息。包括变量类型,值以及变量引用次数。如果传递的是一个数组,则会递归数组中的所有元素。
结果
注释掉xdebug_debug_zval('a'); 查看opcode
由此我们可以推断出此处的处理函数:
static int ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op2; zval **variable_ptr_ptr; zval **value_ptr_ptr; SAVE_OPLINE(); value_ptr_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->op2.var TSRMLS_CC); if (IS_CV == IS_VAR && value_ptr_ptr && !Z_ISREF_PP(value_ptr_ptr) && opline->extended_value == ZEND_RETURNS_FUNCTION && !EX_T(opline->op2.var).var.fcall_returned_reference) { if (free_op2.var == NULL) { PZVAL_LOCK(*value_ptr_ptr); /* undo the effect of get_zval_ptr_ptr() */ } zend_error(E_STRICT, "Only variables should be assigned by reference"); if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } return ZEND_ASSIGN_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } else if (IS_CV == IS_VAR && opline->extended_value == ZEND_RETURNS_NEW) { PZVAL_LOCK(*value_ptr_ptr); } if (IS_CV == IS_VAR && UNEXPECTED(EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr)) { zend_error_noreturn(E_ERROR, "Cannot assign by reference to overloaded object"); } variable_ptr_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->op1.var TSRMLS_CC); if ((IS_CV == IS_VAR && UNEXPECTED(value_ptr_ptr == NULL)) || (IS_CV == IS_VAR && UNEXPECTED(variable_ptr_ptr == NULL))) { zend_error_noreturn(E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); } zend_assign_to_variable_reference(variable_ptr_ptr, value_ptr_ptr TSRMLS_CC); //在这里执行分配的操作 if (IS_CV == IS_VAR && opline->extended_value == ZEND_RETURNS_NEW) { Z_DELREF_PP(variable_ptr_ptr); } if (RETURN_VALUE_USED(opline)) { PZVAL_LOCK(*variable_ptr_ptr); AI_SET_PTR(&EX_T(opline->result.var), *variable_ptr_ptr); } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); }
static void zend_assign_to_variable_reference(zval **variable_ptr_ptr, zval **value_ptr_ptr TSRMLS_DC) { zval *variable_ptr = *variable_ptr_ptr; zval *value_ptr = *value_ptr_ptr; if (variable_ptr == &EG(error_zval) || value_ptr == &EG(error_zval)) { variable_ptr_ptr = &EG(uninitialized_zval_ptr); } else if (variable_ptr != value_ptr) { if (!PZVAL_IS_REF(value_ptr)) { //此时右值不是一个引用 /* break it away */ Z_DELREF_P(value_ptr); //refcount_gc减1的作用 是看 是否还有其他变量也使用了valu_ptr_ptr对应的zval,如果有,则重新分配zval if (Z_REFCOUNT_P(value_ptr)>0) { ALLOC_ZVAL(*value_ptr_ptr); ZVAL_COPY_VALUE(*value_ptr_ptr, value_ptr); value_ptr = *value_ptr_ptr; zendi_zval_copy_ctor(*value_ptr); } Z_SET_REFCOUNT_P(value_ptr, 1); //因为上面减1了,所以这里要加1, Z_SET_ISREF_P(value_ptr); //设置is_ref为1 } *variable_ptr_ptr = value_ptr; //将variable_ptr_ptr这个地址指针内容 为 1 的地址 Z_ADDREF_P(value_ptr); //还要将 refcount_gc加1 zval_ptr_dtor(&variable_ptr); //根据情况释放内存 } else if (!Z_ISREF_P(variable_ptr)) { if (variable_ptr_ptr == value_ptr_ptr) { SEPARATE_ZVAL(variable_ptr_ptr); } else if (variable_ptr==&EG(uninitialized_zval) || Z_REFCOUNT_P(variable_ptr)>2) { /* we need to separate */ Z_SET_REFCOUNT_P(variable_ptr, Z_REFCOUNT_P(variable_ptr) - 2); ALLOC_ZVAL(*variable_ptr_ptr); ZVAL_COPY_VALUE(*variable_ptr_ptr, variable_ptr); zval_copy_ctor(*variable_ptr_ptr); *value_ptr_ptr = *variable_ptr_ptr; Z_SET_REFCOUNT_PP(variable_ptr_ptr, 2); } Z_SET_ISREF_PP(variable_ptr_ptr); } }
ZEND_API void _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC) { switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) { case IS_RESOURCE: { TSRMLS_FETCH(); zend_list_addref(zvalue->value.lval); } break; case IS_BOOL: case IS_LONG: case IS_NULL: break; case IS_CONSTANT: case IS_STRING: CHECK_ZVAL_STRING_REL(zvalue); if (!IS_INTERNED(zvalue->value.str.val)) { zvalue->value.str.val = (char *) estrndup_rel(zvalue->value.str.val, zvalue->value.str.len); } break; case IS_ARRAY: case IS_CONSTANT_ARRAY: { zval *tmp; HashTable *original_ht = zvalue->value.ht; HashTable *tmp_ht = NULL; TSRMLS_FETCH(); if (zvalue->value.ht == &EG(symbol_table)) { return; /* do nothing */ } ALLOC_HASHTABLE_REL(tmp_ht); zend_hash_init(tmp_ht, zend_hash_num_elements(original_ht), NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(tmp_ht, original_ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); zvalue->value.ht = tmp_ht; } break; case IS_OBJECT: { TSRMLS_FETCH(); Z_OBJ_HT_P(zvalue)->add_ref(zvalue TSRMLS_CC); } break; } }
#define CHECK_ZVAL_STRING_REL(z) \ if (Z_STRVAL_P(z)[ Z_STRLEN_P(z) ] != '\0') { zend_error(E_WARNING, "String is not zero-terminated (%s) (source: %s:%d)", Z_STRVAL_P(z) ZEND_FILE_LINE_RELAY_CC); }
#define estrndup_rel(s, length) _estrndup((s), (length) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { char *p; #ifdef ZEND_SIGNALS TSRMLS_FETCH(); #endif HANDLE_BLOCK_INTERRUPTIONS(); p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); if (UNEXPECTED(p == NULL)) { HANDLE_UNBLOCK_INTERRUPTIONS(); return p; } memcpy(p, s, length); p[length] = 0; HANDLE_UNBLOCK_INTERRUPTIONS(); return p; }
static zend_always_inline void i_zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) { if (!Z_DELREF_P(zval_ptr)) { TSRMLS_FETCH(); ZEND_ASSERT(zval_ptr != &EG(uninitialized_zval)); GC_REMOVE_ZVAL_FROM_BUFFER(zval_ptr); zval_dtor(zval_ptr); efree_rel(zval_ptr); } else { TSRMLS_FETCH(); if (Z_REFCOUNT_P(zval_ptr) == 1) { Z_UNSET_ISREF_P(zval_ptr); } GC_ZVAL_CHECK_POSSIBLE_ROOT(zval_ptr); } }
ZEND_API void _zval_dtor_func(zval *zvalue ZEND_FILE_LINE_DC) { switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) { case IS_STRING: case IS_CONSTANT: CHECK_ZVAL_STRING_REL(zvalue); STR_FREE_REL(zvalue->value.str.val); break; case IS_ARRAY: case IS_CONSTANT_ARRAY: { TSRMLS_FETCH(); if (zvalue->value.ht && (zvalue->value.ht != &EG(symbol_table))) { /* break possible cycles */ Z_TYPE_P(zvalue) = IS_NULL; zend_hash_destroy(zvalue->value.ht); FREE_HASHTABLE(zvalue->value.ht); } } break; case IS_OBJECT: { TSRMLS_FETCH(); Z_OBJ_HT_P(zvalue)->del_ref(zvalue TSRMLS_CC); } break; case IS_RESOURCE: { TSRMLS_FETCH(); /* destroy resource */ zend_list_delete(zvalue->value.lval); } break; case IS_LONG: case IS_DOUBLE: case IS_BOOL: case IS_NULL: default: return; break; } }
#define efree_rel(ptr) _efree((ptr) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
ZEND_API void _efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { TSRMLS_FETCH(); if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) { AG(mm_heap)->_free(ptr); return; } _zend_mm_free_int(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); }
相关文章推荐
- 变量赋值(引用) php内核的实现(二)
- php源码之路第三章第六节( 变量的生命周期之变量的赋值和销毁)
- php中变量赋值是引用的例子
- php变量引用赋值一些知识
- 关于php变量的赋值和引用的区别
- php变量赋值与引用
- php三天基础笔记(6)变量的传值赋值和引用赋值 (7)变量销毁 (动态变量名)
- php引用赋值后,其中一个变量销毁,另一个变量,如何变化!
- PHP变量引用赋值与变量赋值变量的区别
- php变量赋值与引用
- PHP源码分析-变量的引用计数、写时复制(Reference counting & Copy-on-Write)
- PHP内核探索:变量赋值与销毁
- php的list()的一步操作给一组变量进行赋值的使用
- PHP内核探索:变量的引用与计数规则
- php引用(&)变量引用,函数引用,对象引用和参数引用用法详解
- php 传值赋值与引用赋值的区别
- php unset函数 赋值 null来销毁变量
- 深入理解PHP原理之变量分离/引用(Variables Separation)
- PHP判断变量是否存在并且己赋值安全的写法
- php & 引用和 变量赋值的区别