Stored procedure decomposition of variables in PHP source code analysis

  • 2021-07-07 06:37:24
  • OfStack

The PHP code is as follows:

$php_var = 1;  

The code corresponding to C is:

zval* c_var;    // Definition PHP Variable pointer   
MAKE_STD_ZVAL(c_var);  // Initialization PHP Variable  
ZVAL_LONG(c_var,1) ;// Assignment  
ZEND_SET_SYMBL( EG(active_symbol_table), " php_var ", c_var);// Register to Global Variable Symbol Table

1. First look at line 1: zval* c_var; //Declare 1 zval pointer c_var; The structure of zval is as follows:


struct _zval_struct { 
    /* Variable information */ 
    zvalue_value value;     /* Value of variable */ 
    zend_uint refcount;     /* Reference count , Used in garbage collection */ 
    zend_uchar type;        /* Variable type */ 
    zend_uchar is_ref;      /* Is it a reference variable */ 
}; 
typedef struct _zval_struct zval; 

Where the value zvalue_value is structured as follows:


typedef union _zvalue_value { 
    long lval;              /* Long plastic */ 
    double dval;            /* Double precision type */ 
    struct {                  /* Values of type string */ 
        char *val;             
        int len; 
    } str; 
    HashTable *ht;              /* Values of array type */ 
    zend_object_value obj;     /* Value of object type */ 
} zvalue_value; 

2. Next, look at line 2: MAKE_STD_ZVAL (new_val); //Variable initialization related macros are as follows://Initialize


#define MAKE_STD_ZVAL(zv)                \ 
    ALLOC_ZVAL(zv); \ 
    INIT_PZVAL(zv); 
 
#define ALLOC_ZVAL(z)   \ 
    ZEND_FAST_ALLOC(z, zval, ZVAL_CACHE_LIST) 
 
#define ZEND_FAST_ALLOC(p, type, fc_type)   \ 
    (p) = (type *) emalloc(sizeof(type)) 
 
#define INIT_PZVAL(z)       \ 
    (z)->refcount = 1;      \ 
    (z)->is_ref = 0; 


Expand to read:


(c_var) = (zval *) emalloc(sizeof(zval));  // Allocate memory  
(c_var)-> refcount = 1;  // Reference count initialization  
(c_var)-> is_ref = 0; // Whether to reference  

It can be seen that its function is to allocate memory and initialize refcount, is_ref

3. Look at Line 3 ZVAL_LONG (c_var, 1) The related macros are:


// Definition value  
#define ZVAL_LONG(z, l) {           \ 
     Z_TYPE_P(z) = IS_LONG;      \ 
     Z_LVAL_P(z) = l;            \ 

#define Z_TYPE_P(zval_p)    Z_TYPE(*zval_p) 
#define Z_TYPE(zval)        (zval).type 
#define Z_LVAL_P(zval_p)    Z_LVAL(*zval_p) 
#define Z_LVAL(zval)            (zval).value.lval 

Expand to read:


(* c_var).type = IS_LONG; 
(* c_var).value = 1; 

4: Next, look at line 4: ZEND_SET_SYMBOL (EG (active_symbol_table), "php_var", c_var); First of all, it is explained that the variables of PHP exist in one hashtable


struct _zend_executor_globals {   
        … . 
        HashTable symbol_table;// Symbol table of global variables    
        HashTable *active_symbol_table;// Symbol table of local variables    
        … .. 
    };   

Key of Hashtable is the name of the variable, that is, php_var, and the value is a pointer to the PHP variable, that is, an c_var pointer; The related macros are:


#define ZEND_SET_SYMBOL(symtable, name, var)          \   {                                                     \ 
        char *_name = (name);                         \ 
        ZEND_SET_SYMBOL_WITH_LENGTH(symtable, _name, strlen(_name)+1, var, 1, 0);   \ 

// The main implementation is the following function:  
#define ZEND_SET_SYMBOL_WITH_LENGTH(symtable, name, name_length, var, _refcount, _is_ref)                                                       \ 
    {                                                                        
        zval **orig_var;                                        \  
        if (zend_hash_find(symtable, (name), (name_length), (void **) &orig_var)==SUCCESS                                                         \ 
            && PZVAL_IS_REF(*orig_var)) {                     \ 
            (var)->refcount = (*orig_var)->refcount;                  \ 
            (var)->is_ref = 1;                                \ 
            if (_refcount) {                                      \ 
                (var)->refcount += _refcount-1;               \ 
            }                                             \ 
            zval_dtor(*orig_var);                             \ 
            **orig_var = *(var);                                  \ 
            FREE_ZVAL(var);                               \ 
        } else {                                              \ 
            (var)->is_ref = _is_ref;                              \ 
            if (_refcount) {                                      \ 
                (var)->refcount = _refcount;                      \ 
            }                                             \ 
            zend_hash_update(symtable, (name), (name_length), &(var), sizeof(zval *), NULL);                                                           \ 
        }                                                  \ 
    }            

The functions of this function are:
1. If the variable already exists in the global symbol table and is a reference type, then

a. Assign the reference count refcount and is_ref information of the original variable to c_var;
b. Release the value of the original variable zvalue, for example, if the original value points to an mysql connection resource, then release the resource.
c. Assign the variable pointed to by c_var to the original variable d. Release the memory space of c_var. This ensures that if the variable is applied, the value 1 changes. For example, if you have $b= in front of you & a;

2. If the variable does not exist in the global symbol table or if the variable exists but is not a reference variable, change its value directly.


Related articles: