Skip to main content

类型

简介

PHP 支持 10种 原始数据类型

四种标量类型:

  • bool(布尔型)
  • int(整型)
  • float(浮点型,也称为 double)
  • string(字符串)

四种符合类型:

  • array(数组)
  • object(对象)
  • callable(可调用)
  • iterable(可迭代)

两种特殊类型:

  • resource(资源)
  • NULL(无类型)

可能还会读到一些关于“双精度(double)”类型的参考。实际上 double 和 float 是相同的,由于一些历史的原因,这两个名称同时存在。

变量的类型通常不是由程序员设定的,确切地说,是由 PHP 根据该变量使用的上下文在运行时决定的。

note

如果想查看某个表达式的值和类型,用 var_dump() 函数。

如果只是想得到一个易读懂的类型的表达方式用于调试,用 gettype() 函数。要检验某个类型,不要用 gettype(),而用 is_type 函数。以下是一些范例:

<?php
$a_bool = TRUE; // 布尔值 boolean
$a_str = "foo"; // 字符串 string
$a_str2 = 'foo'; // 字符串 string
$an_int = 12; // 整型 integer

echo gettype($a_bool); // 输出: boolean
echo gettype($a_str); // 输出: string

// 如果是整型,就加上 4
if (is_int($an_int)) {
$an_int += 4;
}

// 如果 $bool 是字符串,就打印出来
// (啥也没打印出来)
if (is_string($a_bool)) {
echo "String: $a_bool";
}
?>

如果要将一个变量强制转换为某类型,可以对其使用强制转换或者 settype() 函数。

注意变量根据其当时的类型在特定场合下会表现出不同的值。更多信息见类型转换的判别。此外,还可以参考 PHP 类型比较表看不同类型相互比较的例子

Boolean 布尔类型

这是最简单的类型。boolean 表达了真值,可以为 true false

语法

要指定一个布尔值,使用常量 true false 。两个都不区分大小写。

<?php
$foo = True; // 设置 $foo 为 TRUE
?>

通常运算符所返回的 boolean 值结果会被传递给控制流程。

<?php
// == 是一个操作符,它检测两个变量是否相等,并返回一个布尔值
if ($action == "show_version") {
echo "The version is 1.23";
}

// 这样做是不必要的...
if ($show_separators == TRUE) {
echo "<hr>\n";
}

// ...因为可以使用下面这种简单的方式:
if ($show_separators) {
echo "<hr>\n";
}
?>

转换为布尔值

要明确地将一个值转换成 boolean,用 (bool) 或者 (boolean) 来强制转换。但是很多情况下不需要用强制转换,因为当运算符,函数或者流程控制结构需要一个 boolean 参数时,该值会被自动转换。

参见类型转换的判别

当转换为 boolean 时,以下值被认为是 false:

  • 布尔值 false 本身
  • 整型值 0(零)及 -0 (零)
  • 浮点型值 0.0(零)-0.0(零)
  • 空字符串,以及字符串 "0"
  • 不包括任何元素的数组
  • 特殊类型 NULL(包括尚未赋值的变量)
  • 从空标记生成的 SimpleXML 对象

所有其它值都被认为是 true (包括任何资源 和 NAN)。

tip

-1 和其它非零值(不论正负)一样,被认为是 true!

<?php
var_dump((bool) ""); // bool(false)
var_dump((bool) 1); // bool(true)
var_dump((bool) -2); // bool(true)
var_dump((bool) "foo"); // bool(true)
var_dump((bool) 2.3e5); // bool(true)
var_dump((bool) array(12)); // bool(true)
var_dump((bool) array()); // bool(false)
var_dump((bool) "false"); // bool(true)·
?>

Integer 整型

int 是集合 ℤ = {..., -2, -1, 0, 1, 2, ...} 中的某个数。

参见:

语法

整型值 int 可以使用十进制,十六进制,八进制或二进制表示,前面可以加上可选的符号(- 或者 +)。 可以用 负运算符 来表示一个负的int。

要使用八进制表达,数字前必须加上 0(零)。要使用十六进制表达,数字前必须加上 0x。要使用二进制表达,数字前必须加上 0b。

从 PHP 7.4.0 开始,整型数值可能会包含下划线 (_),为了更好的阅读体验,这些下划线在展示的时候,会被 PHP 过滤掉。

示例1 整数文字表达

<?php
$a = 1234; // 十进制数
$a = 0123; // 八进制数 (等于十进制 83)
$a = 0x1A; // 十六进制数 (等于十进制 26)
$a = 0b11111111; // 二进制数字 (等于十进制 255)
$a = 1_234_567; // 整型数值 (PHP 7.4.0 以后)
?>

int 语法的结构形式是(PHP 7.4.0 之前不支持下划线):

decimal     : [1-9][0-9]*(_[0-9]+)*
| 0

hexadecimal : 0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)*

octal : 0[0-7]+(_[0-7]+)*

binary : 0[bB][01]+(_[01]+)*

integer : decimal
| hexadecimal
| octal
| binary

整型数 int 的字长和平台有关,尽管通常最大值是大约二十亿(32 位有符号)。64 位平台下的最大值通常是大约 9E18。 PHP 不支持无符号的 int。int 值的字长可以用常量 PHP_INT_SIZE来表示, 最大值可以用常量 PHP_INT_MAX 来表示, 最小值可以用常量 PHP_INT_MIN 表示。

