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