原生类型

PHP 是动态类型语言,没有编译期的类型检查机制,因此无须为变量、函数和方法指定类型。同时也是弱类型语言,在各种情况下(包括使用运算符、使用函数、使用各种语言结构)总是尝试进行隐式类型转换而不是抛出运行时的类型异常。

原始类型

PHP 支持 9 种原始数据类型。

  • 标量类型:

    • boolean

    • integer

    • float(或称double)

    • string

  • 复合类型:

    • array

    • object

    • callable

  • 特殊类型:

    • resource

    • NULL

boolean

该类型只有两个值,字面量分别用TRUEFALSE表示(大小写不敏感)。其它类型的值可能会被显式或隐式地转化为boolean值,此时以下值会被转化为FALSE:

  • 整型值 0
  • 浮点型值 0.0
  • 空字符串,以及字符串 "0"
  • 不包括任何元素的数组
  • 特殊类型 NULL
  • 从空标记生成的 SimpleXML 对象

其余任何情况均被转化为TRUE。

integer

PHP中integer的实现与平台有关。字长可以用常量PHP_INT_SIZE来表示,自 PHP 4.4.0 和 PHP 5.0.5后,最大值可以用常量PHP_INT_MAX来表示,最小值可以在 PHP 7.0.0 及以后的版本中用常量PHP_INT_MIN表示。任何溢出的integer值自动转化成一个float值。

integer字面量形式包括:

  • 二进制:以0b开头
  • 八进制:以0开头

  • 十进 制

  • 十六进制:以0x开头

以上形式均支持在开头使用正负号。

其它类型的值可能会被显式或隐式地转化为integer值:

  • boolean -> integer:FALSE为0, TRUE为1
  • float -> integer:向下取整。对于相对于integer类型来说溢出了的浮点数,其转换时的行为时未定义的
  • string -> integer:如果该字符串以合法的数值开始,则使用该数值。否则其值为 0(零)。合法数值由可选的正负号,后面跟着一个或多个数字
  • resource -> integer:PHP 运行时为 resource 分配的唯一资源号
  • 其它情况:行为未定义

float

PHP中的float使用的是IEEE 754 双精度格式,存在着一般编程语言中常见的计算与比较时的精度问题(见JS中的相关讨论,PHP中有相应扩展BCMathGMP可用)。字面量可以形如1.2341.2e37E-10

有一特殊的浮点数值保存在常量(而不是字面量)NAN中,此结果代表着一个在浮点数运算中未定义或不可表述的值。任何拿此值与其它任何值( 包括它自身、 除了 TRUE)进行的任何比较的结果都是 FALSE。 应该用 is_nan() 来检查。

其它类型的值可能会被显式或隐式地转化为float值:

  • string -> float:见string一节
  • object -> float:触发一 E_NOTICE 级别的错误
  • int -> float:??
  • string -> float:如果该字符串以合法的数值开始,则使用该数值。否则其值为 0(零)。合法数值由可选的正负号,后面跟着一个或多个数字(可能有小数点),再跟着可选的指数部分。指数部分由 'e' 或 'E' 后面跟着一个或多个数字构成
  • 其它情况:类似于先将值转换成整型,然后再转换成浮点

string

PHP 中的 string 的实现方式是一个由字节组成的数组再加上一个整数指明缓冲区长度,总长度最大2GB。string值中并不包括如何将字节转换成字符的信息,也就是说,string类型实际上是类似Node.js中的buffer、python2中的byte的二进制数据类型。对于程序员在代码中使用的string字面量,解释器按照与该PHP文件相同的编码方式(应当是某个 ASCII 的兼容超集。在 php.ini 中的default_charset选项中指定)编码为二进制数据。

尽管string类型的值是二进制数据,没有明确的编码方案,但许多字符串函数在处理string类型的值时对二进制数据实际采用的编码方案做了假定,例如 intl 扩展中的大部分函数都假定它们所操作的string值是使用utf-8方案编码得到的二进制数据。

