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来实现,在src/Zend/zend.h中我们能看到zval的定义:
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
属性名 | 含义 | 默认值 |
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能够实现变量弱类型的核心,定义如下:
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;
zend_ast *ast;
} zvalue_value;
- C中字符串是以 结尾的字符数组,这里多存储了字符串的长度,这和我们在设计数据库时增加的冗余字段异曲同工。 因为要实时获取到字符串的长度的时间复杂度是O(n),而字符串的操作在PHP中是非常频繁的,这样能避免重复计算字符串的长度, 这能节省大量的时间,是空间换时间的做法。
- 这么看在PHP中strlen()函数可以在常数时间内获取到字符串的长度。 计算机语言中字符串的操作都非常之多,所以大部分高级语言中都会存储字符串的长度。
布尔型,zval.type=IS_BOOL,会读取zval.value.lval字段,值为1/0。
字符串,zval.type=IS_STRING,会读取zval.value.str,这是一个结构体,存储了字符串指针和长度。
如果是NULL,只需要zval.type=IS_NULL,不需要读取值。
C 语言中,用"