php变量

在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。

 

 

 

posted @ 2013-01-19 16:22  风去无痕  阅读(340)  评论(0编辑  收藏  举报