整数溢出

如果给定的一个数超出了 int 的范围,将会被解释为 float。同样如果执行的运算结果超出了 int 范围,也会返回 float。

示例2 32位系统下的整数溢出

<?php
$large_number = 2147483647;
var_dump($large_number); // int(2147483647)

$large_number = 2147483648;
var_dump($large_number); // float(2147483648)

$million = 1000000;
$large_number = 50000 * $million;
var_dump($large_number); // float(50000000000)
?>

示例3 64位系统下的整数溢出

<?php
$large_number = 9223372036854775807;
var_dump($large_number); // int(9223372036854775807)

$large_number = 9223372036854775808;
var_dump($large_number); // float(9.2233720368548E+18)

$million = 1000000;
$large_number = 50000000000000 * $million;
var_dump($large_number); // float(5.0E+19)
?>

PHP中没有 int 除法运算符,要实现这一点,请使用 intdiv() 函数。1/2 产生出 float 0.5。 值可以舍弃小数部分,强制转换为 int,或者使用 round() 函数可以更好地进行四舍五入。

<?php
var_dump(25/7); // float(3.5714285714286)
var_dump((int) (25/7)); // int(3)
var_dump(round(25/7)); // float(4)
?>

转换为整型

要明确地将一个值转换为 int,用 (int) 或 (integer) 强制转换。不过大多数情况下都不需要强制转换,因为当运算符,函数或流程控制需要一个 int 参数时,值会自动转换。还可以通过函数 intval() 来将一个值转换成 int 整型。

将 resource 转换成 int 时, 结果会是 PHP 运行时为 resource 分配的唯一资源号。

参见:类型转换的判别

从布尔值转换

false 将产生出 0(零),true 将产生出 1(壹)。

从浮点型转换

当从浮点数 float 转换成整数 int 时,将向下取整。

如果浮点数超出了 int 范围(32 位平台下通常为 +/- 2.15e+9 = 2^31,64 位平台下,通常为 +/- 9.22e+18 = 2^63),则结果为未定义, 因为没有足够的精度给出一个确切的 int 结果。 在此情况下没有警告,甚至没有任何通知!

info

NaN 和 Infinity 在转换成 int 时是零。

caution

绝不要将未知的分数强制转换为 int,这样有时会导致不可预料的结果。

<?php
echo (int) ( (0.1+0.7) * 10 ); // 显示 7!
?>

参数关于浮点数精度的警告

从字符串转换

如果字符串是数字或前导数字,则它将解析为相应的整数值,否则将转换为零(0)。

从 NULL 转换

null 会转换为零 (0)。

从其他类型转换

caution

没有定义从其它类型转换为 int 的行为。 不要依赖任何现有的行为,因为它会未加通知地改变。

Float 浮点型

浮点型(也叫浮点数 float,双精度数 double 或实数 real)可以用以下任一语法定义:

<?php
$a = 1.234;
$b = 1.2e3;
$c = 7E-10;
$d = 1_234.567; // 从 PHP 7.4.0 开始支持
?>

浮点数的形式表示(PHP 7.4.0 之前不支持下划线):

LNUM          [0-9]+(_[0-9]+)*
DNUM ([0-9]*(_[0-9]+)*[\.]{LNUM}) | ({LNUM}[\.][0-9]*(_[0-9]+)*)
EXPONENT_DNUM (({LNUM} | {DNUM}) [eE][+-]? {LNUM})

浮点数的字长和平台相关,尽管通常最大值是 1.8e308 并具有 14 位十进制数字的精度(64 位 IEEE 格式)。

caution

浮点数的精度

浮点数的精度有限。尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16。非基本数学运算可能会给出更大误差,并且要考虑到进行复合运算时的误差传递。

此外,以十进制能够精确表示的有理数如 0.1 或 0.7,无论有多少尾数都不能被内部所使用的二进制精确表示,因此不能在不丢失一点点精度的情况下转换为二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999999991118...。

所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数

参见» 浮点数指南网页的简单解释。

转为浮点数

从字符串转换

If the string is numeric or leading numeric then it will resolve to the corresponding float value, otherwise it is converted to zero (0).

从其他类型转换

对于其它类型的值,其情况类似于先将值转换成 int,然后再转换成 float。 请参阅“转换为整型”一节以获取更多信息。

note

由于某些类型在转换为 int 时具有未定义的行为,因此在转换为 float 时也是如此。

比较浮点数

如上述警告信息所言,由于内部表达方式的原因,比较两个浮点数是否相等是有问题的。不过还是有迂回的方法来比较浮点数值的。

要测试浮点数是否相等,要使用一个仅比该数值大一丁点的最小误差值。该值也被称为机器极小值(epsilon)或最小单元取整数,是计算中所能接受的最小的差别值。

<?php
// $a 和 $b 在小数点后五位精度内都是相等的。
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;

if(abs($a-$b) < $epsilon) {
echo "true";
}
?>

NAN

某些数学运算会产生一个由常量 NAN 所代表的结果。此结果代表着一个在浮点数运算中未定义或不可表述的值。任何拿此值与其它任何值(除了 true )进行的松散或严格比较的结果都是 false

由于 NAN 代表着任何不同值,不应拿 NAN 去和其它值进行比较,包括其自身,应该用 is_nan() 来检查。

浮点数计算心得

caution

切记 为确保计算的准确,不能直接将数字进行数学运算,应当使用对应的数学函数进行计算

举例

<?php
echo 10-9.99; // 0.0099999999999998
echo bcsub(10, 9.99, 2); // 0.01

String 字符串

一个字符串 string 就是由一系列的字符组成,其中每个字符等同于一个字节。这意味着 PHP 只能支持 256 的字符集,因此不支持 Unicode 。详见字符串类型详解

note

在 32 位版本中,string 最大可以达到 2GB(最多 2147483647 字节)。

语法

一个字符串可以用 4 种方式表达:

单引号

定义一个字符串的最简单的方法是用单引号把它包围起来(字符 ' )。

要表达一个单引号自身,需在它的前面加个反斜线(\)来转义。要表达一个反斜线自身,则用两个反斜线(\)。其它任何方式的反斜线都会被当成反斜线本身:也就是说如果想使用其它转义序列例如 \r 或者 \n,并不代表任何特殊含义,就单纯是这两个字符本身。

note

注意: 不像双引号和 heredoc 语法结构,在单引号字符串中的变量和特殊字符的转义序列将不会被替换。

<?php
echo 'this is a simple string';

// 可以录入多行
echo 'You can also have embedded newlines in
strings this way as it is
okay to do';

// 输出: Arnold once said: "I'll be back"
echo 'Arnold once said: "I\'ll be back"';

// 输出: You deleted C:\*.*?
echo 'You deleted C:\\*.*?';

// 输出: You deleted C:\*.*?
echo 'You deleted C:\*.*?';

// 输出: This will not expand: \n a newline
echo 'This will not expand: \n a newline';

// 输出: Variables do not $expand $either
echo 'Variables do not $expand $either';
?>

双引号

如果字符串是包围在双引号( " )中, PHP 将对以下特殊的字符进行解析:

序列含义
\n换行(ASCII 字符集中的 LF 或 0x0A (10))
\r回车(ASCII 字符集中的 CR 或 0x0D (13))
\t水平制表符(ASCII 字符集中的 HT 或 0x09 (9))
\v垂直制表符(ASCII 字符集中的 VT 或 0x0B (11))
\eEscape(ASCII 字符集中的 ESC 或 0x1B (27))
\f换页(ASCII 字符集中的 FF 或 0x0C (12))
\反斜线
\$美元标记
\"双引号
[0-7]{1,3}符合该正则表达式序列的是一个以八进制方式来表达的字符,which silently overflows to fit in a byte (e.g. "\400" === "\000")
\x[0-9A-Fa-f]{1,2}符合该正则表达式序列的是一个以十六进制方式来表达的字符
\u{[0-9A-Fa-f]+}匹配正则表达式的字符序列是 unicode 码位, 该码位能作为 UTF-8 的表达方式输出字符串

和单引号字符串一样,转义任何其它字符都会导致反斜线被显示出来。

用双引号定义的字符串最重要的特征是变量会被解析,详见变量解析

heredoc 语法结构

第三种表达字符串的方法是用 heredoc 句法结构:<<<。在该运算符之后要提供一个标识符,然后换行。接下来是字符串 string 本身,最后要用前面定义的标识符作为结束标志。

结束时所引用的标识符必须在该行的第一列,而且,标识符的命名也要像其它标签一样遵守 PHP 的规则:只能包含字母、数字和下划线,并且必须以字母和下划线作为开头。

caution

要注意的是结束标识符这行除了有一个分号(;)外,绝对不能包含其它字符。这意味着标识符不能缩进,分号的前后也不能有任何空白或制表符。更重要的是结束标识符的前面必须是个被本地操作系统认可的换行,比如在 UNIX 和 macOS 系统中是 \n,而结束定界符之后也必须紧跟一个换行。

如果不遵守该规则导致结束标识不“干净”,PHP 将认为它不是结束标识符而继续寻找。如果在文件结束前也没有找到一个正确的结束标识符,PHP 将会在最后一行产生一个解析错误。

示例1 非法的示例

<?php
class foo {
public $bar = <<<EOT
bar
EOT;
}
// Identifier must not be indented
?>

示例2 合法的示例

<?php
class foo {
public $bar = <<<EOT
bar
EOT;
}
?>

Heredocs 结构不能用来初始化类的属性。自 PHP 5.3 起,此限制仅对 heredoc 包含变量时有效。

Heredoc 结构就象是没有使用双引号的双引号字符串,这就是说在 heredoc 结构中单引号不用被转义,但是上文中列出的转义序列还可以使用。变量将被替换,但在 heredoc 结构中含有复杂的变量时要格外小心。

示例3 Heredoc 结构的字符串示例

<?php
$str = <<<EOD
Example of string
spanning multiple lines
using heredoc syntax.
EOD;

/* 含有变量的更复杂示例 */
class foo
{
var $foo;
var $bar;

function __construct()
{
$this->foo = 'Foo';
$this->bar = array('Bar1', 'Bar2', 'Bar3');
}
}

$foo = new foo();
$name = 'MyName';

echo <<<EOT
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should print a capital 'A': \x41
EOT;
?>

以上例程会输出:

My name is "MyName". I am printing some Foo.
Now, I am printing some Bar2.
This should print a capital 'A': A

也可以把 Heredoc 结构用在函数参数中来传递数据:

示例4 Heredoc 结构在参数中的示例

<?php
var_dump(array(<<<EOD
foobar!
EOD
));
?>

可以用 Heredoc 结构来初始化静态变量和类的属性和常量:

示例5 使用 Heredoc 结构来初始化静态值

<?php
// 静态变量
function foo()
{
static $bar = <<<LABEL
Nothing in here...
LABEL;
}

// 类的常量、属性
class foo
{
const BAR = <<<FOOBAR
Constant example
FOOBAR;

public $baz = <<<FOOBAR
Property example
FOOBAR;
}

还可以在 Heredoc 结构中用双引号来声明标识符:

示例6 在 Heredoc 结构中使用双引号

<?php
echo <<<"FOOBAR"
Hello World!
FOOBAR;
?>

nowdoc 语法结构

查看官网解释

就象 heredoc 结构类似于双引号字符串,Nowdoc 结构是类似于单引号字符串的。Nowdoc 结构很象 heredoc 结构,但是 nowdoc 中不进行解析操作。这种结构很适合用于嵌入 PHP 代码或其它大段文本而无需对其中的特殊字符进行转义。与 SGML 的 <![CDATA[ ]]> 结构是用来声明大段的不用解析的文本类似,nowdoc 结构也有相同的特征。

一个 nowdoc 结构也用和 heredocs 结构一样的标记 <<<, 但是跟在后面的标识符要用单引号括起来,即 <<<'EOT'。Heredoc 结构的所有规则也同样适用于 nowdoc 结构,尤其是结束标识符的规则。

个人理解: nowdoc 与 heredoc 基本一致,区别就是 nowdoc 不解析变量

简单举例: 含变量引用的 Nowdoc 字符串示例

<?php

/* 含有变量的更复杂的示例 */
class foo
{
public $foo;
public $bar;

function __construct()
{
$this->foo = 'Foo';
$this->bar = array('Bar1', 'Bar2', 'Bar3');
}
}

$foo = new foo();
$name = 'MyName';

echo <<<'EOT'
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should not print a capital 'A': \x41
EOT;
?>

以上例程会输出:

My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should not print a capital 'A': \x41

字符串类型详解

官网详解

数字字符串

如果一个 PHP String 可以被解释为 int 或 float 类型,则它被视为数字字符串。

PHP 8.0.0 正式可用

WHITESPACES      \s*
LNUM [0-9]+
DNUM ([0-9]*)[\.]{LNUM}) | ({LNUM}[\.][0-9]*)
EXPONENT_DNUM (({LNUM} | {DNUM}) [eE][+-]? {LNUM})
INT_NUM_STRING {WHITESPACES} [+-]? {LNUM} {WHITESPACES}
FLOAT_NUM_STRING {WHITESPACES} [+-]? ({DNUM} | {EXPONENT_DNUM}) {WHITESPACES}
NUM_STRING ({INT_NUM_STRING} | {FLOAT_NUM_STRING})

PHP 也有前导数字字符串的概念。这只是一个字符串,其开头类似于数字字符串,后跟任何字符。

在数字上下文中使用的字符串

当一个 string 需要被当作一个数字计算时,(例如:算术运算, int 类型声明等),则采取以下步骤来确定结果:

1、如果 string 是数字,当 string 是整数字符串并且符合 int 类型的范围限制(即是 PHP_INT_MAX 定义的值),则解析为 int ,否则解析为 float 。

2、如果上下文允许前导数字和一个 string,如果 string 的前导部分是整数数字字符串且符合 int 类型限制(由 PHP_INT_MAX 定义),则解析为 int ,否则解析为 float 。 此外,还会导致 E_WARNING 级别的错误。

3、如果 string 不是数字,则会抛出一个 TypeError 的异常。

PHP 8.0.0 之前的行为

在 PHP 8.0.0 之前, 只有在前导空格的时候,string 才被认为是数字;如果它有尾随空格,则该字符串被视为是前导数字。

在 PHP 8.0.0 之前,当在数字上下文中使用字符串时,它将执行与上述相同的步骤,但有以下区别:

  • 使用前导数字字符串将导致 E_NOTICE 而不是 E_WARNING 错误。
  • 如果字符串不是数字,则会导致 E_WARNING 错误并返回 0 。

在 PHP 7.1.0 之前,则既不会导致 E_NOTICE,也不会导致 E_WARNING。

<?php
$foo = 1 + "10.5"; // $foo 是 float (11.5)
$foo = 1 + "-1.3e3"; // $foo 是 float (-1299)
$foo = 1 + "bob-1.3e3"; // PHP 8.0.0 起产生 TypeError;在此之前 $foo 是 integer (1)
$foo = 1 + "bob3"; // PHP 8.0.0 起产生 TypeError;在此之前 $foo 是 integer (1)
$foo = 1 + "10 Small Pigs"; // PHP 8.0.0 起,$foo 是 integer (11),并且产生 E_WARNING;在此之前产生 E_NOTICE
$foo = 4 + "10.2 Little Piggies"; // PHP 8.0.0 起,$foo 是 float (14.2),并且产生 E_WARNING;在此之前产生 E_NOTICE
$foo = "10.0 pigs " + 1; // PHP 8.0.0 起,$foo 是 float (11),并且产生 E_WARNING;在此之前产生 E_NOTICE
$foo = "10.0 pigs " + 1.0; // PHP 8.0.0 起,$foo 是 float (11),并且产生 E_WARNING;在此之前产生 E_NOTICE
?>

Array 数组

PHP 中的 array 实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型。此类型针对多种不同用途进行了优化; 它可以被视为数组、列表(向量)、哈希表(映射的实现)、字典、集合、堆栈、队列等等。 由于 array 的值可以是其它 array 所以树形结构和多维 array 也是允许的。

解释这些数据结构超出了PHP手册的范围,但对每种结构至少会提供一个例子。 要得到这些数据结构的更多信息,建议参考有关此广阔主题的其它文献。

语法

定义数组 array()

可以用 array() 语言结构来新建一个 array 。它接受任意数量用逗号分隔的 键(key) => 值(value) 对。

array(
key => value,
key2 => value2,
key3 => value3,
...
)

最后一个数组单元之后的逗号可以省略。通常用于单行数组定义中,例如常用 array(1, 2) 而不是 array(1, 2, )。对多行数组定义通常保留最后一个逗号,这样要添加一个新单元时更方便。

note

可以用短数组语法 [] 替代 array() 。

示例1 一个简单数组

<?php
$array = array(
"foo" => "bar",
"bar" => "foo",
);

// 使用短数组语法
$array = [
"foo" => "bar",
"bar" => "foo",
];
?>

key 可以是 integer 或者 string。value 可以是任意类型。

此外 key 会有如下的强制转换:

  • String 中包含有效的十进制 int,除非数字前面有一个 + 号,否则将被转换为 int 类型。例如键名 "8" 实际会被储存为 8。另外, "08" 不会被强制转换,因为它不是一个有效的十进制整数。
  • Float 也会被转换为 int ,意味着其小数部分会被舍去。例如键名 8.7 实际会被储存为 8。
  • Bool 也会被转换成 int。即键名 true 实际会被储存为 1 而键名 false 会被储存为 0。
  • Null 会被转换为空字符串,即键名 null 实际会被储存为 ""。
  • Array 和 object 不能 被用为键名。坚持这么做会导致警告:Illegal offset type。

如果在数组定义时多个元素都使用相同键名,那么只有最后一个会被使用,其它的元素都会被覆盖。

示例2 类型转换与覆盖的示例

<?php
$array = array(
1 => "a",
"1" => "b",
1.5 => "c",
true => "d",
);
var_dump($array);
?>

以上例程会输出:

array(1) {
[1]=>
string(1) "d"
}

上例中所有的键名都被强制转换为 1,则每一个新单元都会覆盖前一个的值,最后剩下的只有一个 "d"。

PHP 数组可以同时含有 int 和 string 类型的键名,因为 PHP 实际并不区分索引数组和关联数组。

混合 int 和 string 键名

<?php
$array = array(
"foo" => "bar",
"bar" => "foo",
100 => -100,
-100 => 100,
);
var_dump($array);
?>

以上例程会输出:

array(4) {
["foo"]=>
string(3) "bar"
["bar"]=>
string(3) "foo"
[100]=>
int(-100)
[-100]=>
int(100)
}

key 为可选项。如果未指定,PHP 将自动使用之前用过的最大 int 键名加上 1 作为新的键名。

示例4 没有键名的索引数组

<?php
$array = array("foo", "bar", "hello", "world");
var_dump($array);
?>

以上例程会输出:

array(4) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
[2]=>
string(5) "hello"
[3]=>
string(5) "world"
}

