<?php $foo = 10; $bar = 20; function change() { global $foo; //echo '函数内部$foo = '.$foo.'<br />'; //若是不把$bar定义为global变量,函数体内是不能访问$bar的 $bar = 0; $foo++; } change(); echo $foo, ' ', $bar; ?>
程序输出 11 20。缘由是,方法内部没法访问$bar变量,因此它的值仍是20。使用global以后,能够取得$foo的值,自增后$foo的值就是11。php
Global的做用是定义全局变量,可是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require的全部文件。数据库
前言中提到变量的三个基本特性,其中的有一个特性为变量的类型,变量都有特定的类型, 如:字符串、数组、对象等等。编程语言的类型系统能够分为强类型和弱类型两种:编程
强类型语言是一旦某个变量被申明为某个类型的变量,则在程序运行过程当中,该不能将该变量的类型之外的值赋予给它 (固然并不彻底如此,这可能会涉及到类型的转换,后面的小节会有相应介绍),C/C++/Java等语言就属于这类。数组
PHP及Ruby,JavaScript等脚本语言属于弱类型语言:一个变量能够表示任意的数据类型。数据结构
PHP之因此成为一个简单而强大的语言,很大一部分的缘由是它拥有弱类型的变量。 可是有些时候这也是一把双刃剑,使用不当也会带来一些问题。就像仪器同样,越是功能强大, 出现错误的可能性也就越大。编程语言
在官方的PHP实现内部,全部变量使用同一种数据结构(zval)来保存,而这个结构同时表示PHP中的各类数据类型。 它不只仅包含变量的值,也包含变量的类型。这就是PHP弱类型的核心。函数
那zval结构具体是如何实现弱类型的呢,下面咱们一块儿来揭开面纱。性能
PHP在声明或使用变量的时候,并不须要显式指明其数据类型。网站
PHP是弱类型语言,这并不表示PHP没有类型,在PHP中,存在8种变量类型,能够分为三类ui
* 标量类型:boolean、integer、float(double)、string
* 复合类型: array、object
* 特殊类型: resource、NULL
官方PHP是用C实现的,而C是强类型的语言,那这是怎么实现PHP中的弱类型的呢?
变量的值存储到如下所示zval结构体中。 zval结构体定义在Zend/zend.h文件,其结构以下:
1 |
typedef struct _zval_struct zval; |
2 |
... |
3 |
struct _zval_struct { |
4 |
/* Variable information */ |
5 |
zvalue_value value; /* value */ |
6 |
zend_uint refcount__gc; |
7 |
zend_uchar type; /* active type */ |
8 |
zend_uchar is_ref__gc; |
9 |
}; |
PHP使用这个结构来存储变量的全部数据。和其余编译性静态语言不一样, PHP在存储变量时将PHP用户空间的变量类型也保存在同一个结构体中。这样咱们就能经过这些信息获取到变量的类型。
zval结构体中有四个字段,其含义分别为:
属性名 | 含义 | 默认值 |
---|---|---|
refcount__gc | 表示引用计数 | 1 |
is_ref__gc | 表示是否为引用 | 0 |
value | 存储变量的值 | |
type | 变量具体的类型 |
在PHP5.3以后,引入了新的垃圾收集机制,引用计数和引用的字段名改成refcount__gc和is_ref__gc。在此以前为refcount和is__ref。
而变量的值则存储在另一个结构体zvalue_value中。值存储见下面的介绍。
PHP用户空间指的在PHP语言这一层面,而本书中大部分地方都在探讨PHP的实现。 这些实现能够理解为内核空间。因为PHP使用C实现,而这个空间的范畴就会限制在C语言。 而PHP用户空间则会受限于PHP语法及功能提供的范畴以内。 例若有些PHP扩展会提供一些PHP函数或者类,这就是向PHP用户空间导出了方法或类。
zval结构体的type字段就是实现弱类型最关键的字段了,type的值能够为: IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一。 从字面上就很好理解,他们只是类型的惟一标示,根据类型的不一样将不一样的值存储到value字段。 除此以外,和他们定义在一块儿的类型还有IS_CONSTANT和IS_CONSTANT_ARRAY。
这和咱们设计数据库时的作法相似,为了不重复设计相似的表,使用一个标示字段来记录不一样类型的数据。
前面提到变量的值存储在zvalue_value联合体中,结构体定义以下:
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; } zvalue_value;
这里使用联合体而不是用结构体是出于空间利用率的考虑,由于一个变量同时只能属于一种类型。 若是使用结构体的话将会没必要要的浪费空间,而PHP中的全部逻辑都围绕变量来进行的,这样的话, 内存浪费将是十分大的。这种作法成本小但收益很是大。
各类类型的数据会使用不一样的方法来进行变量值的存储,其对应赋值方式以下:
1. 通常类型
变量类型 | 宏 | ? |
---|---|---|
boolean | ZVAL_BOOL | 布尔型/整型的变量值存储于(zval).value.lval中,其类型也会以相应的IS_*进行存储。Z_TYPE_P(z)=IS_BOOL/LONG; Z_LVAL_P(z)=((b)!=0); |
integer | ZVAL_LONG | |
float | ZVAL_DOUBLE | |
null | ZVAL_NULL | NULL值的变量值不须要存储,只须要把(zval).type标为IS_NULL。Z_TYPE_P(z)=IS_NULL; |
resource | ZVAL_RESOURCE | 资源类型的存储与其余通常变量无异,但其初始化及存取实现则不一样。Z_TYPE_P(z) = IS_RESOURCE; Z_LVAL_P(z) = l; |
2. 字符串Sting
字符串的类型标示和其余数据类型同样,不过在存储字符串时多了一个字符串长度的字段。
struct { char *val; int len; } str;
C中字符串是以\0结尾的字符数组,这里多存储了字符串的长度,这和咱们在设计数据库时增长的冗余字段殊途同归。 由于要实时获取到字符串的长度的时间复杂度是O(n),而字符串的操做在PHP中是很是频繁的,这样能避免重复计算字符串的长度, 这能节省大量的时间,是空间换时间的作法。 这么看在PHP中strlen()函数能够在常数时间内获取到字符串的长度。 计算机语言中字符串的操做都很是之多,因此大部分高级语言中都会存储字符串的长度。
3. 数组Array
数组是PHP中最经常使用,也是最强大变量类型,它能够存储其余类型的数据,并且提供各类内置操做函数。数组的存储相对于其余变量要复杂一些, 数组的值存储在zvalue_value.ht字段中,它是一个HashTable类型的数据。 PHP的数组使用哈希表来存储关联数据。哈希表是一种高效的键值对存储结构。PHP的哈希表实现中使用了两个数据结构HashTable和Bucket。 PHP全部的工做都由哈希表实现,在下节HashTable中将进行哈希表基本概念的介绍以及PHP的哈希表实现。
4. 对象Object
在面向对象语言中,咱们能本身定义本身须要的数据类型,包括类的属性,方法等数据。而对象则是类的一个具体实现。 对象有自身的状态和所能完成的操做。
PHP的对象是一种复合型的数据,使用一种zend_object_value的结构体来存放。其定义以下:
typedef struct _zend_object_value { zend_object_handle handle; // unsigned int类型,EG(objects_store).object_buckets的索引 zend_object_handlers *handlers; } zend_object_value;
PHP的对象只有在运行时才会被建立,前面的章节介绍了EG宏,这是一个全局结构体用于保存在运行时的数据。 其中就包括了用来保存全部被建立的对象的对象池,EG(objects_store),而object对象值内容的zend_object_handle域就是当前 对象在对象池中所在的索引,handlers字段则是将对象进行操做时的处理函数保存起来。 这个结构体及对象相关的类的结构_zend_class_entry,后面会介绍到。
PHP的弱变量容器的实现方式是兼容并包的形式体现,针对每种类型的变量都有其对应的标记和存储空间。 使用强类型的语言在效率上一般会比弱类型高,由于不少信息能在运行以前就能肯定,这也能帮助排除程序错误。 而这带来的问题是编写代码相对会受制约。
PHP主要的用途是做为Web开发语言,在普通的Web应用中瓶颈一般在业务和数据访问这一层。不过在大型应用下语言也会是一个关键因素。 facebook所以就使用了本身的php实现。将PHP编译为C++代码来提升性能。不过facebook的hiphop并非完整的php实现, 因为它是直接将php编译为C++,有一些PHP的动态特性好比eval结构就没法实现。固然非要实现也是有方法的, hiphop不实现应该也是作了一个权衡。