字面量

  • 使用单引号:该方式书写的字面量 除了\'\\外 不转义任何字符
  • 使用双引号:会转义一些字符,包括\n\r\t\v\e\f\\\$\"\八进制串\x十六进制串以及PHP7开始支持的\u{codepoint}(类似JS);具有变量解析特性
  • Heredoc形式:使用符号<<<,在该符号之后要提供一个标识符(PHP5.3.0后可以使用双引号来包裹),然后换行。接下来是字符串 string 本身,最后要用前面定义的标识符作为结束标志
    • 代码文件中结束标识符这行除了可能有一个分号外,绝对不能包含其它字符。这意味着标识符不能缩进
    • Heredocs 结构不能用来初始化类的属性。自 PHP 5.3 起,此限制仅对 heredoc 包含变量时有效
    • 存在转义和变量解析特性
  • Nowdoc形式:Nowdoc 结构和 heredocs 结构一样的标记 <<<, 但是跟在后面的标识符要用单引号括起 + 不进行转义和变量解析操作,很适合用于嵌入代码文本或其它大段文本
    • 可以充当编译期使用的字面量,例如用来初始化类的属性

在字面量之前可以加前缀b,例如b'hello world',这是一种兼容不曾存在过的PHP6的语法。详见What does the b in front of string literals do?

变量解析

当字符串用双引号或 heredoc 结构定义时,其中的变量将会被解析。 这类似JS 中的“模板字符串”功能(但只支持特定的表达式形式,而不像前者一样能支持任意的表达式)。

存在两种语法:

  • 简单语法:当 PHP 解析器遇到一个美元符号($)时,会去组合尽量多的字符以形成一个合法(但不一定存在)的变量名/array索引形式(不带引号)/对象的属性名,并试图使用它
  • 复杂语法:通过大括号,可以告诉PHP解析器字符串中我们欲解析的符号名(包括任何具有 string 表达的标量变量,数组单元或对象属性)的边界。只需简单地像在 string 以外的地方那样写出表达式,然后用花括号 { 和 } 把它括起来即可;此语法还允许将通过调用函数、方法或通过静态类变量和类常量得来字符串当作变量名来解析:"hello {${getName()}}",假设getName()将得到字符串"cyl",则相当于"hello {$cyl}",最终导致PHP解析器去寻找$cyl变量。

读取与改写一个字符串

PHP中的string是可变数据,毕竟其本质是一个二进制数据数组。通过方括号或大括号访问法,可以读写string中的数据。

  • 使用方括号或大括号作为下标时,若括号中的值不是整数,解析器会进行隐式类型转换得到一个整数,并触发一个E_NOTICE 级别错误
  • 用负数下标读取字符串时返回空字符串
  • 写入时只用到了赋值字符串的第一个字符
  • 用超出字符串长度的下标写入将会拉长该字符串并以空格填充
  • 用空字符串赋值则赋给的值是 '\0' 字符

用 [] 或 {} 访问任何其它类型(不包括数组或具有相应接口的对象实现)的变量只会无声地返回 NULL。

类型转换

其它类型的值可能会被显式或隐式地转换到string类型:

  • boolean -> string:TRUE成为"1",FALSE成为空字符串
  • integer -> string、float -> string:数字的字符串表示
  • array -> string:转换成字符串"Array"
  • object -> string:转换成字符串"Object"
  • resource -> string:转换成形如 "Resource id #1" 这种结构的字符串
  • NULL -> string:空字符串

当string值需要被当作一个数值使用时,如果该字符串没有包含 '.','e' 或 'E' 并且其数字值在整型的范围之内(由 PHPINTMAX 所定义),该字符串将被当成 integer 来取值。其它所有情况下都被作为 float 来取值。

相关函数与最佳实践

array

PHP中的数组实质上是有序字典,可以把该类型当作列表、字典、队列、栈、集合等抽象数据结构来使用。如果想使用更特定的数据结构,可以使用Data Structures模块中的类。

字面量

使用语法array()[]来使用字面量创建一个PHP数组:

<?php
$array1 = array(
    "a" => 1,
 
    "b" => 2,
 
    3 => 3,
 
    TRUE => 4,
 
);
 
 
// 省略键名
$array2 = array(1,2,3,4,);
 
 
// 部分省略键名
$array3 = array(
    "a",
     "b",
     "key1" => "c",
     "d",
     100 => "e",
     "f", // 其key自动被置为int(101)
 );
  • 以上代码中,array()均可使用[]替代( PHP5.4起)
  • 键名和键值可使用运行时求值的表达式
  • 由于数组是有序字典,字面量中各个键值对的出现顺序是有意义的
  • 最后一项的逗号是可以省略的
  • 在字面量中重复定义一个键,以最后一次为准
  • 如果省略了键名,则键名是在此前的最大整数键名上累加得到的整数。若是首个被省略键名的值,则键名是0。该规则不仅适用于通过字面量创建的键值,也适用于通过其它方式添加到数组中的键值

键名与键值

