PHP enum(PHP 8.1+)은 가능한 값, 즉 case의 집합이 고정된 타입을 정의합니다: enum Status { case Active; case Banned; }. case는 Status::Active처럼 쓰며, Status 타입의 매개변수는 다른 것을 받지 않습니다. backed enum인 enum Status: string은 각 case에 저장할 수 있는 값도 주며, Status::from()으로 다시 변환할 수 있습니다.
enum 이전에는 같은 것을 문자열 상수 목록으로 만들었고, 'actve' 같은 오타가 데이터베이스에 들어가는 것을 막을 방법이 없었습니다. 매개변수 타입이 Status이면 세 case만 넘길 수 있습니다.
순수 enum과 backed enum
순수 enum의 case는 이름뿐입니다. backed enum은 이름 뒤에 int나 string 타입을 선언하며, 그러면 모든 case가 그 타입의 고유한 값을 가져야 합니다. 모든 case에는 ->name이 있고, backed case에만 ->value가 있습니다.
값이 프로그램 밖으로 나간다면 backed enum을 고르세요. 데이터베이스 컬럼, URL, 폼 필드, JSON 등입니다. 파서의 상태처럼 코드 안에만 존재하는 값이라면 순수 enum을 고르세요.
from()과 tryFrom()으로 값 변환하기
backed enum에는 저장된 값을 다시 case로 바꾸는 정적 메서드 두 개가 생깁니다. from()은 알 수 없는 값에 ValueError를 던지고, tryFrom()은 null을 반환하므로 기본값을 위한 ??와 잘 어울립니다.
사용자가 보낸 것에는 tryFrom()을, 알 수 없는 값이 알고 싶은 버그를 뜻하는 직접 쓴 값에는 from()을 쓰세요. 인수는 일반적인 매개변수 타입 규칙을 따릅니다. int enum에서 기본 모드의 Priority::from('5')는 '5'가 먼저 5로 변환되므로 Priority::Normal을 반환하고, Priority::from('5x')는 TypeError이며, declare(strict_types=1) 아래에서는 어떤 문자열이든 TypeError입니다. tryFrom()도 마찬가지라서 Priority::tryFrom('abc')는 null을 반환하는 대신 예외를 던집니다. 그러니 int enum용 폼 입력은 먼저 filter_var($raw, FILTER_VALIDATE_INT)로 확인하세요.
cases()로 모든 case 나열하기
Enum::cases()는 선언된 순서대로 모든 case의 배열을 반환합니다. 드롭다운을 만들거나, 목록을 검증하거나, array_column으로 모든 값을 얻는 방법입니다.
실행하고 요금제를 고른 뒤 Choose를 누르세요. $_POST['plan']이 채워진 채로 같은 스크립트가 다시 실행되고, tryFrom()이 제출된 문자열을 Plan case로 바꿉니다. plan=gold 같은 위조된 값은 null이 되고 아무 옵션도 선택되지 않습니다.
enum 메서드, 상수, 인터페이스
enum은 메서드, 정적 메서드, 상수를 가질 수 있고 인터페이스를 구현할 수 있습니다. 메서드 안에서 $this는 현재 case이므로 각 case에 데이터를 붙이는 자연스러운 방법은 match ($this)입니다. enum이 가질 수 없는 것은 속성입니다. case별 데이터는 메서드에서 나옵니다.
match 속의 enum
match는 ===로 비교하고, enum case는 자기 자신과만 동일하므로 match와 enum은 잘 맞습니다. case를 빠뜨렸는데 그 case가 match에 도달하면 PHP는 조용히 아무것도 반환하지 않는 대신 UnhandledMatchError를 던집니다.
match가 switch와 어떻게 다른지는 match를 참고하세요.
JSON과 문자열로서의 enum
json_encode()는 backed case를 그 값으로 씁니다. 순수 enum은 인코딩할 수 없고, enum은 문자열이 아니라 객체이므로 어떤 case든 echo하면 실패합니다. 대신 ->value나 ->name을 출력하세요.
echo Role::Admin;은 Error("Object of class Role could not be converted to string")를 던지며, enum은 __toString()을 구현할 수 없습니다. 돌아올 때 JSON은 평범한 문자열을 주므로 위처럼 from()이나 tryFrom()으로 변환하세요. 인코딩에 대한 자세한 내용은 json_encode에 있습니다.
흔한 enum 실수
세 가지 에러가 계속 나옵니다. case는 그 값과 절대 같지 않고, case는 배열 키가 될 수 없으며, enum에는 new가 동작하지 않습니다.
case 자체를 키로 쓰는 맵이 필요하다면 객체를 키로 받는 SplObjectStorage나 WeakMap을 쓰세요.
자주 묻는 질문
PHP에서 enum의 값을 얻으려면 어떻게 하나요?
backed enum에서는 ->value를 읽으세요: Status::Active->value는 'active'입니다. 모든 case에는 case 이름을 문자열로 담은 ->name('Active')도 있습니다. 순수 enum(: string이나 : int가 없는 것)에는 ->name만 있습니다.
PHP enum에서 from()과 tryFrom()의 차이는 무엇인가요?
둘 다 값을 backed enum의 case로 바꿉니다. Status::from('active')는 case를 반환하고, 그 값을 가진 case가 없으면 ValueError를 던집니다. Status::tryFrom('nope')는 대신 null을 반환합니다. 사용자 입력에는 tryFrom()을, 잘못된 값이 버그일 때는 from()을 쓰세요.
PHP에서 enum의 모든 값을 얻으려면 어떻게 하나요?
Status::cases()는 선언 순서대로 모든 case를 반환합니다. backed enum의 값은 array_column(Status::cases(), 'value')로, 이름은 array_column(Status::cases(), 'name')으로 얻으세요.
순수 enum과 backed enum의 차이는 무엇인가요?
순수 enum(enum Suit { case Hearts; })의 case는 이름뿐입니다. backed enum(enum Suit: string { case Hearts = 'H'; })은 각 case에 고유한 int나 string 값을 주며, 데이터베이스에 저장하거나, 폼이나 JSON에 넣거나, from()이나 tryFrom()으로 다시 변환하려면 이것이 필요합니다.
PHP enum에 메서드가 있을 수 있나요?
네. enum은 메서드, 정적 메서드, 상수를 가질 수 있고 인터페이스를 구현할 수 있습니다. 메서드 안에서 $this는 현재 case이므로 return match ($this) { self::Active => 'green', self::Banned => 'red' };가 흔한 패턴입니다. enum은 속성을 가질 수 없습니다.