PHP / 반복문 / foreach
PHP의 foreach는 배열의 각 원소를 순서대로 꺼내 같은 작업을 반복하는 구문입니다. 값만 필요하면 as $value, 키도 필요하면 as $key => $value를 사용합니다. 직접 인덱스를 증가시키지 않아도 되므로 배열 목록을 처리할 때 편리합니다.
값을 하나씩 꺼내 계산하기
다음 코드를 foreach-basic.php로 저장하고 PHP가 설치된 터미널에서 php foreach-basic.php로 실행하세요. 뒤의 명령줄 예제도 별도 파일로 저장해 실행할 수 있으며 추가 라이브러리는 필요하지 않습니다.
<?php
$numbers = [1, 2, 3, 4];
foreach ($numbers as $value) {
$result = $value * 2 - 1;
echo $result, PHP_EOL;
}
출력은 한 줄씩 1, 3, 5, 7입니다. 매 반복에서 $value가 현재 원소의 값을 받고, $result는 그 값의 두 배에서 1을 뺀 결과를 받습니다. 마지막 문장까지 실행되면 다음 원소로 이동하고 모든 원소를 처리하면 반복문을 끝냅니다.
배열을 정렬하거나 키를 0부터 연속된 숫자로 바꾸는 구문은 아닙니다. 여기에서는 배열에 저장된 순서대로 값이 나옵니다. 배열뿐 아니라 객체의 순회에도 사용할 수 있지만, 이 글의 수정 예제는 배열을 대상으로 합니다. 기본 구문은 PHP foreach 문서를 참고하세요.
키와 값을 사용하고 조건에 따라 건너뛰기
상품 코드처럼 키에 의미가 있다면 키와 값을 함께 받습니다. 배열에 저장된 실제 키를 사용하므로, 숫자 키가 불연속이거나 문자열이어도 별도의 인덱스 계산이 필요하지 않습니다.
<?php
$stock = ['notebook' => 5, 'mug' => 0, 'lamp' => 2];
foreach ($stock as $name => $quantity) {
if ($quantity === 0) {
continue;
}
echo "$name: $quantity", PHP_EOL;
}
foreach ($stock as $name => $quantity) {
if ($quantity > 0) {
echo "first available: $name", PHP_EOL;
break;
}
}
첫 반복문은 notebook: 5와 lamp: 2를 출력합니다. 수량이 0인 mug에서는 continue가 현재 반복의 나머지 부분을 건너뛰고 다음 원소로 이동합니다. 배열에서 mug를 삭제한 것은 아닙니다.
두 번째 반복문은 first available: notebook만 출력합니다. break가 현재 반복문을 종료하기 때문입니다. 원하는 원소를 찾으면 탐색을 끝내거나, 특정 원소만 처리하지 않을 때 각각 사용할 수 있습니다. 중첩 반복문의 숫자 인수 등은 break 문서와 continue 문서에서 확인할 수 있습니다.
원본 배열 변경과 참조 해제
위 예제처럼 값을 받는 방식에서 $value에 새 숫자를 대입해도 그 대입만으로 원본 배열의 원소가 교체되지는 않습니다. 원본을 수정하려면 현재 키로 원소를 지정해 대입하는 방법이 명확합니다. 다음 코드는 각 가격에 100을 더합니다.
<?php
$prices = ['notebook' => 1000, 'mug' => 2000];
foreach ($prices as $name => $price) {
$prices[$name] = $price + 100;
}
echo json_encode($prices), PHP_EOL;
결과는 {"notebook":1100,"mug":2100}입니다. 같은 배열을 수정하는 것이므로 계산 전 값을 따로 보존해야 한다면 먼저 복사하거나 결과를 별도 배열에 저장하도록 구성하세요. 여기서 설명하는 것은 숫자 원소의 대입이며, 값이 객체인 경우의 내부 상태 변경과는 구분해야 합니다.
값 변수 앞에 &를 쓰면 배열 원소를 참조해 수정할 수도 있습니다. 참조는 변수 이름이 같은 값을 가리키도록 연결하는 방식입니다. 반복이 끝난 뒤에도 마지막 원소와의 연결이 남으므로, 참조 순회가 끝나자마자 unset()으로 연결을 해제해야 합니다.
<?php
$numbers = [2, 4, 6];
foreach ($numbers as &$value) {
$value += 1;
}
unset($value);
$value = 999;
echo json_encode($numbers), PHP_EOL;
이 코드는 [3,5,7]을 출력합니다. unset($value) 뒤에 $value를 999로 설정해도 마지막 원소는 7로 유지됩니다. unset()을 빼면 마지막 원소가 999로 바뀔 수 있습니다. 참조 해제 문서에서 설명하듯 unset은 여기에서 변수의 연결을 끊는 것이며 배열의 마지막 원소를 삭제하는 명령이 아닙니다.
HTML에 상품 목록 출력하기
HTML과 PHP를 섞어 쓸 때는 foreach 뒤에 콜론을 쓰고 endforeach로 닫는 대체 문법을 사용할 수 있습니다. 다음 전체 코드를 products.php로 저장하세요. 해당 폴더에서 php -S 127.0.0.1:8000 -t .를 실행한 뒤 http://127.0.0.1:8000/products.php에 접근하면 됩니다. 파일을 더블클릭하는 방식으로는 PHP가 실행되지 않습니다.
<?php
$products = [
['name' => '노트', 'price' => 1500],
['name' => '컵 & 받침', 'price' => 3000],
];
?>
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>foreach 상품 목록</title>
</head>
<body>
<h1>상품 목록</h1>
<?php if ($products === []): ?>
<p>등록된 상품이 없습니다.</p>
<?php else: ?>
<ul>
<?php foreach ($products as $product): ?>
<li><?= htmlspecialchars($product['name'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>:
<?= number_format($product['price']) ?>원</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</body>
</html>
브라우저에는 노트: 1,500원과 컵 & 받침: 3,000원이 목록으로 표시됩니다. 각 원소는 name과 price를 가진 배열이고, 반복문은 현재 상품 배열을 $product에 넣습니다. 상품명을 HTML 본문의 텍스트로 표시하므로 htmlspecialchars()로 특수문자를 변환합니다. 이 처리가 모든 URL·JavaScript 문맥의 검증까지 대신하는 것은 아닙니다.
$products를 빈 배열 []로 바꾸면 반복 결과 대신 등록된 상품이 없다는 문장이 표시됩니다. 확인 후 원래 배열로 되돌리세요. endforeach를 빠뜨리거나 중괄호 형식과 섞어 닫으면 문법 오류가 생길 수 있으므로 한 반복문 안에서는 시작과 끝의 형식을 맞춰야 합니다.
비어 있는 배열과 잘못된 자료형 구분
빈 배열은 오류 없이 본문을 한 번도 실행하지 않습니다. 변수 자체가 없거나 null·문자열·정수처럼 의도하지 않은 값이면 같은 상황으로 취급하면 안 됩니다. 먼저 데이터를 가져오는 단계에서 예상한 자료형과 각 원소의 필드가 맞는지 확인하세요. 배열만 받아야 하는 코드라면 is_array()로 검증하는 방식이 목적에 맞습니다.
배열과 Traversable 객체를 함께 허용하는 인터페이스에는 is_iterable() 검사를 검토할 수 있습니다. Traversable은 반복을 지원하는 객체의 인터페이스이며, iterable 자료형은 배열 또는 Traversable을 뜻합니다. 일반 객체의 공개 속성 순회와 이 자료형 검사는 완전히 같은 개념이 아니므로, 검사 하나로 모든 객체 구조까지 보장된다고 해석하지 마세요.




