编程语言可以分为三大类
1. 静态类型语言,比如:C/Java等,在静态语言类型中,类型的检查是在编译期(compile-time)确定的。
2. 动态语言类型,比如:PHP,python等各种脚本语言,这类语言中的类型是在运行时确定的。
3. 无类型语言,比如:汇编语言,汇编语言操作的是底层存储,他们对类型毫无感知。
在官方的PHP实现内部,所有变量使用同一种数据结构(zval)来保存。 它不仅仅包含变量的值,也包含变量的类型。
在PHP中,存在8种变量类型,可以分为三类:
a. 标量类型: boolean、integer、float(double)、string
b. 复合类型: array、object
c. 特殊类型: resource、NULL
变量存储结构如下:
struct _zval_struct { zvalue_value value; /* 存储变量的值 是个联合体*/ zend_uint refcount__gc; /*表示引用计数 默认1*/ zend_uchar type; /* 变量具体的类型 */ zend_uchar is_ref__gc; /*表示是否为引用 默认0*/ };
type的值可以为: IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE之一。
上面的value是个联合体,正因为是这样,才能做到弱类型,联合体如下:
typedef union _zvalue_value { long lval; /*boolean integer*/ double dval; /*float*/ struct { char *val; int len; } str; /*String*/ HashTable *ht; /* Array */ zend_object_value obj; /*Object*/ } zvalue_value;
a. 键(key):用于操作数据的标示,例如PHP数组中的索引,或者字符串键等等。
b. 槽(slot/bucket):哈希表中用于保存数据的一个单元,也就是数据真正存放的容器。
c. 哈希函数(hash function):将key映射(map)到数据应该存放的slot所在位置的函数。
d. 哈希冲突(hash collision):哈希函数将两个不同的key映射到同一个索引的情况。
PHP的大部分的语言特性都是基于哈希表实现的, 例如:变量的作用域、函数表、类的属性、方法等,Zend引擎内部的很多数据都是保存在哈希表中的。
PHP中的哈希表是使用拉链法来解决冲突的,Zend为了保存数据之间的关系使用了双向链表来链接元素。
拉链法如下图所示:
图中,”John Smith”和”Sandra Dee” 通过哈希函数都指向了152 这个索引,该索引又指向了一个链表, 在链表中依次存储了这两个字符串。
Zend引擎哈希表结构和关系如下:
a. Bucket结构体维护了两个双向链表,pNext和pLast指针分别指向本槽位所在的链表的关系。
b. 而pListNext和pListLast指针指向的则是整个哈希表所有的数据之间的链接关系。
c. HashTable结构体中的pListHead和pListTail则维护整个哈希表的头元素指针和最后一个元素的指针。
PHP内核会在词法解析时将这些常量的内容赋值进行替换,而不是在运行时进行分析。
常量是在变量的zval结构的基础上添加了一额外的元素。
typedef struct _zend_constant { zval value; /* zval结构,PHP内部变量的存储结构,在第一小节有说明 */ int flags; /* 常量的标记如 CONST_PERSISTENT | CONST_CS | CONST_CT_SUBST*/ char *name; /* 常量名称 */ uint name_len; int module_number; /* 模块号 */ } zend_constant;
1. CONST_CS :常量大小写敏感
2. CONST_PERSISTENT :常量需要持久化;如果是非持久常量,会在RSHUTDOWN阶段就将该常量释放,否则只会在MSHUTDOWN阶段将内存释放。用户定义的常量都是非持久化的,通常扩展和内核定义的常量会设置为持久化。
3. CONST_CT_SUBST :Allow compile-time substitution(在编译时可被替换)。在PHP内核中这些常量包括:TRUE、FALSE、NULL、ZEND_THREAD_SAFE和ZEND_DEBUG_BUILD五个。
PHP常量的定义过程如下:
define('KFJ', 'Hello World');
PHP内置定义的常量,他们属于标准常量。如错误报告级别E_ALL, E_WARNING等。
这些常量都是持久化常量。
PHP中有七个魔术常量,他们的值其实是变化的,它们的值随着它们在代码中的位置改变而改变。 所以称他们为魔术常量。
对于全部脚本而言,PHP 提供了大量的 预定义变量 。这些变量将所有的外部变量表示成内建环境变量,并且将错误信息表示成返回头。
$_GET,$_POST,$_SERVER,$_FILES等变量,会在PHP脚本运行之前就将它们加入到HashTable数据类型的符号表中。
由于都存储在一个地方,所以在某个局部函数中使用类似于$GLOBALS变量这样的预定义变量, 如果在此函数中有改变的它们的值的话,这些变量在其它局部函数调用时会发现也会同步变化。
通常意义上静态变量是静态分配的,他们的生命周期和程序的生命周期一样, 只有在程序退出时(RSHUTDOWN)才结束期生命周期,这和局部变量相反,局部变量只有在函数执行时才会存在。 通常,当一个函数执行完毕,它的局部变量的值就已经不存在,而且变量所占据的内存也被释放。
静态变量可以分为3中:
1)静态 全局 变量:PHP中的 全局变量 (预定义变量等)也可以理解为静态全局变量,因为除非明确unset释放,在程序运行过程中始终存在。
2)静态 局部 变量:也就是在 函数内定义 的静态变量,Zend为每个函数分配了一个私有的符号表(EG(active_op_array)->static_variables)来保存该函数的静态变量。
3)静态 成员 变量:这是在 类中定义 的静态变量,和实例变量相对应,静态成员变量属于类,不属于某个实例,所以可以在所有实例中共享。
查看在线代码 :
//静态局部变量 function static_function() { static $i=0; $i++; print_r($i); } static_function();//1 static_function();//2 static_function();//3
//静态成员变量 //Zend为每个函数分配了一个私有的符号表来保存该函数的静态变量。 class static_class { public static $i=0; public function get_static() { return ++self::$i; } } $class1 = new static_class(); $class2 = new static_class(); print_r($class1->get_static());//1 print_r($class2->get_static());//2
PHP5中引入了类型提示这个概念。在定义方法参数时,同时定义参数的对象类型。
下面的示例代码就是类型提示,但是在引用的时候传入1,就会报错。
function prompt(Array $arr) { print_r($arr); } prompt(1);
类型提示的实现有2种:
1)参数声明时的类型提示,例如“$arr=[1,2];”
2)函数或方法调用时的类型提示(上面的示例代码)
在ZE进行词法和语法的分析之后,生成具体的opcode,这些opcode最终被execute函数解释执行。
在使用一个变量时,我们不需要声明,也不需要初始化,直接对其赋值就可以使用。
$a = 10;
当赋值的时候,zval结构中的refcount_gc默认为1,当引用这个值的时候,会加1。
$a = 10; $b = &$a;//$a和$b引用了同一个zval结构,refcount_gc变为2,is_ref_gc为1 $c = $a;//$c新建了一个zvak结构,refcount_gc变为2为1
变量按作用域类型分为:全局变量和局部变量。
与JavaScript不同,得益于闭包的特性,JavaScript可以在函数中调用函数外的变量,而PHP不行。下面的代码是错误的:
$bar = 'outter'; function _global() { print_r($bar);//这里会报错 } _global(); print_r($bar);//输出为outter
a. 全局变量会保存在symbol_table中, 也就是顶层作用域中的变量。
b. 函数或者对象的方法在被调用时会创建active_symbol_table来保存局部变量。
c. 函数中的静态变量存放在私有的符号表(EG(active_op_array)->static_variables)中。所以不会在函数结束的时候销毁。
global语句的作用是定义全局变量,也就是将变量放到symbol_table中。
将上面的代码修改一下,增加一个global声明:
$bar = 'outter'; function _global() { global $bar ;//添加global声明 $bar = 'inner'; print_r($bar); } _global(); print_r($bar);//输出inner
1)隐式类型转换
a. 直接的变量赋值操作
b. 运算式结果对变量的赋值操作
$str = 33; //integer 另外的float(double)类似 var_dump('prase'.$str); $str = null; //null var_dump('prase'.$str); $str = true; //boolean var_dump('prase'.$str); $str = array(1,2); //array var_dump('prase'.$str); $str = new static_class(); //object 调用静态变量中的类 var_dump('prase'.$str);
a. 隐式转换null的时候,最后输出的是空
b. boolean转换成了0或1
c. 虽然array最后输出了,但最后还是报错。
d. 而类是直接报错,没有输出。
2)显示类型转换
PHP中允许的强制类型有:
a. (int), (integer) 转换为整型
b. (bool), (boolean) 转换为布尔类型
c. (float), (double) 转换为浮点类型
d. (string) 转换为字符串
e. (array) 转换为数组
f. (object) 转换为对象
g. (unset) 转换为NULL,这个还是第一次见到
参考文档:
http://www.php-internals.com/book/?p=chapt03/03-01-00-variables-structure 变量的结构和类型
http://www.cnblogs.com/yangecnu/archive/2014/10/31/Introduce-Hashtable.html 浅谈算法和数据结构: 十一 哈希表
http://www.php-internals.com/book/?p=chapt03/03-02-const-var 常量
http://www.php-internals.com/book/?p=chapt03/03-03-pre-defined-variable 预定义变量
http://www.php-internals.com/book/?p=chapt03/03-04-static-var 静态变量
http://www.php-internals.com/book/?p=chapt03/03-05-impl-of-type-hint 类型提示的实现
http://www.php-internals.com/book/?p=chapt03/03-06-00-var-lifecycle 变量的生命周期
http://www.php-internals.com/book/?p=chapt03/03-06-01-var-define-and-init 变量的赋值和销毁
http://www.php-internals.com/book/?p=chapt03/03-06-02-var-scope 变量的作用域
http://www.php-internals.com/book/?p=chapt03/03-06-03-var-global global语句
http://www.php-internals.com/book/?p=chapt03/03-07-type-cast 数据类型转换