PHP foreach 迴圈

foreach 是專門用來遍歷陣列和物件的迴圈。它會自動處理索引和元素,是遍歷陣列最簡單、最常用的方式。

基本語法

只取值

foreach ($array as $value) {
    // 使用 $value
}
<?php
$fruits = ['蘋果', '香蕉', '橘子'];

foreach ($fruits as $fruit) {
    echo "$fruit\n";
}
?>

輸出:

蘋果
香蕉
橘子

取鍵和值

foreach ($array as $key => $value) {
    // 使用 $key 和 $value
}
<?php
$fruits = ['蘋果', '香蕉', '橘子'];

foreach ($fruits as $index => $fruit) {
    echo "$index: $fruit\n";
}
?>

輸出:

0: 蘋果
1: 香蕉
2: 橘子

遍歷關聯陣列

<?php
$person = [
    'name' => 'Alice',
    'age' => 25,
    'city' => 'Taipei'
];

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

輸出:

name: Alice
age: 25
city: Taipei

傳址修改元素

使用 & 可以直接修改陣列中的元素:

<?php
$numbers = [1, 2, 3, 4, 5];

// 將每個元素乘以 2
foreach ($numbers as &$num) {
    $num *= 2;
}
unset($num);  // 重要:解除參考

print_r($numbers);
// [2, 4, 6, 8, 10]
?>

使用傳址迴圈後,務必使用 unset() 解除參考,否則可能造成意外的結果:

<?php
$arr = [1, 2, 3];

