PHP变量在内核中的存储方式

PHP是弱类型语言,也就是说一个PHP变量可以保存任何的数据类型。但是PHP是使用C语言编写的,而C语言是强类型的语言,每个变量都有固定类型,不能随意改变变量的类型(可以通过强类型转换改变,不过有可能出现问题),在Zend引擎中是怎么做到一个变量保存任何的数据类型呢?

打开Zend/zend.h文件,会发现以下一些结构体:

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
    zend_ast *ast;
} zvalue_value;

struct _zval_struct {
    /* Variable information */
    zvalue_value value;     /* value */
    zend_uint refcount__gc;
    zend_uchar type;    /* active type */
    zend_uchar is_ref__gc;
};

zval结构体就是通常用到的PHP变量(如$variable)在内核中的表示方式。在zval结构体中,可以看到4个成员变量,分别是

  • zvalue_value value : 变量的值,PHP变量的值就保存在这里。
  • zend_uint refcount__gc : 变量引用数,变量引用计算器。
  • zend_uchar type;zend_uchar type : 变量的类型。
  • zend_uchar is_ref__gc : 变量是否被引用。

zval结构体的value成员变量是一个zvalue_value联合体,PHP能够保持任何的结构类型就是因为这个联合体。从zvalue_value联合体的成员变量中可以看到,不同的类型会保存到不同的成员变量中,这样就实现了PHP变量可以存储任何数据类型。例如,当变量是整数类型时,会保存到value的lval成员变量中;当变量的类型是字符串时,又会保存到value的str成员变量中。如下表展示了不同类型保存到对应的成员变量中。

PHP语言层类型 保存在zvalue_value的成员变量
long,bool,resoure lval
double dval
string str(len保存字符串的长度,val保存字符串的值)
array ht
object obj

现在已经解决了一个PHP变量可以保存任意类型的问题,但是另一个问题又出现了,就是Zend引擎是怎么知道这个变量保存的是什么类型呢?我们注意到,zval结构体中有个type成员变量,这个成员变量就是保存一个PHP变量的类型。

Zend引擎定义了几种变量类型,如下:

#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

每一个宏定义对应PHP语言层的一种类型,例如当zval的type成员变量等于IS_STRING时(zval.type==IS_STRING),说明这个变量的类型时字符串类型。

宏定义 表示类型
IS_NULL NULL类型(null)
IS_LONG 整数类型(int)
IS_DOUBLE 浮点类型(float)
IS_STRING 字符串类型(string)
IS_ARRAY 数组类型(array)
IS_OBJECT 对象类型(object)
IS_BOOL 布尔类型(bool)
IS_RESOURCE 资源类型(resource)

可以通过下面的代码打印一个zval的类型:

switch(zval.type) {
    case IS_NULL:
        php_printf("zval type is null\n");
        break;
    case IS_STRING:
        php_printf("zval type is string\n");
        break;
    case IS_LONG:
        php_printf("zval type is long\n");
        break;
    case IS_ARRAY:
        php_printf("zval type is array\n");
        break;

    ...
}

你可能感兴趣的:(php,PHP学习历程)