对zend中变量的一些说明

在php中变量类型和值是通过c语言实现的,那php内核具体是怎么实现的呢?


HashTable在php内核中广泛被使用,而变量就是存储在hashtable实现的符号表中.


当在PHP中调用一个函数或者类时,内核会创建一个新的符号表,这也是为什么在函数中无法使用函数外部定义的变量的原因。(因为他们分属两个符号表,一个当前作用域,一个全局作用域)




内核中作用域的定义,PHP的所有 局部变量,全局变量,函数,类的 Hash表 都在这里定义了
struct _zend_executor_globals {
zval **return_value_ptr_ptr;
 
zval uninitialized_zval;
zval *uninitialized_zval_ptr;
 
zval error_zval;
zval *error_zval_ptr;
 
zend_ptr_stack arg_types_stack;
 
/* symbol table cache */
HashTable *symtable_cache[SYMTABLE_CACHE_SIZE];
HashTable **symtable_cache_limit;
HashTable **symtable_cache_ptr;
 
zend_op **opline_ptr;
 
HashTable *active_symbol_table;  //局部变量
HashTable symbol_table; /* main symbol table */ //全局变量
 
HashTable included_files; /* files already included */ //include的文件
 
JMP_BUF *bailout;
 
int error_reporting;
int orig_error_reporting;
int exit_status;
 
zend_op_array *active_op_array;
 
HashTable *function_table; /* function symbol table */ //函数表
HashTable *class_table; /* class table */ //类表
HashTable *zend_constants; /* constants table */ //常量表
 
zend_class_entry *scope;
zend_class_entry *called_scope; /* Scope of the calling class */
 
zval *This;
 
long precision;
 
int ticks_count;
 
zend_bool in_execution;
HashTable *in_autoload;
zend_function *autoload_func;
zend_bool full_tables_cleanup;
 
/* for extended information support */
zend_bool no_extensions;
 
#ifdef ZEND_WIN32
zend_bool timed_out;
OSVERSIONINFOEX windows_version_info;
#endif
 
HashTable regular_list;
HashTable persistent_list;
 
zend_vm_stack argument_stack;
 
int user_error_handler_error_reporting;
zval *user_error_handler;
zval *user_exception_handler;
zend_stack user_error_handlers_error_reporting;
zend_ptr_stack user_error_handlers;
zend_ptr_stack user_exception_handlers;
 
zend_error_handling_t error_handling;
zend_class_entry *exception_class;
 
/* timeout support */
int timeout_seconds;
 
int lambda_count;
 
HashTable *ini_directives;
HashTable *modified_ini_directives;
 
zend_objects_store objects_store;
zval *exception, *prev_exception;
zend_op *opline_before_exception;
zend_op exception_op[3];
 
struct _zend_execute_data *current_execute_data;
 
struct _zend_module_entry *current_module;
 
zend_property_info std_property_info;
 
zend_bool active;
 
void *saved_fpu_cw;
 
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};


我们可以通过宏EG来访问符号表, EG(active_symbol_table)访问当前作用域的符号变量表,EG(symbol_table)访问全局的变量符号表.


<?php
   $foo = 'bar';
?>
上面这段代码很简单,创建变量foo,并赋值bar.之后的php代码中可以调用变量$foo。


看一下变量$foo,在php内核中是怎么实现的.


zval* foo;  
MAKE_STD_ZVAL(foo);  //创建一个zval结构,并设置类型。
ZVAL_STRING(foo, "bar", 1); //赋值
ZEND_SET_SYMBOL( EG(active_symbol_table), "foo", foo); //将其加入当前作用域符号表,只有这样用户才能在PHP里使用这个变量。
通过简单的这三步,即可实现定义PHP变量。简单的原因,在于内核为我们提供了强大的宏。现在我们将宏分别展开。


#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__gc = 1;(z)->is_ref__gc = 0;
MAKE_STD_ZVAL(foo)展开后得到:


(foo) = (zval *) emalloc(sizeof(zval));  
(foo)->refcount__gc = 1;  //引用次数
(foo)->is_ref__gc = 0;   //是否被引用
可以看出,MAKE_STD_ZVAL做了三件事:分配内存、初始化zval结构中的refcount、is_ref.


ZVAL_STRING用到的宏:




#define ZVAL_STRING(z, s, duplicate) {    \
        const char *__s=(s);            \
        Z_STRLEN_P(z) = strlen(__s);    \
        Z_STRVAL_P(z) = (duplicate?estrndup(__s, Z_STRLEN_P(z)):(char*)__s);\
        Z_TYPE_P(z) = IS_STRING;        \
    }
#define Z_STRLEN_P(zval_p)        Z_STRLEN(*zval_p)
#define Z_STRLEN(zval)            (zval).value.str.len
#define Z_STRVAL_P(zval_p)        Z_STRVAL(*zval_p)
#define Z_STRVAL(zval)            (zval).value.str.val
#define Z_TYPE_P(zval_p)            Z_TYPE(*zval_p)
#define Z_TYPE(zval)            (zval).type
#define IS_STRING                6


宏展开后的代码:


const char *__s=("foo");
(foo).value.str.len=strlen(__s);
(foo).value.str.val=(duplicate?estrndup(__s, (zval).value.str.len):(char*)__s);
(foo).type=6;
ZVAL_STRING的主要功能就是:设置数据类型和赋值。


ZEND_SET_SYMBOL使用到的一些宏:


# define EG(v) (executor_globals.v)
展开后的代码:


ZEND_SET_SYMBOL(executor_globals.active_symbol_table, "foo", foo);
将变量名入当前作用域符号表。


 


下面我来看一下zval的定义:


zval在Zend/zend.h中被定义,ypedef struct _zval_struct zval; //原来它是 _zval_struct 的别名




typedef union _zvalue_value {
        long lval;  //保存long类型的数据
        double dval; //保存 double类型的数据
        struct {
                char *val; //真正的值在这里
                int len;   //这里返回长度
        } str;
        HashTable *ht; //数组等
        zend_object_value obj; //这是一个对象
} zvalue_value;
 
struct _zval_struct {
zvalue_value value;             //保存的值
zend_uint refcount__gc;//被引用的次数 如果为1 则只被自己使用如果大于1 则被其他变量以&的形式引用.
zend_uchar type;       //数据类型 这也是 为什么 PHP是弱类型的原因
zend_uchar is_ref__gc;  //表示是否为引用
};


我们也可以通过写php扩展的方式来展示一下:




PHP_FUNCTION( test_test ){
        zval *value;
        char *s="create a php variable";
        //MAKE_STD_ZVAL
        value=(zval*)malloc(sizeof(zval));
        memset(value,0,sizeof(value));
        //ZVAL_STRING
        value->is_ref__gc=0; //非引用变量
        value->refcount__gc=1;//引用次数 只有自己
        value->type=IS_STRING;//类型为字符串
        value->value.str.val=s;//值
        value->value.str.len=strlen(s);//长度
        ZEND_SET_SYMBOL(EG(active_symbol_table),"a",value);
}






在这里我们创建了一个php的变量$a.


 


下面来讲一下php弱类型变量的实现.


Zend/zend_type.h


 25 typedef unsigned char zend_bool;
 26 typedef unsigned char zend_uchar;
 27 typedef unsigned int zend_uint;
 28 typedef unsigned long zend_ulong;
 29 typedef unsigned short zend_ushort;
根据zval的结构,可以看到_zvalue_value是真正保存数据的关键部分。通过共用体实现的弱类型变量声明。


Zend引擎是如何判别、存储PHP中的多种数据类型的呢?
_zval_struct.type中存储着一个变量的真正类型,根据type来选择如何获取zvalue_value的值




type值列表(Zend/zend.h):
#define IS_NULL        0
#define IS_LONG        1
#define IS_DOUBLE    2
#define IS_BOOL        3
#define IS_ARRAY    4
#define IS_OBJECT    5
#define IS_STRING    6
#define IS_RESOURCE    7
#define IS_CONSTANT    8
#define IS_CONSTANT_ARRAY    9


来看一个简单的列子:


<?php
    $a = 1;
    //此时zval.type = IS_LONG,那么zval.value就去取lval.
    $a = array();
    //此时zval.type = IS_ARRAY,那么zval.value就去取ht.
这其中最复杂的,并且在开发第三方扩展中经常需要用到的是"资源类型".
在PHP中,任何不属于PHP的内建的变量类型的变量,都会被看作资源来进行保存。
比如:数据库句柄、打开的文件句柄、打开的socket句柄。


资源类型,需要使用ZE提供的API函数来注册,资源变量的声明和使用将在单独的篇目中进行详细介绍。


正是因为ZE这样的处理方式,使PHP就实现了弱类型,而对于ZE的来说,它所面对的永远都是同一种类型zval。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值