foreach ($arr as &$val) { // ... } // 忘記 unset($val)

foreach ($arr as $val) { // $val 仍然是參考,會修改陣列最後一個元素 }

print_r($arr); // [1, 2, 2] - 意外! ?>

遍歷多維陣列

<?php
$users = [
    ['name' => 'Alice', 'age' => 25],
    ['name' => 'Bob', 'age' => 30],
    ['name' => 'Charlie', 'age' => 35],
];

foreach ($users as $user) {
    echo "{$user['name']} 是 {$user['age']} 歲\n";
}
?>

輸出:

Alice 是 25 歲
Bob 是 30 歲
Charlie 是 35 歲

巢狀 foreach

<?php
$matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
];

foreach ($matrix as $rowIndex => $row) {
    foreach ($row as $colIndex => $value) {
        echo "[$rowIndex][$colIndex] = $value  ";
    }
    echo "\n";
}
?>

解構賦值 (PHP 7.1+)

在 foreach 中使用解構賦值可以直接取出陣列元素:

<?php
$users = [
    ['Alice', 25, 'Taipei'],
    ['Bob', 30, 'Taichung'],
    ['Charlie', 35, 'Kaohsiung'],
];

foreach ($users as [$name, $age, $city]) {
    echo "$name, $age 歲, 住在 $city\n";
}
?>

關聯陣列的解構:

<?php
$users = [
    ['name' => 'Alice', 'age' => 25],
    ['name' => 'Bob', 'age' => 30],
];

foreach ($users as ['name' => $name, 'age' => $age]) {
    echo "$name 是 $age 歲\n";
}
?>

break 和 continue

break - 跳出迴圈

<?php
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

foreach ($numbers as $num) {
    if ($num > 5) {
        echo "找到大於 5 的數字,停止迴圈\n";
        break;
    }
    echo "$num ";
}
// 輸出:1 2 3 4 5 找到大於 5 的數字,停止迴圈
?>

continue - 跳過當前迭代

<?php
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

foreach ($numbers as $num) {
    // 跳過偶數
    if ($num % 2 === 0) {
        continue;
    }
    echo "$num ";
}
// 輸出:1 3 5 7 9
?>

遍歷物件

foreach 可以遍歷物件的公開屬性:

<?php
class Person {
    public string $name = 'Alice';
    public int $age = 25;
    private string $secret = '秘密';
}

$person = new Person();

foreach ($person as $key => $value) {
    echo "$key: $value\n";
}
// 只會顯示 public 屬性
// name: Alice
// age: 25
?>

實作 Iterator 介面

自訂類別可以實作 Iterator 介面來支援 foreach

<?php
class NumberRange implements Iterator {
    private int $start;
    private int $end;
    private int $current;
    
    public function __construct(int $start, int $end) {
        $this->start = $start;
        $this->end = $end;
    }
    
    public function rewind(): void {
        $this->current = $this->start;
    }
    
    public function current(): int {
        return $this->current;
    }
    
    public function key(): int {
        return $this->current - $this->start;
    }
    
    public function next(): void {
        $this->current++;
    }
    
    public function valid(): bool {
        return $this->current <= $this->end;
    }
}

$range = new NumberRange(1, 5);

foreach ($range as $index => $value) {
    echo "[$index] = $value\n";
}
?>

替代語法

在 HTML 模板中使用替代語法:

<?php $items = ['蘋果', '香蕉', '橘子']; ?>

<ul>
<?php foreach ($items as $item): ?>
    <li><?= htmlspecialchars($item) ?></li>
<?php endforeach; ?>
</ul>

實際應用範例

生成 HTML 選單

<?php
$menuItems = [
    'home' => '首頁',
    'about' => '關於我們',
    'products' => '產品',
    'contact' => '聯絡我們',
];

$currentPage = 'about';
?>

<nav>
    <ul>
    <?php foreach ($menuItems as $url => $title): ?>
        <li<?= ($url === $currentPage) ? ' class="active"' : '' ?>>
            <a href="<?= $url ?>.php"><?= $title ?></a>
        </li>
    <?php endforeach; ?>
    </ul>
</nav>

處理表單輸入

<?php
$_POST = [
    'name' => 'Alice',
    'email' => 'alice@example.com',
    'message' => 'Hello!',
];

$allowedFields = ['name', 'email', 'message'];
$data = [];

foreach ($allowedFields as $field) {
    if (isset($_POST[$field])) {
        $data[$field] = trim($_POST[$field]);
    }
}

print_r($data);
?>

計算總和和平均

<?php
$scores = [85, 92, 78, 96, 88];

$total = 0;
$count = 0;

foreach ($scores as $score) {
    $total += $score;
    $count++;
}

$average = $total / $count;

echo "總分:$total\n";     // 439
echo "平均:$average\n";   // 87.8
?>

過濾陣列

<?php
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

$evens = [];
foreach ($numbers as $num) {
    if ($num % 2 === 0) {
        $evens[] = $num;
    }
}

print_r($evens);  // [2, 4, 6, 8, 10]

// 使用 array_filter 更簡潔
$evens = array_filter($numbers, fn($n) => $n % 2 === 0);
?>

轉換陣列

<?php
$users = [
    ['id' => 1, 'name' => 'Alice'],
    ['id' => 2, 'name' => 'Bob'],
    ['id' => 3, 'name' => 'Charlie'],
];

// 建立 id => name 的對應
$userMap = [];
foreach ($users as $user) {
    $userMap[$user['id']] = $user['name'];
}

print_r($userMap);
// [1 => 'Alice', 2 => 'Bob', 3 => 'Charlie']

// 使用 array_column 更簡潔
$userMap = array_column($users, 'name', 'id');
?>

合併重複項目

<?php
$orders = [
    ['product' => 'Apple', 'quantity' => 2],
    ['product' => 'Banana', 'quantity' => 3],
    ['product' => 'Apple', 'quantity' => 1],
    ['product' => 'Orange', 'quantity' => 4],
];

$summary = [];

foreach ($orders as $order) {
    $product = $order['product'];
    
    if (!isset($summary[$product])) {
        $summary[$product] = 0;
    }
    
    $summary[$product] += $order['quantity'];
}

print_r($summary);
// ['Apple' => 3, 'Banana' => 3, 'Orange' => 4]
?>

生成樹狀結構

<?php
$categories = [
    ['id' => 1, 'name' => '電子產品', 'parent_id' => null],
    ['id' => 2, 'name' => '手機', 'parent_id' => 1],
    ['id' => 3, 'name' => '筆電', 'parent_id' => 1],
    ['id' => 4, 'name' => '服飾', 'parent_id' => null],
    ['id' => 5, 'name' => '上衣', 'parent_id' => 4],
];

function buildTree(array $items, $parentId = null): array {
    $tree = [];
    
    foreach ($items as $item) {
        if ($item['parent_id'] === $parentId) {
            $children = buildTree($items, $item['id']);
            if ($children) {
                $item['children'] = $children;
            }
            $tree[] = $item;
        }
    }
    
    return $tree;
}

$tree = buildTree($categories);
print_r($tree);
?>