还可以只对某些单元指定键名而对其它的空置:

示例5 仅对部分单元指定键名

<?php
$array = array(
"a",
"b",
6 => "c",
"d",
);
var_dump($array);
?>

以上例程会输出:

array(4) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[6]=>
string(1) "c"
[7]=>
string(1) "d"
}

可以看到最后一个值 "d" 被自动赋予了键名 7。这是由于之前最大的整数键名是 6。

示例6 复杂类型转换和覆盖的例子

这个例子包括键名类型转换和元素覆盖的所有变化。

<?php
$array = array(
1 => 'a',
'1' => 'b', // 值 "a" 会被 "b" 覆盖
1.5 => 'c', // 值 "b" 会被 "c" 覆盖
-1 => 'd',
'01' => 'e', // 由于这不是整数字符串,因此不会覆盖键名 1
'1.5' => 'f', // 由于这不是整数字符串,因此不会覆盖键名 1
true => 'g', // 值 "c" 会被 "g" 覆盖
false => 'h',
'' => 'i',
null => 'j', // 值 "i" 会被 "j" 覆盖
'k', // 值 “k” 的键名被分配为 2。这是因为之前最大的整数键是 1
2 => 'l', // 值 "k" 会被 "l" 覆盖
);

var_dump($array);
?>

