为了账号安全,请及时绑定邮箱和手机立即绑定

PHP变量和数据类型

标签:
PHP

编程语言可以分为三大类

1. 静态类型语言,比如:C/Java等,在静态语言类型中,类型的检查是在编译期(compile-time)确定的。

2. 动态语言类型,比如:PHP,python等各种脚本语言,这类语言中的类型是在运行时确定的。

3. 无类型语言,比如:汇编语言,汇编语言操作的是底层存储,他们对类型毫无感知。

 

一、变量的结构和类型

1)变量的存储结构

在官方的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之一。

 

2)变量的值存储

上面的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;


 

3)哈希表(HashTable)

a. 键(key):用于操作数据的标示,例如PHP数组中的索引,或者字符串键等等。

b. 槽(slot/bucket):哈希表中用于保存数据的一个单元,也就是数据真正存放的容器。

c. 哈希函数(hash function):将key映射(map)到数据应该存放的slot所在位置的函数。

d. 哈希冲突(hash collision):哈希函数将两个不同的key映射到同一个索引的情况。

 

4)PHP的哈希实现

PHP的大部分的语言特性都是基于哈希表实现的, 例如:变量的作用域、函数表、类的属性、方法等,Zend引擎内部的很多数据都是保存在哈希表中的。

PHP中的哈希表是使用拉链法来解决冲突的,Zend为了保存数据之间的关系使用了双向链表来链接元素。

拉链法如下图所示:


图中,”John Smith”和”Sandra Dee” 通过哈希函数都指向了152 这个索引,该索引又指向了一个链表, 在链表中依次存储了这两个字符串。

Zend引擎哈希表结构和关系如下:


a. Bucket结构体维护了两个双向链表,pNext和pLast指针分别指向本槽位所在的链表的关系。

b. 而pListNext和pListLast指针指向的则是整个哈希表所有的数据之间的链接关系。

c. HashTable结构体中的pListHead和pListTail则维护整个哈希表的头元素指针和最后一个元素的指针。

 

二、常量

PHP内核会在词法解析时将这些常量的内容赋值进行替换,而不是在运行时进行分析。

1)常量的存储结构

常量是在变量的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');

 

2)标准常量

PHP内置定义的常量,他们属于标准常量。如错误报告级别E_ALL, E_WARNING等。

这些常量都是持久化常量。

 

3)魔术常量

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();//1static_function();//2static_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());//1print_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函数解释执行。

1)变量的声明和赋值

在使用一个变量时,我们不需要声明,也不需要初始化,直接对其赋值就可以使用。

$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

 

2)变量的作用域

变量按作用域类型分为:全局变量和局部变量。

与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)中。所以不会在函数结束的时候销毁。

 

3)global

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;    //nullvar_dump('prase'.$str);$str = true;    //booleanvar_dump('prase'.$str);$str = array(1,2);    //arrayvar_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,这个还是第一次见到


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
64
获赞与收藏
367

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消