首页 > 代码库 > 变量赋值 php内核的实现(二)

变量赋值 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 =http://www.mamicode.com/= ZEND_RETURNS_FUNCTION &&"color: #0000ff;">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 =http://www.mamicode.com/= 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 =http://www.mamicode.com/= 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;    //对于$b=&$a , variable_ptr存放的是$b对应的的值zval所对应的内存地址(这里并没有zval) , variable_ptr_ptr实现存放的是$b变量的地址, $b存放的是对应zval的内存地址    zval *value_ptr = *value_ptr_ptr;       //对于$b=&$a , value_ptr为 $a的值1的地址,value_ptr_ptr存放的是$a变量的地址,$a存放的是1的内存地址    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_variables.cZEND_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;    }}
//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.cZEND_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;}

 

//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) - 1);#endif Z_DELREF_PP(zval_ptr); if (Z_REFCOUNT_PP(zval_ptr) == 0) { 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) == 1) { Z_UNSET_ISREF_PP(zval_ptr); } GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr); }}

 

#include <stdio.h>#include <stdlib.h>int main(){    char *variable="abc";    char **variable_ptr_ptr=variable;    char *variable_ptr=*variable_ptr_ptr;    char *value=http://www.mamicode.com/"def";    char **value_ptr_ptr=value;    char *value_ptr=*value_ptr_ptr;    *variable_ptr_ptr=value_ptr;    return 0;                                                                                                                                        }