以上例程会输出:

array(7) {
[1]=>
string(1) "g"
[-1]=>
string(1) "d"
["01"]=>
string(1) "e"
["1.5"]=>
string(1) "f"
[0]=>
string(1) "h"
[""]=>
string(1) "j"
[2]=>
string(1) "l"
}

用方括号语法访问数组单元

数组单元可以通过 array[key] 语法来访问。

示例7 访问数组单元

<?php
$array = array(
"foo" => "bar",
42 => 24,
"multi" => array(
"dimensional" => array(
"array" => "foo"
)
)
);

var_dump($array["foo"]);
var_dump($array[42]);
var_dump($array["multi"]["dimensional"]["array"]);
?>

以上例程会输出:

string(3) "bar"
int(24)
string(3) "foo"
caution

在 PHP 8.0.0 之前,方括号和花括号可以互换使用来访问数组单元(例如 $array[42] 和 $array{42} 在上例中效果相同)。 花括号语法在 PHP 7.4.0 中已弃用,在 PHP 8.0.0 中不再支持。

示例8 数组解引用

<?php
function getArray() {
return array(1, 2, 3);
}

$secondElement = getArray()[1];

// 或
list(, $secondElement) = getArray();
?>
caution

试图访问一个未定义的数组键名与访问任何未定义变量一样:会导致 E_NOTICE 级别错误信息,其结果为 null

