1
0
mirror of https://github.com/php/doc-zh.git synced 2026-03-24 07:02:15 +01:00
Files
archived-doc-zh/language/types/array.xml

1501 lines
37 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<!-- EN-Revision: 3061900171d4ae84d532571bfd6eda823726dad4 Maintainer: HonestQiao Status: ready -->
<!-- CREDITS: Geogory, dallas, jwj, mowangjuanzi, Luffy -->
<sect1 xml:id="language.types.array">
<title>Array 数组</title>
<para>
有关数组操作的更多信息,请参阅 <link linkend="ref.array">数组函数</link> 章节的文档。
</para>
<para>
PHP 中的 <type>array</type> 实际上是一个有序映射。映射是一种把
<emphasis>values</emphasis> 关联到 <emphasis>keys</emphasis> 的类型。此类型针对多种不同用途进行了优化;
它可以被视为数组、列表(向量)、哈希表(映射的实现)、字典、集合、堆栈、队列等等。
由于 <type>array</type> 的值可以是其它 <type>array</type>
所以树形结构和多维 <type>array</type> 也是允许的。
</para>
<para>
解释这些数据结构超出了本手册的范围,但对每种结构至少会提供一个例子。
要得到这些数据结构的更多信息,建议参考有关此广阔主题的其它文献。
</para>
<sect2 xml:id="language.types.array.syntax">
<title>语法</title>
<sect3 xml:id="language.types.array.syntax.array-func">
<title>定义数组 <function>array</function></title>
<para>
可以用 <function>array</function> 语言结构来新建一个 <type>array</type>。它接受任意数量用逗号分隔的
<literal><replaceable>key</replaceable> =&gt; <replaceable>value</replaceable></literal> 对。
</para>
<synopsis>
array(
<optional><replaceable>key</replaceable> =&gt; </optional><replaceable>value</replaceable>,
<optional><replaceable>key2</replaceable> =&gt; </optional><replaceable>value2</replaceable>,
<optional><replaceable>key3</replaceable> =&gt; </optional><replaceable>value3</replaceable>,
...
)</synopsis>
<!-- Do not fix the whitespace for the synopsis end element. A limitation of PhD prevents proper trimming -->
<para>
最后一个数组单元之后的逗号可以省略。通常用于单行数组定义中,例如常用
<literal>array(1, 2)</literal> 而不是
<literal>array(1, 2, )</literal>。对多行数组定义通常保留最后一个逗号,这样要添加一个新单元时更方便。
</para>
<note>
<para>
可以用短数组语法
<literal>[]</literal> 替代 <literal>array()</literal>
</para>
</note>
<example>
<title>一个简单数组</title>
<programlisting role="php">
<![CDATA[
<?php
$array1 = array(
"foo" => "bar",
"bar" => "foo",
);
// 使用短数组语法
$array2 = [
"foo" => "bar",
"bar" => "foo",
];
var_dump($array1, $array2);
?>
]]>
</programlisting>
</example>
<para>
<replaceable>key</replaceable> 可以是 <type>integer</type> 或者
<type>string</type><replaceable>value</replaceable> 可以是任意类型。
</para>
<para xml:id="language.types.array.key-casts">
此外 <replaceable>key</replaceable> 会有如下的强制转换:
<itemizedlist>
<listitem>
<simpara>
<type>String</type> 中包含有效的十进制 <type>int</type>,除非数字前面有一个 <literal>+</literal>
号,否则将被转换为 <type>int</type> 类型。例如键名 <literal>"8"</literal> 实际会被储存为
<literal>8</literal>。另外, <literal>"08"</literal>
不会被强制转换,因为它不是一个有效的十进制整数。
</simpara>
</listitem>
<listitem>
<simpara>
<type>Float</type> 也会被转换为 <type>int</type> ,意味着其小数部分会被舍去。例如键名
<literal>8.7</literal> 实际会被储存为 <literal>8</literal>
</simpara>
</listitem>
<listitem>
<simpara>
<type>Bool</type> 也会被转换成 <type>int</type>。即键名
&true; 实际会被储存为 <literal>1</literal>
而键名 &false; 会被储存为 <literal>0</literal>
</simpara>
</listitem>
<listitem>
<simpara>
<type>Null</type> 会被转换为空字符串,即键名
<literal>null</literal> 实际会被储存为 <literal>""</literal>
</simpara>
</listitem>
<listitem>
<simpara>
<type>Array</type><type>object</type> <emphasis>不能</emphasis>
被用为键名。坚持这么做会导致警告:<literal>Illegal offset type</literal>
</simpara>
</listitem>
</itemizedlist>
</para>
<para>
如果在数组定义时多个元素都使用相同键名,那么只有最后一个会被使用,其它的元素都会被覆盖。
</para>
<example>
<title>类型转换与覆盖的示例</title>
<programlisting role="php">
<![CDATA[
<?php
$array = array(
1 => "a",
"1" => "b",
1.5 => "c",
true => "d",
);
var_dump($array);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(1) {
[1]=>
string(1) "d"
}
]]>
</screen>
<para>
上例中所有的键名都被强制转换为 <literal>1</literal>,则每一个新单元都会覆盖前一个的值,最后剩下的只有一个
<literal>"d"</literal>
</para>
</example>
<para>
PHP 数组可以同时含有 <type>int</type><type>string</type> 类型的键名,因为
PHP 实际并不区分索引数组和关联数组。
</para>
<example>
<title>混合 <type>int</type><type>string</type> 键名</title>
<programlisting role="php">
<![CDATA[
<?php
$array = array(
"foo" => "bar",
"bar" => "foo",
100 => -100,
-100 => 100,
);
var_dump($array);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(4) {
["foo"]=>
string(3) "bar"
["bar"]=>
string(3) "foo"
[100]=>
int(-100)
[-100]=>
int(100)
}
]]>
</screen>
</example>
<para>
<replaceable>key</replaceable> 为可选项。如果未指定PHP
将自动使用之前用过的最大 <type>int</type> 键名加上 1 作为新的键名。
</para>
<example>
<title>没有键名的索引数组</title>
<programlisting role="php">
<![CDATA[
<?php
$array = array("foo", "bar", "hello", "world");
var_dump($array);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(4) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
[2]=>
string(5) "hello"
[3]=>
string(5) "world"
}
]]>
</screen>
</example>
<para>
还可以只对某些单元指定键名而对其它的空置:
</para>
<example>
<title>仅对部分单元指定键名</title>
<programlisting role="php">
<![CDATA[
<?php
$array = array(
"a",
"b",
6 => "c",
"d",
);
var_dump($array);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(4) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[6]=>
string(1) "c"
[7]=>
string(1) "d"
}
]]>
</screen>
<para>
可以看到最后一个值 <literal>"d"</literal> 被自动赋予了键名
<literal>7</literal>。这是由于之前最大的整数键名是 <literal>6</literal>
</para>
</example>
<example>
<title>复杂类型转换和覆盖的例子</title>
<para>
这个例子包括键名类型转换和元素覆盖的所有变化。
</para>
<programlisting role="php">
<![CDATA[
<?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);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
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"
}
]]>
</screen>
</example>
<example>
<title>负数索引示例</title>
<simpara>
当分配负整数 key <literal>n</literal>PHP 会将下一个 key 分配给 <literal>n+1</literal>
</simpara>
<programlisting role="php">
<![CDATA[
<?php
$array = [];
$array[-5] = 1;
$array[] = 2;
var_dump($array);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(2) {
[-5]=>
int(1)
[-4]=>
int(2)
}
]]>
</screen>
<warning>
<simpara>
PHP 8.3.0 之前,分配负整数 key <literal>n</literal> 会将下一个 key 分配给
<literal>0</literal>,因此前面的示例将输出:
</simpara>
<informalexample>
<screen>
<![CDATA[
array(2) {
[-5]=>
int(1)
[0]=>
int(2)
}
]]>
</screen>
</informalexample>
</warning>
</example>
</sect3>
<sect3 xml:id="language.types.array.syntax.accessing">
<title>用方括号语法访问数组单元</title>
<para>
数组单元可以通过 <literal>array[key]</literal> 语法来访问。
</para>
<example>
<title>访问数组单元</title>
<programlisting role="php">
<![CDATA[
<?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"]);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
string(3) "bar"
int(24)
string(3) "foo"
]]>
</screen>
</example>
<note>
<para>
在 PHP 8.0.0 之前,方括号和花括号可以互换使用来访问数组单元(例如
<literal>$array[42]</literal><literal>$array{42}</literal>
在上例中效果相同)。 花括号语法在 PHP 7.4.0 中已弃用,在 PHP 8.0.0 中不再支持。
</para>
</note>
<example>
<title>数组解引用</title>
<programlisting role="php">
<![CDATA[
<?php
function getArray() {
return array(1, 2, 3);
}
$secondElement = getArray()[1];
var_dump($secondElement);
?>
]]>
</programlisting>
</example>
<note>
<para>
试图访问一个未定义的数组键名与访问任何未定义变量一样:会导致
<constant>E_WARNING</constant>
级别错误信息(在 PHP 8.0.0 之前是 <constant>E_NOTICE</constant>
级别),其结果为 &null;
</para>
</note>
<note>
<para>
数组解引用非 <type>string</type> 的标量值会产生 &null;
在 PHP 7.4.0 之前,它不会发出错误消息。
从 PHP 7.4.0 开始,这个问题产生 <constant>E_NOTICE</constant>
从 PHP 8.0.0 开始,这个问题产生 <constant>E_WARNING</constant>
</para>
</note>
</sect3>
<sect3 xml:id="language.types.array.syntax.modifying">
<title>用方括号的语法新建/修改</title>
<para>
可以通过明示地设定其中的值来修改现有的 <type>array</type>
</para>
<para>
这是通过在方括号内指定键名来给 <type>array</type>
赋值实现的。也可以省略键名,在这种情况下给变量名加上一对空的方括号(<literal>[]</literal>)。
</para>
<synopsis>
$arr[<replaceable>key</replaceable>] = <replaceable>value</replaceable>;
$arr[] = <replaceable>value</replaceable>;
// <replaceable>key</replaceable> 可以是 <type>int</type><type>string</type>
// <replaceable>value</replaceable> 可以是任意类型的值</synopsis>
<para>
如果 <varname>$arr</varname> 不存在或者设置为 &null; 或者 &false;,将会新建它,这也是另一种创建
<type>array</type> 的方法。不过并不鼓励这样做,因为如果
<varname>$arr</varname> 已经包含有值(例如来自请求变量的 <type>string</type>)则此值会保留而
<literal>[]</literal> 实际上代表着<link
linkend="language.types.string.substr">字符串访问运算符</link>。初始化变量的最好方式是直接给其赋值。
</para>
<note>
<simpara>
从 PHP 7.1.0 起,对字符串应用空索引操作符会抛出致命错误。以前,字符串被静默地转换为数组。
</simpara>
</note>
<note>
<simpara>
从 PHP 8.1.0 起,弃用从 &false; 值中创建一个新数组。
但仍然允许从 &null; 或者未定义的变量中创建新数组。
</simpara>
</note>
<para>
要修改某个值,通过其键名给该单元赋一个新值。要删除某键值对,对其调用
<function>unset</function> 函数。
</para>
<example>
<title>Using Square Brackets with Arrays</title>
<programlisting role="php">
<![CDATA[
<?php
$arr = array(5 => 1, 12 => 2);
$arr[] = 56; // 这与 $arr[13] = 56 相同;
// 在脚本的这一点上
$arr["x"] = 42; // 添加一个新元素
// 键名使用 "x"
unset($arr[5]); // 从数组中删除元素
unset($arr); // 删除整个数组
?>
]]>
</programlisting>
</example>
<note>
<para>
如上所述,如果给出方括号但没有指定键名,则取当前最大
<type>int</type> 索引值,新的键名将是该值加上
1但是最小为 0。如果当前还没有 <type>int</type> 索引,则键名将为
<literal>0</literal>
</para>
<para>
注意这里所使用的最大整数键名<emphasis>目前不需要存在于 <type>array</type>
</emphasis>。 它只要在上次 <type>array</type> 重新生成索引后曾经存在于
<type>array</type> 就行了。以下面的例子来说明:
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
// 创建一个简单的数组
$array = array(1, 2, 3, 4, 5);
print_r($array);
// 现在删除其中的所有元素,但保持数组本身不变:
foreach ($array as $i => $value) {
unset($array[$i]);
}
print_r($array);
// 添加一个单元(注意新的键名是 5而不是你可能以为的 0
$array[] = 6;
print_r($array);
// 重新索引:
$array = array_values($array);
$array[] = 7;
print_r($array);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
)
Array
(
)
Array
(
[5] => 6
)
Array
(
[0] => 6
[1] => 7
)
]]>
</screen>
</informalexample>
</note>
</sect3>
<sect3 xml:id="language.types.array.syntax.destructuring">
<title>数组解包</title>
<para>
可以使用 <literal>[]</literal>(自 PHP 7.1.0 起)或者 <function>list</function>
语言结构解包数组。这些结构可用于将数组解包为不同的变量。
</para>
<example>
<title>Array Destructuring</title>
<programlisting role="php">
<![CDATA[
<?php
$source_array = ['foo', 'bar', 'baz'];
[$foo, $bar, $baz] = $source_array;
echo $foo, PHP_EOL; // 打印 "foo"
echo $bar, PHP_EOL; // 打印 "bar"
echo $baz, PHP_EOL; // 打印 "baz"
?>
]]>
</programlisting>
</example>
<para>
数组解包可用于 &foreach; 在迭代多维数组时对其进行解包。
</para>
<example>
<title>Array Destructuring in Foreach</title>
<programlisting role="php">
<![CDATA[
<?php
$source_array = [
[1, 'John'],
[2, 'Jane'],
];
foreach ($source_array as [$id, $name]) {
echo "{$id}: '{$name}'\n";
}
?>
]]>
</programlisting>
</example>
<para>
如果变量未提供,数组元素将会被忽略。数组解包始终从索引 <literal>0</literal> 开始。
</para>
<example>
<title>Ignoring Elements</title>
<programlisting role="php">
<![CDATA[
<?php
$source_array = ['foo', 'bar', 'baz'];
// 将索引 2 的元素分配给变量 $baz
[, , $baz] = $source_array;
echo $baz; // 打印 "baz"
?>
]]>
</programlisting>
</example>
<para>
自 PHP 7.1.0 起,也可以解包关联数组。这在数字索引数组中更容易选择正确的元素,因为可以显式指定索引。
</para>
<example>
<title>Destructuring Associative Arrays</title>
<programlisting role="php">
<![CDATA[
<?php
$source_array = ['foo' => 1, 'bar' => 2, 'baz' => 3];
// 将索引 'baz' 处的元素分配给变量 $three
['baz' => $three] = $source_array;
echo $three, PHP_EOL; // 打印 3
$source_array = ['foo', 'bar', 'baz'];
// 将索引 2 处的元素分配给变量 $baz
[2 => $baz] = $source_array;
echo $baz, PHP_EOL; // 打印 "baz"
?>
]]>
</programlisting>
</example>
<para>
数组解包可以方便的用于两个变量交换。
</para>
<example>
<title>Swapping Two Variable</title>
<programlisting role="php">
<![CDATA[
<?php
$a = 1;
$b = 2;
[$b, $a] = [$a, $b];
echo $a, PHP_EOL; // 打印 2
echo $b, PHP_EOL; // 打印 1
?>
]]>
</programlisting>
</example>
<note>
<para>
分配时不支持展开运算符(<literal>...</literal>)。
</para>
</note>
<note>
<para>
尝试访问未定义的数组键与访问任何未定义的变量相同:都将发出 <constant>E_WARNING</constant>
级别的错误消息PHP 8.0.0 之前是 <constant>E_NOTICE</constant> 级别),结果将是 &null;
</para>
</note>
<note>
<para>
解构标量值会分配 &null; 到所有变量。
</para>
</note>
</sect3>
</sect2><!-- end syntax -->
<sect2 xml:id="language.types.array.useful-funcs">
<title>实用函数</title>
<para>
有很多操作数组的函数,参见 <link linkend="ref.array">数组函数</link> 一节。
</para>
<note>
<para>
<function>unset</function> 函数允许删除
<type>array</type> 中的某个键。但要注意数组将<emphasis>不会</emphasis>重建索引。如果需要删除后重建索引,可以用
<function>array_values</function> 函数重建 <type>array</type> 索引。
</para>
<example>
<title>Unsetting Intermediate Elements</title>
<programlisting role="php">
<![CDATA[
<?php
$a = array(1 => 'one', 2 => 'two', 3 => 'three');
/* 该数组将被定义为
$a = array(1 => 'one', 3 => 'three');
而不是
$a = array(1 => 'one', 2 =>'three');
*/
unset($a[2]);
var_dump($a);
$b = array_values($a);
// Now $b is array(0 => 'one', 1 =>'three')
var_dump($b);
?>
]]>
</programlisting>
</example>
</note>
<para>
&foreach; 控制结构是专门用于
<type>array</type> 的。它提供了一个简单的方法来遍历 <type>array</type>
</para>
</sect2>
<sect2 xml:id="language.types.array.donts">
<title>数组做什么和不做什么</title>
<sect3 xml:id="language.types.array.foo-bar">
<title>为什么 <literal>$foo[bar]</literal> 错了?</title>
<para>
应该始终在用字符串表示的数组索引上加上引号。例如用
<literal>$foo['bar']</literal> 而不是
<literal>$foo[bar]</literal>。但是为什么呢?可能在老的脚本中见过如下语法:
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$foo[bar] = 'enemy';
echo $foo[bar];
// 及其它
?>
]]>
</programlisting>
</informalexample>
<para>
这样是错的,但可以正常运行。那么为什么错了呢?原因是此代码中有一个未定义的常量(
<literal>bar</literal> )而不是 <type>string</type><literal>'bar'</literal>-注意引号)。而
PHP 可能会在以后定义此常量,不幸的是你的代码中有同样的名字。它能运行,是因为
PHP 自动将<emphasis>裸字符串</emphasis>(没有引号的 <type>string</type>
且不对应于任何已知符号)转换成一个其值为该裸 <type>string</type>
<type>string</type>。例如,如果没有常量定义为
<constant>bar</constant>那么PHP 将在
<type>string</type> 中替代为 <literal>'bar'</literal> 并使用之。
</para>
<warning>
<simpara>
将未定义的常量当作裸字符串的回退会触发 <constant>E_NOTICE</constant> 级别错误。
从 PHP 7.2.0 起已废弃,并触发 <constant>E_WARNING</constant> 级别错误。
从 PHP 8.0.0 起被移除,并触发 <classname>Error</classname> 异常。
</simpara>
</warning>
<simpara>
这并不意味着<emphasis>总是</emphasis>给键名加上引号。用不着给键名为
<link linkend="language.constants">常量</link>
<link linkend="language.variables">变量</link> 的加上引号,否则会使 PHP
不能解析它们。
</simpara>
<example>
<title>Key Quoting</title>
<programlisting role="php">
<![CDATA[
<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('html_errors', false);
// 简单数组:
$array = array(1, 2);
$count = count($array);
for ($i = 0; $i < $count; $i++) {
echo "\n检查 $i: \n";
echo "坏的: " . $array['$i'] . "\n";
echo "好的: " . $array[$i] . "\n";
echo "坏的: {$array['$i']}\n";
echo "好的: {$array[$i]}\n";
}
?>
]]>
</programlisting>
</example>
&example.outputs;
<screen>
<![CDATA[
检查 0:
Notice: Undefined index: $i in /path/to/script.html on line 9
坏的:
好的: 1
Notice: Undefined index: $i in /path/to/script.html on line 11
坏的:
好的: 1
检查 1:
Notice: Undefined index: $i in /path/to/script.html on line 9
坏的:
好的: 2
Notice: Undefined index: $i in /path/to/script.html on line 11
坏的:
好的: 2
]]>
</screen>
<para>
演示此行为的更多例子:
</para>
<example>
<title>More Examples</title>
<programlisting role="php">
<![CDATA[
<?php
// 显示所有错误
error_reporting(E_ALL);
$arr = array('fruit' => 'apple', 'veggie' => 'carrot');
// 正确
echo $arr['fruit'], PHP_EOL; // apple
echo $arr['veggie'], PHP_EOL; // carrot
// 不正确。此操作无效,并会因未定义名为 fruit 的常量而抛出 PHP 错误
//
// Error: Undefined constant "fruit"
try {
echo $arr[fruit];
} catch (Error $e) {
echo get_class($e), ': ', $e->getMessage(), PHP_EOL;
}
// 此处定义了一个常量以说明其行为,将值 'veggie' 赋予名为 fruit 的常量
define('fruit', 'veggie');
// 注意现在不同
echo $arr['fruit'], PHP_EOL; // apple
echo $arr[fruit], PHP_EOL; // carrot
// 以下写法是允许的,因为其位于字符串内部。常量在字符串中不会被查找,因此此处不会产生错误
echo "Hello $arr[fruit]", PHP_EOL; // Hello apple
// 例外:字符串中用花括号包围的数组允许对常量进行解析
echo "Hello {$arr[fruit]}", PHP_EOL; // Hello carrot
echo "Hello {$arr['fruit']}", PHP_EOL; // Hello apple
// 拼接是另一种选择
echo "Hello " . $arr['fruit'], PHP_EOL; // Hello apple
?>
]]>
</programlisting>
</example>
<informalexample>
<programlisting role="php">
<![CDATA[
// 这将不起作用,并会导致解析错误,例如:
// Parse error: parse error, expecting T_STRING' or T_VARIABLE' or T_NUM_STRING'
// 这当然也适用于在字符串中使用超全局变量
print "Hello $arr['fruit']";
print "Hello $_GET['foo']";
?>
]]>
</programlisting>
</informalexample>
<para>
和在 <link linkend="language.types.array.syntax">语法</link>
一节中规定的一样,在方括号(<literal>[</literal>” 和
<literal>]</literal>”)之间必须有一个表达式。这意味着可以这样写:
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
echo $arr[somefunc($bar)];
?>
]]>
</programlisting>
</informalexample>
<para>
这是一个用函数返回值作为数组索引的例子。PHP 也可以用已知常量,可能之前已经见过:
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$error_descriptions[E_ERROR] = "A fatal error has occurred";
$error_descriptions[E_WARNING] = "PHP issued a warning";
$error_descriptions[E_NOTICE] = "This is just an informal notice";
?>
]]>
</programlisting>
</informalexample>
<para>
注意 <literal>E_ERROR</literal> 也是个合法的标识符,就和第一个例子中的
<literal>bar</literal> 一样。但是上一个例子实际上和如下写法是一样的:
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$error_descriptions[1] = "A fatal error has occurred";
$error_descriptions[2] = "PHP issued a warning";
$error_descriptions[8] = "This is just an informal notice";
?>
]]>
</programlisting>
</informalexample>
<para>
因为 <literal>E_ERROR</literal> 等于 <literal>1</literal>,等等。
</para>
<sect4 xml:id="language.types.array.foo-bar.why">
<title>那么为什么这样做不好?</title>
<para>
也许有一天PHP 开发小组可能会想新增一个常量或者关键字,或者用户可能希望以后在自己的程序中引入新的常量,那就有麻烦了。例如已经不能这样用
<literal>empty</literal><literal>default</literal> 这两个词了,因为他们是
<link linkend="reserved">保留关键字</link>
</para>
<note>
<simpara>
重申一次,在双引号字符串中,不给索引加上引号是合法的因此
<literal>"$foo[bar]"</literal>
是合法的(“合法”的原文为 valid。在实际测试中这么做确实可以访问数组的该元素但是会报一个常量未定义的
notice。无论如何强烈建议不要使用 $foo[bar]这样的写法,而要使用 $foo['bar']
来访问数组中元素。--haohappy 注)。至于为什么参见以上的例子和
<link linkend="language.types.string.parsing">字符串中的变量解析</link> 中的解释。
</simpara>
</note>
</sect4>
</sect3>
</sect2>
<sect2 xml:id="language.types.array.casting">
<title>转换为数组</title>
<para>
对于任意 <type>int</type><type>float</type>
<type>string</type><type>bool</type><type>resource</type> 类型,如果将一个值转换为
<type>array</type>,将得到一个仅有一个元素的数组,其下标为
0该元素即为此标量的值。换句话说<code>(array) $scalarValue</code>
<literal>array($scalarValue)</literal> 完全一样。
</para>
<para>
如果将 <type>object</type> 类型转换为
<type>array</type>,则结果为一个数组,其单元为该对象的属性。键名将为成员变量名,不过有几点例外:整数属性不可访问;
私有变量前会加上类名作前缀;保护变量前会加上一个 '*' 做前缀。这些前缀的前后都各有一个 <literal>NUL</literal> 字节。
未初始化的<link linkend="language.oop5.properties.typed-properties">类型属性</link>将会被丢弃。
</para>
<example>
<title>Converting to an Array</title>
<programlisting role="php">
<![CDATA[
<?php
class A {
private $B;
protected $C;
public $D;
function __construct()
{
$this->{1} = null;
}
}
var_export((array) new A());
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array (
'' . "\0" . 'A' . "\0" . 'B' => NULL,
'' . "\0" . '*' . "\0" . 'C' => NULL,
'D' => NULL,
1 => NULL,
)
]]>
</screen>
</example>
<para>
这些 <literal>NUL</literal> 会导致一些意想不到的行为:
</para>
<example>
<title>Casting an Object to an Array</title>
<programlisting role="php">
<![CDATA[
<?php
class A {
private $A; // 将变为 '\0A\0A'
}
class B extends A {
private $A; // 将变为 '\0B\0A'
public $AA; // 将变为 'AA'
}
var_dump((array) new B());
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
array(3) {
["BA"]=>
NULL
["AA"]=>
NULL
["AA"]=>
NULL
}
]]>
</screen>
</example>
<para>
上例会有两个键名为 'AA',不过其中一个实际上是 '\0A\0A'。
</para>
<para>
&null; 转换为 <type>array</type> 会得到一个空的数组。
</para>
</sect2>
<sect2 xml:id="language.types.array.comparing">
<title>比较</title>
<para>
可以用 <function>array_diff</function> 函数和<link
linkend="language.operators.array">数组运算符</link>来比较数组。
</para>
</sect2>
<sect2 xml:id="language.types.array.unpacking">
<title>数组解包</title>
<para>
在 array 定义时,用 <code>...</code> 前缀的一个 array 可以被展开到当前位置。
只有实现了 <interfacename>Traversable</interfacename> 的数组和对象才能被展开。
PHP 7.4.0 开始可以使用 <code>...</code> 解包 array。这也被称之为展开运算符。
</para>
<para>
它可以多次使用,在 <code>...</code> 操作符前后都可以添加常规元素:
<example>
<title>简单的数组解包</title>
<programlisting role="php">
<![CDATA[
<?php
// 使用简短的数组语法。
// 亦可用于 array() 语法
$arr1 = [1, 2, 3];
$arr2 = [...$arr1]; // [1, 2, 3]
$arr3 = [0, ...$arr1]; // [0, 1, 2, 3]
$arr4 = [...$arr1, ...$arr2, 111]; // [1, 2, 3, 1, 2, 3, 111]
$arr5 = [...$arr1, ...$arr1]; // [1, 2, 3, 1, 2, 3]
function getArr() {
return ['a', 'b'];
}
$arr6 = [...getArr(), 'c' => 'd']; // ['a', 'b', 'c' => 'd']
var_dump($arr1, $arr2, $arr3, $arr4, $arr5, $arr6);
?>
]]>
</programlisting>
</example>
</para>
<para>
<code>...</code> 操作符解包 array 时也遵守函数 <function>array_merge</function> 的语义。
也就是说key 为字符时后面的字符键会覆盖之前的字符键key 为 integer 时则会重新编号:
<example>
<title>重复 key 的数组解包</title>
<programlisting role="php">
<![CDATA[
<?php
// string key
$arr1 = ["a" => 1];
$arr2 = ["a" => 2];
$arr3 = ["a" => 0, ...$arr1, ...$arr2];
var_dump($arr3); // ["a" => 2]
// integer key
$arr4 = [1, 2, 3];
$arr5 = [4, 5, 6];
$arr6 = [...$arr4, ...$arr5];
var_dump($arr6); // [1, 2, 3, 4, 5, 6]
// 即 [0 => 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5, 5 => 6]
// 也就是原始的 integer key 不再保留
?>
]]>
</programlisting>
</example>
</para>
<note>
<para>
既不是 integer 也不是 string 的 key 会抛出 <classname>TypeError</classname>
这类 key 只能通过 <interfacename>Traversable</interfacename> 对象生成。
</para>
</note>
<note>
<para>
在 PHP 8.1 之前,带有 string 键的 array 无法解包:
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$arr1 = [1, 2, 3];
$arr2 = ['a' => 4];
$arr3 = [...$arr1, ...$arr2];
// Fatal error: Uncaught Error: Cannot unpack array with string keys in example.php:5
$arr4 = [1, 2, 3];
$arr5 = [4, 5];
$arr6 = [...$arr4, ...$arr5]; // works. [1, 2, 3, 4, 5]
?>
]]>
</programlisting>
</informalexample>
</note>
</sect2>
<sect2 xml:id="language.types.array.examples">
<title>示例</title>
<para>
PHP 中的数组类型有非常多的用途。以下是一些示例:
</para>
<example>
<title>Array Versatility</title>
<programlisting role="php">
<![CDATA[
<?php
// 这里:
$a = array( 'color' => 'red',
'taste' => 'sweet',
'shape' => 'round',
'name' => 'apple',
4 // 键名为 0
);
$b = array('a', 'b', 'c');
var_dump($a, $b);
// . . .完全等同于:
$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').
?>
]]>
</programlisting>
</example>
<example>
<title>使用 array()</title>
<programlisting role="php">
<![CDATA[
<?php
// Array as (property-)map
$map = array( 'version' => 4,
'OS' => 'Linux',
'lang' => 'english',
'short_tags' => true
);
var_dump($map);
// strictly numerical keys
// this is the same as array(0 => 7, 1 => 8, ...)
$array = array( 7,
8,
0,
156,
-10
);
var_dump($array);
$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 覆盖
);
var_dump($switching);
// 空数组
$empty = array();
var_dump($empty);
?>
]]>
<!-- TODO example of
- overwriting keys
- using vars/functions as key/values
- warning about references
-->
</programlisting>
</example>
<example xml:id="language.types.array.examples.loop">
<title>集合</title>
<programlisting role="php">
<![CDATA[
<?php
$colors = array('red', 'blue', 'green', 'yellow');
foreach ($colors as $color) {
echo "Do you like $color?\n";
}
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Do you like red?
Do you like blue?
Do you like green?
Do you like yellow?
]]>
</screen>
</example>
<para>
可以通过引用传递 <type>array</type> 的值来直接更改数组的值。
</para>
<example xml:id="language.types.array.examples.changeloop">
<title>在循环中改变单元</title>
<programlisting role="php">
<![CDATA[
<?php
$colors = array('red', 'blue', 'green', 'yellow');
foreach ($colors as &$color) {
$color = mb_strtoupper($color);
}
unset($color); /* 确保后面对
$color 的写入不会修改最后一个数组元素 */
print_r($colors);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Array
(
[0] => RED
[1] => BLUE
[2] => GREEN
[3] => YELLOW
)
]]>
</screen>
</example>
<para>
本例生成一个下标从 1 开始的数组。
</para>
<example>
<title>下标从 1 开始的数组</title>
<programlisting role="php">
<![CDATA[
<?php
$firstquarter = array(1 => 'January', 'February', 'March');
print_r($firstquarter);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Array
(
[1] => January
[2] => February
[3] => March
)
]]>
</screen>
</example>
<example>
<title>填充数组</title>
<programlisting role="php">
<![CDATA[
<?php
// 把指定目录中的所有项填充到数组
$handle = opendir('.');
while (false !== ($file = readdir($handle))) {
$files[] = $file;
}
closedir($handle);
var_dump($files);
?>
]]>
</programlisting>
</example>
<para>
<type>Array</type> 是有序的。也可以使用不同的排序函数来改变顺序。更多信息参见
<link linkend="ref.array">数组函数</link>。可以用
<function>count</function> 函数来统计出
<type>array</type> 中元素的个数。
</para>
<example>
<title>数组排序</title>
<programlisting role="php" annotations="non-interactive">
<![CDATA[
<?php
sort($files);
print_r($files);
?>
]]>
</programlisting>
</example>
<para>
因为 <type>array</type> 中的值可以为任意值,也可是另一个
<type>array</type>。这样可以产生递归或多维 <type>array</type>
</para>
<example>
<title>递归和多维数组</title>
<programlisting role="php">
<![CDATA[
<?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"
)
);
var_dump($fruits);
// 处理上面数组中的值的一些例子
echo $fruits["holes"][5]; // 打印 "second"
echo $fruits["fruits"]["a"]; // 打印 "orange"
unset($fruits["holes"][0]); // 删除 "first"
// 创建一个新的多维数组
$juices["apple"]["green"] = "good";
var_dump($juices);
?>
]]>
</programlisting>
</example>
<para>
<type>Array</type> 的赋值总是会涉及到值的拷贝。使用
<link linkend="language.operators">引用运算符</link> 通过引用来拷贝
<type>array</type>
</para>
<example>
<title>Array Copying</title>
<programlisting role="php">
<![CDATA[
<?php
$arr1 = array(2, 3);
$arr2 = $arr1;
$arr2[] = 4; // $arr2 已更改,
// $arr1 仍然是 array(2, 3)
$arr3 = &$arr1;
$arr3[] = 4; // 现在 $arr1 和 $arr3 是一样的
var_dump($arr1, $arr2, $arr3);
?>
]]>
</programlisting>
</example>
</sect2>
</sect1>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->