变量赋值(引用) php内核的实现(二)
<?php
$a=1;
$b=&$a;
$c=2;
$a=&$c;
echo $a."\n";
echo $b;
2
1
结论:
首先保存 左值的内存地址, 因这个内存地址会被再次被赋值
1)右值是引用
进入2.2 2.3 2.4步骤
例子:
<?php
$a=1;
$c=2;
$b=&$a; //执行到这里时,属于第2种情况
$c=&$a; //执行到这里时,属于第1种情况,
2)右值不是引用,右值的refcount_gc减1
2.1)如果refcount_gc减1,大于0 ,说明有别的变量也共同使用了zval,需要单独分配内存给右值
2.2)将右值(内存地址)赋值给左值
2.3)refcount_gc 加1,并设置 is_ref=1
2.4)销毁左值
2.3.1)将上面保存的左值的zval的refcount_gc减1
2.3.1.1)上面值为0,则zval_dtor
2.3.1.2)上面值大于0,则进入GC buffer, 但zval类型必须为 object或 array

可以发现$a,$b,$c全是CV变量
当php解释器执行到$b=&$a时,会执行到下面的handler
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(EX_CVs(), 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(EX_CVs(), 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)>) {
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,
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)>) {
/* we need to separate */
Z_SET_REFCOUNT_P(variable_ptr, Z_REFCOUNT_P(variable_ptr) - );
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, );
}
Z_SET_ISREF_PP(variable_ptr_ptr);
}
}
//zend_variables.c 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, );
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;
}
}
//zend_API.h #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); }
//zend_alloc.h #define estrndup_rel(s, length) _estrndup((s), (length) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
//zend_alloc.c 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+ 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] = ;
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
//zend_execute_API.c
ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */
{
#if DEBUG_ZEND>=2
printf("Reducing refcount for %x (%x): %d->%d\n", *zval_ptr, zval_ptr, Z_REFCOUNT_PP(zval_ptr), Z_REFCOUNT_PP(zval_ptr) - );
#endif
Z_DELREF_PP(zval_ptr);
if (Z_REFCOUNT_PP(zval_ptr) == ) {
TSRMLS_FETCH(); if (*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_PP(zval_ptr) == ) {
Z_UNSET_ISREF_PP(zval_ptr);
} GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr);
}
}
//zend_variables.c 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;
}
}
//zend.h
#define STR_FREE_REL(ptr) if (ptr && !IS_INTERNED(ptr)) { efree_rel(ptr); }
#define efree_rel(ptr) _efree((ptr) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
//zend_alloc.c
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);
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *variable="abc";
char **variable_ptr_ptr=variable; char *variable_ptr=*variable_ptr_ptr; char *value="def";
char **value_ptr_ptr=value;
char *value_ptr=*value_ptr_ptr; *variable_ptr_ptr=value_ptr;
return ;
}
变量赋值(引用) php内核的实现(二)的更多相关文章
- Makefile笔记之一 ------ 变量的引用及赋值
1.变量的引用方式: "$(变量名)"或者"¥{变量名}" 例如: ${Objs}就是取变量Objs的值 注意: 当变量名为单字符是可以采用:"$a& ...
- PHP:第一章——php中的变量001 /普通赋值/引用赋值/php变量的检查与销毁
<?php //php中的变量: //php中的变量用一个美元符$后面紧跟着变量名来表示,变量名是区分大小写的. //有效的变量只能是字母或者下划线开头,后面跟任意数量的字母.数字.或者下划线. ...
- PHP变量引用赋值与变量赋值变量的区别
变量默认总是传值赋值.那也就是说,当将一个表达式的值赋予一个变量时,整个原始表达式的值被赋值到目标变量.这意味着,例如,当一个变量的值赋予另外一个变量时,改变其中一个变量的值,将不会影响到另外一个变量 ...
- 细说PHP-5.3.4变量的引用赋值
变量总是传值赋值.也就是说,当讲一个表达式的值赋予一个变量时,整个原始表达式的值被赋值到目标变量.这意味着,当一个变量的值赋予另个一变量时,改变其中一个变量的值,将不会影响到另一个变量.PHP中提供了 ...
- Linux Shell编程变量赋值和引用
我们可以使用任意一种文字编辑器,比如gedit.kedit.emacs.vi等来编写shell脚本,它必须以如下行开始(必须放在文件的第一行): #!/bin/sh ... 注意:最好使用“! ...
- Linux —— Shell编程之变量赋值和引用
Linux的shell编程是一种非常成熟的编程语言,它支持各种类型的变量.有三种主要的变量类型:环境变量.内部变量和用户变量. 环境变量(environment variable)是系统环境的一部分, ...
- 变量改变时PHP内核做了些什么?
引言 内容来自于<Extending and Embedding PHP>- Chaper 3 - Memory Management,加上自己的理解,对php中变量的引用计数.写时复制, ...
- Python中的变量、引用、拷贝和作用域
在Python中,变量是没有类型的,这和以往看到的大部分编辑语言都不一样.在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可.但是,当用变量的时候,必须要给这个变量赋值:如果只写一个变量,而没 ...
- 走出 null 就是空值的误区,以及变量赋值原理
先放一张图片作为引入: 这里我用了一个示意图作为讲解: 平时,我们写的变量为什么能在我们调用它的时候就能被我们拿到所用,跟存钱罐一样,你往里面存一元大洋,它里面就有一元大洋,那么我们的变量在被我们创建 ...
随机推荐
- 装tortoiseSVN时遇到的坑
今天给新电脑配置开发环境,线上SVN地址申请权限以后,在本地装了tortoiseSVN,可是安装了以后死活打不开.明明浏览器里可以打开SVN地址,但是tortoiseSVN的浏览器里却打不开,摆弄了很 ...
- 1056. Mice and Rice (25)
时间限制 30 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Mice and Rice is the name of a pr ...
- Android 管理Activity中的fragments
为了管理Activity中的fragments,需要使用FragmentManager,为了得到它,需要调用Activity中的getFragmentManager()方法,接下来详细介绍,感兴趣的朋 ...
- Group 原则
select 后面的所有劣种,没有使用聚合函数的列,必须出现在 group by 后面,比如下面的例子 insert into #temp_move2 ) from #temp_move group ...
- aaaa
http://www.host.com http://www.host.com http://sz.weixun.com/scenery/details-3.htm http://sz.weixun. ...
- 1023: [SHOI2008]cactus仙人掌图 - BZOJ
Description如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路 ...
- 1003: [ZJOI2006]物流运输trans - BZOJ
Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过 ...
- 如何在JavaScript里防止事件函数的高频触发和调用
网页中JavaScript最基本的功能是监听或响应用户的动作,这非常的有用.用户的动作有些频率非常高,有的十分罕见.有些监听器函数的执行如闪电般完成,而有些繁重的会把浏览器拖死.拿浏览器窗口的resi ...
- setTimeout浅析
刚学习javascript的时候,感觉setTimeout很好理解,不就是过n(传入的毫秒数)毫秒,执行以下传入的函数吗?这个理解伴随了我挺长的一段时间,才对setTimeout有了新的认识,请先看下 ...
- Makefile教程
Makefile学习教程: 跟我一起写 Makefile 0 Makefile概述 0.1 关于程序的编译和链接 1 Makefile 介绍 1.1 Makefile的规则 1.2 一个示例 1.3 ...