在一个数组中,键值可以是任何类型的值,而键名只能是integer或string,且不存在重复。无论以何种方式(包括字面量创建法)向一个数组添加键名时,会发生如下类型转换:

  • 包含有合法整型值的字符串会被转换为整型。例如键名"8" 实际会被储存为 8。但是"08"则不会强制转换,因为其不是一个合法的十进制数值。
  • 浮点数也会被转换为整型,意味着其小数部分会被舍去。例如键名8.7 实际会被储存为 8。
  • 布尔值也会被转换成整型。即键名true 实际会被储存为 1 而键名 false 会被储存为 0。
  • Null 会被转换为空字符串,即键名 null 实际会被储存为 ""。
  • 数组和对象无法发生转换,这么做会触发警告:Illegal offset type

数组访问

使用方括号或花括号可以访问数组各单元。方括号或花括号中的值会经过上节所述的类型转换过程。

自 PHP 5.4 起可以用直接对函数或方法调用的结果进行访问,在此之前只能通过一个临时变量;自PHP 5.5起可以直接对一个数组字面量进行访问。

试图访问一个未定义的数组键名会得到一个NULL,并触发一个 E_NOTICE 级别错误。

数组修改

在方括号内指定键名来给数组赋值,也可以省略键名,即只用一个空的方括号,此时取当前最大整数索引值,新的键名将是该值加上 1。如果当前还没有整数索引,则键名将为 0 。“最大整数索引值”当前不一定就在数组中,只要曾经存在过就行 (即使后来被删掉了) 。

<?php
$arr['test'] = 42;

注意,上述代码中$arr作为一个不曾初始化过的变量,强行以该法为其添加元素会导致一个数组被创建。

要删除一个键值对,使用函数 unset():

<?php
$arr = [5 => 1, 12 => 2];
unset($arr[5]); // 删除键值对 5 => 1

删除一个键后,该键的位置空缺出来,不会被自动填补。

类型转换

其他类型的值可被显式地转换为数组类型,规则如下:

  • integer,float,string,boolean 和 resource -> array:一个仅有一个元素的数组,其下标为 0,该元素即为此标量的值
  • object -> array:结果为一个数组,其单元为该对象的属性。键名将为成员变量名,不过有几点例外:整数属性不可访问;私有变量前会加上类名作前缀;保护变量前会加上一个 '*' 做前缀。这些前缀的前后都各有一个 '\0' 字符
  • NULL -> array:一个空数组

相关函数

这份列表给出了与数组相关的大量函数。

object

一个object值是某个类的一个实例。详见《PHP中的OOP系统》。

其它类型的值可能被类型转换到object,规则是:

  • NULL -> object:一个空的stdClass实例
  • array -> object:一个stdClass实例,键名成为属性名并具有相对应的值,除了数字键,不迭代就无法被访问
  • 其它 -> object:一个stdClass实例,具有一个scalar属性,其值为转换前的值

使用object类型的值时,实际上总是在通过引用来使用。这点和JS是一样的。

resource

resource 是一种特殊类型,表示到外部资源的一个引用。资源是通过专门的函数来建立和使用的。使用函数getresourcetype可以得到一个描述资源的字符串。

类型转换方面:将其它类型的值转换为resource类型没有意义。

NULL

NULL类型只有一个值,该值保存在常量NULL中。任何未被赋值过的变量、被unset过的变量的值均为NULL。

使用函数 is_null 检测NULL值。

callable

函数,对象的方法,包括静态类方法都是callable类型,但PHP中callable类型的值并非第一类公民,要传递callable类型的值时,通常使用其符号(如函数名)的字符串形式来代替。

类型转换的触发

使用操作符以及某些语言结构时会发生隐式类型转换。除此之外,也可以由程序员指定显式的类型转换,其方法和 C 中的非常像:在要转换的值之前加上用括号括起来的目标类型:

  • (int), (integer) - 转换为整形 integer

  • (bool), (boolean) - 转换为布尔类型 boolean

  • (float), (double), (real) - 转换为浮点型 float

  • (string), (binary) - 转换为字符串 string

  • (array) - 转换为数组 array

  • (object) - 转换为对象 object

  • (unset) - 转换为 NULL (PHP 5)

若要改变一个变量的值的类型,使用函数settype

与类型系统有关的函数

PHP中与类型系统有关的函数集中在Variable handling文档中,包括:

  • boolval/ floatval/ intval/ strval
  • isarray/isbool/iscallable(支持检测类的方法)/isdouble/isfloat/isint/isnull/isobject/isresource//isstring
  • is_scalar
  • gettype/settype