PHP5 数组遍历与常用操作示例

最新新闻5小时前发布 jdkjadmin
0 00
PHP5 数组遍历与常用操作示例

PHP5 数组遍历与常用操作示例

PHP 数组既可以按数字索引保存列表,也可以用字符串键保存一组关联数据,还可以在同一个数组中混用两类键。它常用于处理表单输入、配置项、数据库查询结果和接口数据。掌握数组的遍历、增删改和键值判断,能减少不少常见错误。

本文示例使用 PHP5 兼容的数组语法,即 array(),不使用 PHP 5.4 才支持的短数组语法 []。示例可保存为 PHP 文件后运行;输出示例中可能包含 HTML 转义后的代码,以便在网页中显示。PHP5 已停止维护,若代码用于新项目,应使用仍受支持的 PHP 版本,并在迁移时验证行为差异。

一、创建数组与理解键

数组可以只给出值,让 PHP 自动分配数字键;也可以明确指定键。自动数字键通常从 0 开始,每添加一个没有显式键的元素,就使用当前可用的下一个整数键。

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

$user = array(
    'name' => 'Lin',
    'age' => 28,
    'active' => true
);

$mixed = array(
    'title' => 'report',
    0 => 'first',
    'note' => 'draft'
);
?>

键可以是整数或字符串。浮点数键会转换为整数;布尔键会转换为整数(true 变为 1,false 变为 0);null 键会转换为空字符串。数字格式的字符串键也可能被转换为整数键,例如 '8' 通常变为整数键 8;而带前导零的字符串如 '08' 不按同样规则转换。若键类型很重要,不要只凭表面写法判断,使用 var_dump() 检查实际键类型。

重复指定同一个键时,后面的值覆盖前面的值:

<?php
$data = array(
    'status' => 'pending',
    'status' => 'done'
);

var_dump($data['status']); // string(4) "done"
?>

这条规则对从配置、表单或外部数据构造数组时尤其重要:相同键不会形成两个独立项目。

二、使用 foreach 遍历

foreach 是遍历数组最常用的结构。只关心值时,写成 foreach ($array as $value);需要同时处理键和值时,写成 foreach ($array as $key => $value)。

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

foreach ($colors as $color) {
    echo $color, "\n";
}

$user = array(
    'name' => 'Lin',
    'age' => 28
);

foreach ($user as $key => $value) {
    echo $key, ': ', $value, "\n";
}
?>

遍历时,键保留数组中的实际键,不会因为数组中间有空位而自动重新编号。例如,删除一个元素后,剩余键可能是 0、2、3;foreach 会按当前元素顺序依次访问它们。

直接修改循环变量,默认不会改动原数组:

<?php
$numbers = array(1, 2, 3);

foreach ($numbers as $number) {
    $number = $number * 10;
}

print_r($numbers); // 仍为 1、2、3
?>

如果要在循环中改写数组元素,可按键赋值:

<?php
$numbers = array(1, 2, 3);

foreach ($numbers as $key => $number) {
    $numbers[$key] = $number * 10;
}

print_r($numbers); // 10、20、30
?>

引用遍历的风险

也可以使用引用让循环变量直接指向数组元素,但必须在循环结束后立即解除引用。否则,循环变量仍可能引用最后一个元素;之后再次给这个变量赋值,可能意外改写数组内容。

<?php
$numbers = array(1, 2, 3);

foreach ($numbers as &$number) {
    $number = $number * 10;
}
unset($number); // 解除最后一个元素的引用

print_r($numbers);
?>

除非确实需要按引用修改元素,通常使用“键赋值”的方式更容易理解,也更不容易留下隐患。

三、while、each 与数组内部指针

PHP5 还可以用 each() 配合 while 遍历数组。它每次返回当前键和值,并把数组内部指针向后移动;到达末尾时返回 false。

<?php
$items = array('a' => 10, 'b' => 20);

while (list($key, $value) = each($items)) {
    echo $key, '=', $value, "\n";
}
?>

这是一种旧式写法。each() 使用数组内部指针,若同一数组在其他地方也调用了 current()、next() 或 reset(),遍历位置可能互相影响。一般代码优先使用 foreach,它不依赖这类内部指针状态。

while (list(...) = each(...)) 的写法还要求理解返回值和赋值表达式的行为,不适合用来做简单遍历。阅读旧项目时需要认识它,编写新代码时没有必要优先选择。

四、读取元素与判断键是否存在

通过键读取元素时,若键不存在,PHP5 通常会产生“未定义索引”或“未定义偏移量”提示,并得到空值。访问之前可以先判断键是否存在。

<?php
$user = array('name' => 'Lin');

if (array_key_exists('name', $user)) {
    echo $user['name'];
}
?>

isset() 与 array_key_exists() 的关键区别是:isset() 在键不存在或对应值为 null 时都返回 false;array_key_exists() 只检查键是否存在,即使值为 null 也返回 true。

<?php
$data = array(
    'present_null' => null,
    'present_zero' => 0
);

var_dump(isset($data['missing']));      // false
var_dump(isset($data['present_null'])); // false
var_dump(array_key_exists('present_null', $data)); // true
var_dump(isset($data['present_zero'])); // true
?>

选择哪一种取决于业务含义:如果需要区分“没有这个字段”和“字段存在但值为 null”,用 array_key_exists();如果只关心值能否安全用于后续操作,常用 isset()。

isset、empty 与零值

empty() 会把不存在的变量或数组键也视作空,因此在这种情况下通常不会因未定义键产生提示。但它把多种值都判为“空”,包括 0、'0'、false、空字符串、空数组和 null。如果零是有效数据,不能用 empty() 判断字段是否提供。

<?php
$data = array(
    'count' => 0,
    'label' => '0',
    'enabled' => false
);

var_dump(empty($data['count'])); // true
var_dump(isset($data['count'])); // true
?>

例如,检查分页参数是否存在时,可以先用 isset(),再明确验证它是否为有效整数;不要因为页码 0 被 empty() 判空,就误认为它未提交。字段存在与字段内容有效是两个不同问题。

五、添加、修改和删除元素

给数组键赋值即可添加或覆盖元素;对没有键的数组使用方括号并省略键,可以追加到下一个可用的数字索引位置。

<?php
$items = array('pen', 'book');

$items[] = 'cup'; // 追加
$items[1] = 'notebook'; // 修改键 1 对应的值
$items['category'] = 'stationery'; // 添加关联元素

print_r($items);
?>

删除元素使用 unset()。它会移除元素,但不会自动重排其他数字键:

<?php
$items = array('zero', 'one', 'two');
unset($items[1]);

print_r($items);
// 键 0 和 2 仍然存在

$items[] = 'three'; // 追加时使用下一个可用数字键
print_r($items);
?>

如果需要把数字索引重新整理为从 0 开始连续排列,可以使用 array_values():

<?php
$items = array(0 => 'zero', 2 => 'two');
$items = array_values($items);

print_r($items); // 键变为 0、1
?>

注意,array_values() 会丢弃原有键。对于关联数组,不要为了“看起来整齐”而随意调用,否则字段名称会丢失。

六、数组长度与空数组

count() 返回数组元素个数。PHP5 中也可以通过第二个可选参数启用递归计数:count($array, COUNT_RECURSIVE) 会把嵌套数组中的元素也计入统计。不过它统计的是元素层级总数,不等同于“最外层有多少行”。

<?php
$rows = array(
    array('name' => 'A', 'score' => 90),
    array('name' => 'B', 'score' => 85)
);

echo count($rows); // 2:最外层元素数
echo count($rows, COUNT_RECURSIVE); // 6:包含内层元素
?>

要判断数组是否为空,可使用 count($array) === 0,或在已确认变量为数组的情况下使用 empty($array)。在 PHP5 中,count() 对非数组值的处理与版本及错误报告设置有关,不应把它当作类型验证。稳妥做法是先用 is_array() 检查。

<?php
$value = array();

if (is_array($value) && count($value) > 0) {
    echo '有元素';
} else {
    echo '不是非空数组';
}
?>

七、查找键和值

array_key_exists($key, $array) 检查键是否存在。in_array($needle, $array) 检查某个值是否在数组中;其第三个可选参数 $strict 默认为 false,设为 true 时会同时比较类型。

<?php
$ids = array(10, 20, '30');

var_dump(in_array(30, $ids)); // 非严格比较,可能匹配字符串 '30'
var_dump(in_array(30, $ids, true)); // 严格比较,不匹配字符串 '30'

$record = array('id' => 10);
var_dump(array_key_exists('id', $record)); // true
?>

处理用户输入、权限标识或需要严格类型判断的值时,应考虑使用 in_array() 的严格模式。非严格比较会遵循 PHP 的类型转换规则,某些看似不同的值可能被认为相等。即使严格比较,也不能替代对输入格式和业务范围的验证。

array_search() 返回匹配值对应的键;找不到时返回 false。因为有效键本身可能是整数 0,判断时应使用严格比较:

<?php
$values = array('red', 'green');

$key = array_search('red', $values, true);

if ($key !== false) {
    echo '找到,键为 ', $key;
}
?>

若写成 if ($key),键为 0 时会被当作假,导致误判为未找到。这是搜索数组时很常见的错误。

八、排序与键的保留

PHP 提供了多种排序函数,选择时要先明确是否需要保留键,以及要按键还是按值排序。

  • sort():按值升序排序,并重新建立数字索引。
  • rsort():按值降序排序,并重新建立数字索引。
  • asort():按值升序排序,保留键。
  • arsort():按值降序排序,保留键。
  • ksort()、krsort():按键升序或降序排序。
<?php
$scores = array(
    'Mia' => 82,
    'Noah' => 95,
    'Ivy' => 88
);

asort($scores); // 按分数升序,姓名键保留
print_r($scores);

ksort($scores); // 改为按姓名键排序
print_r($scores);
?>

这些排序函数会直接修改传入的数组。若原顺序还要用于其他操作,先复制一份再排序。对于字符串排序、自然排序或自定义规则,应查看相应函数的比较规则;特别是数值与字符串混合、大小写或语言环境相关数据,不宜假定默认比较方式符合业务预期。

九、合并、拼接与去重

array_merge() 会合并多个数组。字符串键重复时,后一个数组的值覆盖前一个;数字键则会重新编号,从 0 开始依次追加。

<?php
$defaults = array(
    'theme' => 'light',
    'items' => 2
);

$options = array(
    'theme' => 'dark',
    'items' => 5
);

$result = array_merge($defaults, $options);
print_r($result); // theme 为 dark,items 为 5
?>

若想让左侧的字符串键优先,可使用数组联合运算符 +:左边已有的键会保留,右边相同键不会覆盖;数字键也按键是否已存在来处理,不会像 array_merge() 那样一律重新编号。

<?php
$defaults = array('theme' => 'light', 0 => 'first');
$options = array('theme' => 'dark', 0 => 'second', 1 => 'third');

$result = $defaults + $options;
print_r($result);
?>

不要仅凭“合并”二字在两者间随意选择:有数字索引时,它们的结果尤其不同。

array_unique() 用于去除重复值,返回结果保留原键,通常不会自动把数字键整理为连续索引。

<?php
$tags = array('php', 'web', 'php');
$tags = array_values(array_unique($tags));

print_r($tags); // 索引整理为 0、1
?>

去重时也应留意比较规则以及不同类型值的比较结果。若值来自外部输入,先规范化数据类型,再按明确规则去重,往往比依赖隐式转换更可靠。

十、过滤与转换

array_filter() 可按回调函数筛选元素。回调收到元素值;若需要依据键和值共同判断,可在 PHP5.6 及以后使用相应的 ARRAY_FILTER_USE_BOTH 标志,但旧版 PHP5 并不支持该参数。因此若目标环境包含 PHP5.5 或更早版本,不要直接使用这个标志。

下面的示例只依赖较早版本可用的值回调写法:

<?php
$numbers = array(0, 1, 2, 3, 4);

$even = array_filter($numbers, function ($value) {
    return $value % 2 === 0;
});

print_r($even); // 保留原键:0、2、4
?>

匿名函数从 PHP 5.3 开始支持。如果项目运行在更早的 PHP5 版本,需要改用已定义的普通函数作为回调。筛选结果通常保留原键;需要连续数字索引时,再调用 array_values()。

要转换每个值,可用 array_map():

<?php
$prices = array(12, 25, 40);

$withTax = array_map(function ($price) {
    return $price * 1.1;
}, $prices);

print_r($withTax);
?>

匿名函数同样要求 PHP 5.3 或更高版本。对老旧目标版本,使用普通回调函数。无论过滤还是转换,都应确认输入确实是预期数组,并考虑空数组、嵌套数组和异常类型的处理。

十一、嵌套数组的遍历

二维数组常用来表示多条记录。先遍历外层,再按字段名读取内层元素。访问之前应确认数据结构符合预期,否则缺少字段时会出现提示。

<?php
$users = array(
    array('name' => 'Mia', 'age' => 24),
    array('name' => 'Noah', 'age' => 31)
);

foreach ($users as $user) {
    if (isset($user['name'])) {
        echo $user['name'], "\n";
    }
}
?>

若数组层级不固定,应逐层检查类型,不能假定每个元素都是数组。例如数据库查询或解码后的数据可能为空、结构不完整,处理前应验证数据来源和格式。count($array, COUNT_RECURSIVE) 只是递归统计元素数,并不能代替结构校验。

十二、数组与安全:不要把数组函数当作防护

数组本身不会自动让数据安全。用户提交的表单字段、URL 参数、Cookie 和接口响应都应视为不可信输入。先验证类型、允许的键、值的格式和范围,再用于业务逻辑。

<?php
$allowedRoles = array('reader', 'editor', 'admin');
$role = isset($_POST['role']) ? $_POST['role'] : '';

if (!is_string($role) || !in_array($role, $allowedRoles, true)) {
    // 拒绝无效角色
    $role = 'reader';
}
?>

这个检查只是确保角色值属于允许列表,不能单独构成完整的授权系统。是否允许当前用户执行某项操作,仍需结合登录身份、资源归属和业务规则判断。不要因为输入通过了 in_array(),就跳过权限校验。

把数组内容输出到 HTML 页面时,应按输出上下文进行转义。对于 HTML 文本节点,常用 htmlspecialchars();处理 UTF-8 页面时应明确字符集。转义要在输出时进行,不能把“放进数组”当作已转义,也不要对所有上下文套用同一种转义方式。

<?php
$value = isset($_GET['name']) ? $_GET['name'] : '';

echo htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
?>

数据库操作应使用数据库提供的参数化查询,而不是把数组元素拼进 SQL 字符串。命令行参数、文件路径、JavaScript 内容等也各有不同的校验与编码要求。数组提供的是数据组织方式,不是输入过滤或输出编码工具。

十三、常见错误速查

  • 把 isset() 当作键存在判断:值为 null 时它返回 false。需要区分键不存在与值为 null 时使用 array_key_exists()。
  • 用 empty() 判断字段是否提交:数字 0 和字符串 '0' 也被判为空。先判断是否存在,再按业务规则验证。
  • 用 if ($key) 判断搜索结果:搜索到键 0 会误判。应判断 $key !== false。
  • 删除元素后认为索引会重排:unset() 保留其他键。需要重新编号时显式使用 array_values()。
  • 在 foreach 中直接改值变量:默认不修改数组。用键回写,或谨慎使用引用并在循环后 unset() 引用变量。
  • 把所有合并函数当成一样:array_merge() 会重新编号数字键;+ 按键联合,左侧相同键优先。
  • 忽略类型比较:in_array() 默认非严格比较。需要类型一致时传入第三个参数 true。
  • 把 count 当作类型检查:先用 is_array() 确认数据类型,再统计元素数量。

结语

在 PHP5 中,数组操作的关键不只是记住函数名,还要明确三个问题:键是否需要保留、空值与零值如何区分、比较时是否允许类型转换。日常遍历优先使用 foreach;读取可选字段时按业务选择 isset() 或 array_key_exists();删除后是否重排、合并时如何处理数字键,也应在代码中明确表达。

处理外部数据时,先验证结构与类型,再做业务判断,并在输出时进行恰当转义。这样写出的数组代码更容易维护,也能避免把 PHP 的隐式类型转换、索引行为或安全边界误当成理所当然。

© 版权声明

相关文章