caution

数组解引用非 string 的标量值会产生 null。 在 PHP 7.4.0 之前,它不会发出错误消息。 从 PHP 7.4.0 开始,这个问题产生 E_NOTICE ; 从 PHP 8.0.0 开始,这个问题产生 E_WARNING 。

用方括号的语法 新建/修改

官网文档

转换为数组

对于任意 int,float, string,bool 和 resource 类型,如果将一个值转换为 array,将得到一个仅有一个元素的数组,其下标为 0,该元素即为此标量的值。换句话说,(array)$scalarValue 与 array($scalarValue) 完全一样。

如果将 object 类型转换为 array,则结果为一个数组,其单元为该对象的属性。键名将为成员变量名,不过有几点例外:整数属性不可访问;私有变量前会加上类名作前缀;保护变量前会加上一个 '*' 做前缀。这些前缀的前后都各有一个 NUL 字节。这会导致一些不可预知的行为:

<?php

class A {
private $B;
protected $C;
public $D;
function __construct()
{
$this->{1} = null;
}
}

var_export((array) new A());
?>

以上例程会输出:

array (
'' . "\0" . 'A' . "\0" . 'B' => NULL,
'' . "\0" . '*' . "\0" . 'C' => NULL,
'D' => NULL,
1 => NULL,
)

这些 NUL 会导致一些意想不到的行为:

<?php

class A {
private $A; // 将变为 '\0A\0A'
}

class B extends A {
private $A; // 将变为 '\0B\0A'
public $AA; // 将变为 'AA'
}

var_dump((array) new B());
?>

以上例程会输出:

array(3) {
["BA"]=>
NULL
["AA"]=>
NULL
["AA"]=>
NULL
}

上例会有两个键名为 'AA',不过其中一个实际上是 '\0A\0A'。

将 null 转换为 array 会得到一个空的数组。

比较

可以用 array_diff() 函数和 数组运算符 来比较数组

示例:PHP数组类型用途 示例

<?php
// This:
$a = array( 'color' => 'red',
'taste' => 'sweet',
'shape' => 'round',
'name' => 'apple',
4 // 键名为 0
);

$b = array('a', 'b', 'c');

// . . .完全等同于:
$a = array();
$a['color'] = 'red';
$a['taste'] = 'sweet';
$a['shape'] = 'round';
$a['name'] = 'apple';
$a[] = 4; // 键名为 0

$b = array();
$b[] = 'a';
$b[] = 'b';
$b[] = 'c';

// 执行上述代码后,数组 $a 将是
// array('color' => 'red', 'taste' => 'sweet', 'shape' => 'round',
// 'name' => 'apple', 0 => 4), 数组 $b 将是
// array(0 => 'a', 1 => 'b', 2 => 'c'), 或简单的 array('a', 'b', 'c').
?>

示例9 使用array()

<?php
// Array as (property-)map
$map = array( 'version' => 4,
'OS' => 'Linux',
'lang' => 'english',
'short_tags' => true
);

// 严格的数字键
$array = array( 7,
8,
0,
156,
-10
);
// 这相当于 array(0 => 7, 1 => 8, ...)

$switching = array( 10, // key = 0
5 => 6,
3 => 7,
'a' => 4,
11, // key = 6 (整数索引的最大值为 5)
'8' => 2, // key = 8 (整数!)
'02' => 77, // key = '02'
0 => 12 // 值 10 被 12 覆盖
);

// 空数组
$empty = array();
?>

示例10 集合

<?php
$colors = array('red', 'blue', 'green', 'yellow');

foreach ($colors as $color) {
echo "Do you like $color?\n";
}

?>

以上例程会输出:

Do you like red?
Do you like blue?
Do you like green?
Do you like yellow?

可以通过引用传递 array 的值来直接更改数组的值

示例11 在循环中改变单元

<?php
foreach ($colors as &$color) {
$color = strtoupper($color);
}
unset($color); /* 确保后面对
$color 的写入不会修改最后一个数组元素 */

print_r($colors);
?>

以上例程会输出:

Array
(
[0] => RED
[1] => BLUE
[2] => GREEN
[3] => YELLOW
)

本例生成一个下标从 1 开始的数组。

示例12 下标从 1 开始的数组

<?php
$firstquarter = array(1 => 'January', 'February', 'March');
print_r($firstquarter);
?>

以上例程会输出:

Array
(
[1] => 'January'
[2] => 'February'
[3] => 'March'
)

示例13 填充数组

<?php
// 把指定目录中的所有项填充到数组
$handle = opendir('.');
while (false !== ($file = readdir($handle))) {
$files[] = $file;
}
closedir($handle);
?>

Array 是有序的。也可以使用不同的排序函数来改变顺序。更改信息参见数组函数。可以用 count() 函数来统计出 array 中元素的个数。

示例14 数组排序

<?php
sort($files);
print_r($files);
?>

因为 array 中的值可以为任意值,也可是另一个 array。这样可以产生递归或多维 array。

示例15 递归和多维数组

<?php
$fruits = array ( "fruits" => array ( "a" => "orange",
"b" => "banana",
"c" => "apple"
),
"numbers" => array ( 1,
2,
3,
4,
5,
6
),
"holes" => array ( "first",
5 => "second",
"third"
)
);

// 处理上面数组中的值的一些例子
echo $fruits["holes"][5]; // 打印 "second"
echo $fruits["fruits"]["a"]; // 打印 "orange"
unset($fruits["holes"][0]); // 删除 "first"

// 创建一个新的多维数组
$juices["apple"]["green"] = "good";
?>

Array 的赋值总是会涉及到值的拷贝。使用 引用运算符 通过引用来拷贝 array。

<?php
$arr1 = array(2, 3);
$arr2 = $arr1;
$arr2[] = 4; // $arr2 已更改,
// $arr1 仍然是 array(2, 3)

$arr3 = &$arr1;
$arr3[] = 4; // 现在 $arr1 和 $arr3 是一样的
?>

Iterable 可迭代对象

Iterable是 PHP 7.1 中引入的一个伪类型。它接受任何 array 或实现了 Traversable 接口的对象。这些类型都能用 foreach 迭代, 也可以和 生成器 里的 yield from 一起使用。

使用可迭代对象

可迭代对象可以用作参数类型,表示函数需要一组值, 但是不会关心值集的形式,因为它将与 foreach 一起使用。如果一个值不是数组或 Traversable 的实例,则会抛出一个 TypeError

示例1 Interable 可迭代参数类型示例

<?php

function foo(iterable $iterable) {
foreach ($iterable as $value) {
// ...
}
}

?>

声明为可迭代的参数可能会使用 null 或者一个数组作为默认值。

示例2 可迭代参数默认值示例

<?php

function foo(iterable $iterable = []) {
// ...
}

?>

可迭代对象还可以用作返回类型,表示函数将返回一个可迭代的值。 如果返回值不是数组或 Traversable 的实例,则会抛出一个 TypeError

示例3 可迭代返回类型示例

<?php

function bar(): iterable {
return [1, 2, 3];
}

?>

将可迭代对象声明为返回类型的函数也可能是 生成器

示例4 可迭代生成器返回类型的示例

<?php

function gen(): iterable {
yield 1;
yield 2;
yield 3;
}

?>

可迭代类型的协变

类在扩展/实现(extending/implementing)的时候, 可以将参数类型从 array 或 Traversable 放宽到 iterable, 也可以将返回类型 iterable 的范围缩小到 array 或 Traversable。

示例5 可迭代的类型协变示例

<?php

interface Example {
public function method(array $array): iterable;
}

class ExampleImplementation implements Example {
public function method(iterable $iterable): array {
// 放宽了参数类型,缩窄了返回的类型
}
}

?>

Object 对象

对象初始化

要创建一个新的对象 object,使用 new 语句 实例化一个类:

<?php
class foo
{
function do_foo()
{
echo "Doing foo.";
}
}

$bar = new foo;
$bar->do_foo();
?>

详细讨论参见手册中 类与对象 章节

转换为对象

如果将一个对象转换成对象,它将不会有任何变化。如果其他任何类型的值被转换成对象,将会创建一个内置类 stdClass 的实例。如果该值为 null,则新的实例为空。 array 转换成 object 将使键名成为属性名并具有相应的值。注意:在这个例子里,使用 PHP 7.2.0 之前的版本,数字键只能通过迭代访问。

<?php
$obj = (object) array('1' => 'foo');
var_dump(isset($obj->{'1'})); // PHP 7.2.0 后输出 'bool(true)',之前版本会输出 'bool(false)'
var_dump(key($obj)); // PHP 7.2.0 后输出 'string(1) "1"',之前版本输出 'int(1)'
?>

对于其他值,会包含进成员变量名 scalar。

<?php
$obj = (object) 'ciao';
echo $obj->scalar; // outputs 'ciao'
?>
var_dump($obj);
// 输出
object(stdClass)#1 (1) {
["scalar"]=>
string(4) "ciao"
}

Resource 资源类型

资源 resource 是一种特殊变量,保存了到外部资源的一个引用。资源是通过专门的函数来建立和使用的。所有这些函数及其相应资源类型见附录

转换为资源

由于资源类型变量保存有为打开文件、数据库连接、图形画布区等的特殊句柄,因此将其他类型的值转换为资源没有意义。

释放资源

引用计数系统是 Zend 引擎的一部分,可以自动检测到一个资源不再被引用了(和 Java 一样)。这种情况下此资源使用的所有外部资源都会被垃圾回收系统释放。因此,很少需要手工释放内存

caution

