PHP是一门简单而强大的语言,提供了很多Web适用的语言特性,其中就包括了变量弱类型,在弱类型机制下,你能够给一个变量赋任意类型的值。
PHP的执行是通过Zend Engine(下面简称ZE),ZE是使用C编写,在底层实现了一套弱类型机制。ZE的内存管理使用写时拷贝、引用计数等优化策略,减少再变量赋值时候的内存拷贝。
下面不光带你探索PHP弱类型的原理,也会在写PHP扩展角度,介绍如何操作PHP的变量。
1. PHP的变量类型
PHP的变量类型有8种:
- 标准类型:布尔boolen,整型integer,浮点float,字符string
- 复杂类型:数组array,对象object
- 特殊类型:资源resource
PHP不会严格检验变量类型,变量可以不显示的声明其类型,而在运行期间直接赋值。也可以将变量自由的转换类型。如下例,没有实现声明的情况下,$i可以赋任意类型的值。
<? php
$i = 1; //int
$i = 'show me the money'; //string
$i = 0.02; // float
$i = array(1, 2, 3); // array
$i = new Exception('test', 123); // object
$i = fopen('/tmp/aaa.txt', 'a') // resource
?>
如果你对弱类型原理理解不深刻,在变量比较时候,会出现“超出预期”的惊喜。
<? PHP
$str1 = null;
$str2 = false;
echo $str1==$str2 ? '相等' : '不相等';
$str3 = '';
$str4 = 0;
echo $str3==$str4 ? '相等' : '不相等';
$str5 = 0;
$str6 = '0';
echo $str5==$str6 ? '相等' : '不相等';
?>
以上三个结果全部是相等,因为在变量比较的时候,PHP内部做了变量转换。如果希望值和类型同时判断,请使用三个=(如,$a===0)来判断。也许你会觉得司空见惯,也许你会觉得很神奇,那么请跟我一起深入PHP内核,探索PHP变量原理。
2. 变量的存储及标准类型介绍
PHP的所有变量,都是以结构体zval来实现,在Zend/zend.h中我们能看到zval的定义:
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len; /* this will always be set for strings */
} str; /* string (always has length) */
HashTable *ht; /* an array */
zend_object_value obj; /* stores an object store handle, and handlers */
} zvalue_value;
属性名 | 含义 | 默认值 |
refcount__gc | 表示引用计数 | 1 |
is_ref__gc | 表示是否为引用 | 0 |
value | 存储变量的值 | |
type | 变量具体的类型 |
其中refcount__gc和is_ref__gc表示变量是否是一个引用。type字段标识变量的类型,type的值可以是:IS_NULL,IS_BOOL,IS_LONG,IS_FLOAT,IS_STRING,IS_ARRAY,IS_OBJECT,IS_RESOURCE。PHP根据type的类型,来选择如何存储到zvalue_value。
zvalue_value能够实现变量弱类型的核心,定义如下:
布尔型,zval.type=IS_BOOL,会读取zval.value.lval字段,值为1/0。如果是字符串,zval.type=IS_STRING,会读取zval.value.str,这是一个结构体,存储了字符串指针和长度。
C语言中,用"\0"作为字符串结束符。也就是说一个字符串"Hello\0World"在C语言中,用printf来输出的话,只能输出hello,因为"\0"会认为字符已经结束。PHP中是通过结构体的_zval_value.str.len来控制字符串长度,相关函数不会遇到"\0"结束。所以PHP的字符串是二进制安全的。
如果是NULL,只需要zval.type=IS_NULL,不需要读取值。
通过对zval的封装,PHP实现了弱类型,对于ZE来说,通过zval可以存取任何类型。
3. 高级类型Array和Object数组Array
数组是PHP语言中非常强大的一个数据结构,分为索引数组和关联数组,zval.type=IS_ARRAY。在关联数组中每个key可以存储任意类型的数据。PHP的数组是用Hash Table实现的,数组的值存在zval.value.ht中。
后面会专门讲到PHP哈希表的实现。
对象类型的zval.type=IS_OBJECT,值存在zval.value.obj中。
4. 特殊类型——资源类型(Resource)介绍
资源类型是个很特殊的类型,zval.type=IS_RESOURCE,在PHP中有一些很难用常规类型描述的数据结构,比如文件句柄,对于C语言来说是一个指针,不过PHP中没有指针的概念,也不能用常规类型来约束,因此PHP通过资源类型概念,把C语言中类似文件指针的变量,用zval结构来封装。资源类型值是一个整数,ZE会根据这个值去资源的哈希表中获取。
资源类型的定义: