迴圈是處理重複邏輯的核心工具。PHP 提供 while、do-while、for、foreach 四種結構,各有優缺點與適用場景。本文深入分析執行機制、效能差異、常見錯誤與現代最佳實踐。
| 迴圈 | 判斷時機 | 適用場景 | 典型用法 |
|---|---|---|---|
while | 前測 | 未知次數、條件型、資料流讀取 | while ($row = $stmt->fetch()) |
do-while | 後測 | 至少執行一次、選單互動 | 選單輸入驗證 |
for | 前測 | 已知次數、計數、複雜初始/遞增 | for ($i=0; $i<$n; $i++) |
foreach | 內部迭代器 | 陣列/可迭代物件遍歷(首選) | foreach ($arr as $k=>$v) |
while 實戰範例$stmt = $pdo->query('SELECT id, name FROM users');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['name'] . PHP_EOL;
}
$fp = fopen('large.log', 'r');
while (($line = fgets($fp)) !== false) {
if (strpos($line, 'ERROR') !== false) {
error_log($line);
}
}
fclose($fp);
<table>
<?php
$i = 0;
while ($i < 100) {
if ($i % 10 === 0) {
if ($i > 0) echo '</tr>';
$bg = ($i / 10) % 2 === 0 ? '#fff' : '#ccc';
echo '<tr style="background:' . $bg . '">';
}
echo '<td>' . $i . '</td>';
$i++;
}
if ($i % 10 !== 0) echo '</tr>';
?>
</table>
for 進階用法for ($i = 0, $j = count($arr); $i < $j; $i++) {
// $j 預先計算長度,避免每次呼叫 count()
}
$str = 'PHP';
$rev = '';
for ($i = strlen($str) - 1; $i >= 0; $i--) {
$rev .= $str[$i];
}
// $rev = 'PHP' (回文)
for 遍歷關聯陣列// 錯誤:關聯陣列鍵非連續整數
$arr = ['a'=>1, 'b'=>2];
for ($i=0; $i
foreach 核心機制與陷阱$arr = [1, 2, 3];
foreach ($arr as $v) { $v *= 2; }
print_r($arr); // [1, 2, 3] 未改變
foreach ($arr as &$v) { $v *= 2; }
print_r($arr); // [2, 4, 6] 已修改
unset($v); // 必須解除參考,否則 $v 仍指向最後一個元素
$arr = [1, 2, 3, 4];
foreach ($arr as $k => $v) {
if ($v === 3) unset($arr[$k]); // 可能導致跳過元素或無限迴圈
}
// 建議:先收集要刪除的鍵,迴圈後再刪除
$toDelete = [];
foreach ($arr as $k => $v) { if ($v === 3) $toDelete[] = $k; }
foreach ($toDelete as $k) unset($arr[$k]);
$data = [
['id'=>1, 'name'=>'Alice', 'age'=>25],
['id'=>2, 'name'=>'Bob', 'age'=>30],
];
echo '<table><tr><th>ID</th><th>Name</th><th>Age</th></tr>';
foreach ($data as $row) {
echo '<tr>';
foreach ($row as $cell) { echo '<td>' . htmlspecialchars($cell) . '</td>'; }
echo '</tr>';
}
echo '</table>';
| 迴圈類型 | 耗時 | 記憶體 | 備註 |
|---|---|---|---|
for ($i=0; $i<$n; $i++) | 基準 | 極低 | 純計數最快 |
while ($i++ < $n) | +5~10% | 極低 | 語意不同,非直接可比 |
foreach (range(1,$n) as $v) | +30~50% | 高 | 需建立陣列 |
foreach ($arr as $v) | +10~20% | 低 | 遍歷既有陣列首選 |
結論:計數用 for,遍歷陣列/物件用 foreach,條件流用 while。效能差異在百萬級才明顯,優先考慮可讀性與正確性。
each/list/reset 陣列指標操作(舊式)PHP 7.2 棄用、8.0 移除 each(),新專案請勿使用。現代替代方案:
| 舊函數 | 現代替代 |
|---|---|
each($arr) | foreach ($arr as $k=>$v) |
list($k,$v) = each($arr) | foreach ($arr as $k=>$v) |
current($arr) | $arr[key($arr)] 或 reset($arr) 後 $arr[0] |
reset/end/next/prev | array_keys()、array_values()、array_shift()、array_pop() |
foreach:語意明確、支援參考修改、可迭代物件通用。for:for ($i=0; $i<$len; $i++),$len 提前計算。WHERE id IN (...))或非同步佇列。function gen() { for($i=0;$i<1e6;$i++) yield $i; } 逐個產出,記憶體常數級。array_map、array_filter、array_reduce 表達資料轉換管線,減少副作用。總結:理解四種迴圈的判斷時機、適用場景與 foreach 參考機制,配合 Generator 與函數式工具,即能應對從簡單計數到大規模資料串流的各種迴圈需求。