持久数据库连接比较特殊,他们不会被垃圾回收系统销毁。参见数据库永久连接一章

NULL

特殊的 null 值表示一个变量没有值。NULL 类型唯一可能的值就是 null

在下列情况下一个变量被认为是 null:

  • 被赋值为 null。
  • 尚未被赋值。
  • 被 unset()。

语法

null 类型只有一个值,就是不区分大小写的常量 null

<?php
$var = NULL;

?>

参见 is_null()unset()

转换到 NULL

caution

本特性已从 PHP7.2.0 起废弃。强烈建议不要使用本特性。

使用 (unset) $var 将一个变量转换为 null 将不会删除删除该变量或 unset 其值。仅是返回 null 值而已

Callback / Callable 类型

官方文档

自 PHP 5.4 起可用 callable 类型指定回调类型 callback。本文档基于同样理由使用 callback 类型信息。

一些函数如 call_user_func()usort() 可以接受用户自定义的回调函数作为参数。回调函数不止可以是简单函数,还可以是对象的方法,包括静态类方法。

传递

PHP 是将函数以 string 形式传递的。可以使用任何内置或用户自定义函数,但除了语言结构例如:array(),echo,empty(),eval(),exit(),isset(),list(),print 或 unset()。

一个已实例化的 object 的方法被作为 array 传递,下标 0 包含该 object,下标 1 包含方法名。 在同一个类里可以访问 protected 和 private 方法。

静态类方法也可不经实例化该类的对象而传递,只要在下标 0 中包含类名而不是对象。自 PHP 5.2.3 起,也可以传递 'ClassName::methodName'。

除了普通的用户自定义函数外,也可传递 匿名函数 给回调参数。

示例1 回调函数示例

<?php

// An example callback function
function my_callback_function() {
echo 'hello world!';
}

// An example callback method
class MyClass {
static function myCallbackMethod() {
echo 'Hello World!';
}
}

// Type 1: Simple callback
call_user_func('my_callback_function');

// Type 2: Static class method call
call_user_func(array('MyClass', 'myCallbackMethod'));

// Type 3: Object method call
$obj = new MyClass();
call_user_func(array($obj, 'myCallbackMethod'));

// Type 4: Static class method call (As of PHP 5.2.3)
call_user_func('MyClass::myCallbackMethod');

// Type 5: Relative static class method call (As of PHP 5.3.0)
class A {
public static function who() {
echo "A\n";
}
}

class B extends A {
public static function who() {
echo "B\n";
}
}

call_user_func(array('B', 'parent::who')); // A

// Type 6: Objects implementing __invoke can be used as callables (since PHP 5.3)
class C {
public function __invoke($name) {
echo 'Hello ', $name, "\n";
}
}

$c = new C();
call_user_func($c, 'PHP!');
?>

示例2 使用 Closure 的示例

<?php
// Our closure
$double = function($a) {
return $a * 2;
};

// This is our range of numbers
$numbers = range(1, 5);

// Use the closure as a callback here to
// double the size of each element in our
// range
$new_numbers = array_map($double, $numbers);

print implode(' ', $new_numbers);
?>

以上例程会输出:

2 4 6 8 10
caution

在函数中注册有多个回调内容时(如使用 call_user_func()call_user_func_array() ),如在前一个回调中有未捕获的异常,其后的将不再被调用。

类型声明

官方文档

类型声明可以用于函数的参数、返回值,PHP 7.4.0 起还可以用于类的属性,来显性的指定需要的类型,如果预期类型在调用时不匹配,则会抛出一个 TypeError 异常。

note

当子类覆盖父方法时,子类的方法必须匹配父类的声明类型。如果父类没有定义返回类型,那么子方法可以指定自己的返回类型。

单一类型

官方文档

  • self
  • array
  • callable
  • bool
  • float
  • int
  • string
  • iterable
  • object
  • mixed

范例

<?PHP
class C {}
class D extends C {}

// 他没有继承 C
class E {}

function f(C $c) {
echo get_class($c)."\n";
}

f(new C);
f(new D);
f(new E);

以上例程在PHP 8 中会输出:

C
D

Fatal error: Uncaught TypeError: f(): Argument #1 ($c) must be of type C, E given, called in /in/gLonb on line 14 and defined in /in/gLonb:8
Stack trace:
#0 -(14): f(Object(E))
#1 {main}
thrown in - on line 8

返回类型声明

function sum($a, $b): float {
return $a + $b;
}

// 注意必须返回一个 float
var_dump(sum(1, 2);

以上例程会输出:

float(3)

示例4 返回一个对象

<?PHP
class C {}

function getC(): C {
return new C;
}
var_dump(getC());

以上例程会输出:

object(C)#1 (0) {
}

允许为空的 (Nullable) 类型

官方文档

联合类型

官方文档

允许为空的联合类型

false 伪类型

重复冗余的类型

仅仅返回类型

官方文档

void

static

严格类型

联合类型

………………other

类型转换的判别

PHP 在变量定义中不需要(或不支持)明确的类型定义;变量类型是根据使用该变量的上下文所决定的。也就是说,如果把一个 string 值赋给变量 $var,$var 就成了一个 string。如果又把一个int 赋给 $var,那它就成了一个int。

官方文档

有时在类型之间强制转换时确切地会发生什么可能不是很明显。更多信息见如下小节: