PHP 인터페이스는 클래스가 제공하겠다고 약속하는 메서드를 코드 없이 선언합니다: interface Shape { public function area(): float; }. implements Shape라고 쓴 클래스는 area()를 정의해야 하고, Shape를 타입 힌트로 쓴 함수는 그 클래스를 받아들입니다.
describe()는 어떤 클래스를 받는지 알지도 신경 쓰지도 않습니다. 인터페이스가 하는 약속에만 기대므로, 새로운 Triangle implements Shape도 한 줄도 바꾸지 않고 동작합니다.
인터페이스에 맞춰 프로그래밍하기
인터페이스를 쓰는 흔한 이유는 코드가 특정 클래스가 아니라 능력에 의존하게 하려는 것입니다. 여기서 주문 서비스는 어떤 Notifier든 받으므로, 같은 코드가 운영 환경에서는 이메일을 보내고 테스트에서는 메시지를 모을 수 있습니다.
여러 인터페이스 구현하기
클래스는 부모를 최대 하나만 상속하지만 인터페이스는 필요한 만큼 구현할 수 있습니다. implements 뒤에 쉼표로 구분해 나열하고 모든 인터페이스의 메서드를 정의하세요.
JsonSerializable은 PHP의 내장 인터페이스 중 하나입니다. 이것을 구현하면 json_encode()가 객체에 대해 무엇을 만들지 제어할 수 있습니다.
인터페이스 상수
인터페이스는 상수를 선언할 수 있습니다. 구현하는 모든 클래스가 그것을 물려받으며, 인터페이스 이름이나 클래스 이름으로 읽을 수 있습니다.
PHP 8.3에서 인터페이스는 속성을 가질 수 없고 메서드와 상수만 가질 수 있습니다. 메서드는 정적일 수 있습니다. 인터페이스에 public static function fromArray(array $data): static;을 쓰면 각 클래스가 그 정적 메서드를 정의해야 합니다. 관련된 값의 고정된 집합이라면 인터페이스 상수 목록보다 enum이 더 잘 맞는 경우가 많습니다.
인터페이스는 다른 인터페이스를 확장할 수 있다
인터페이스도 extends를 쓰며, 클래스와 달리 여러 개를 한 번에 확장할 수 있습니다. 자식 인터페이스를 구현하는 클래스는 체인 전체의 메서드를 정의해야 합니다.
내장 인터페이스: Countable, IteratorAggregate, Stringable
PHP에는 객체를 언어에 연결하는 인터페이스가 들어 있습니다. Countable은 count($obj)를, IteratorAggregate는 foreach를 동작하게 하고, Stringable은 __toString() 메서드가 있는 모든 클래스가 자동으로 구현합니다.
메서드 빠뜨리기
클래스가 인터페이스를 구현한다고 하면서 메서드를 빠뜨리면 PHP는 빠진 메서드를 알려 주는 치명적 에러와 함께 멈춥니다.
total() 메서드를 추가하면 스크립트가 실행됩니다. 추상 클래스는 인터페이스 메서드를 정의하지 않은 채로 둘 수 있으며, 에러 메시지가 제안하는 우회 방법이 이것입니다.
인터페이스와 추상 클래스
둘 다 클래스가 가져야 할 메서드를 정의하고, 둘 다 타입 힌트로 쓸 수 있습니다. 차이는 그 밖에 무엇을 담을 수 있는지와 클래스가 몇 개를 쓸 수 있는지입니다.
| 인터페이스 | 추상 클래스 | |
|---|---|---|
| 메서드 본문 | 없음(시그니처만) | 추상 메서드와 완성된 메서드 |
| 속성 | 없음 | 있음 |
| 생성자 | 선언할 수 있지만 거의 쓸모없음 | 있음, 코드 포함 |
| 메서드 접근 제어 | public만 | public, protected, private |
| 상수 | 있음 | 있음 |
| 클래스당 개수 | 개수 제한 없음(implements A, B, C) | 하나(extends Base) |
| 일반적인 용도 | 능력: Exportable, Countable | 가족의 공통 코드: Model, Controller |
둘은 함께 잘 쓰입니다. 타입 힌트용으로 인터페이스를 정의하고, 공통 코드로 그것을 구현하는 추상 기반 클래스를 제공하세요. 다른 기반 클래스가 필요한 클래스는 여전히 인터페이스를 직접 구현할 수 있습니다.
자주 묻는 질문
PHP의 인터페이스는 무엇인가요?
본문이 없는 public 메서드 시그니처의 목록입니다: interface Logger { public function log(string $msg): void; }. implements Logger라고 쓴 클래스는 그 안의 모든 메서드를 정의해야 하고, Logger를 타입 힌트로 쓴 코드는 그 클래스를 받아들입니다.
PHP 클래스가 여러 인터페이스를 구현할 수 있나요?
네. implements 뒤에 쉼표로 구분해 나열하세요: class File implements Readable, Writable, Countable. 클래스는 모든 인터페이스의 메서드를 정의해야 합니다. PHP는 이렇게 다른 언어의 다중 상속이 하는 일을 처리합니다.
PHP에서 인터페이스와 추상 클래스의 차이는 무엇인가요?
인터페이스는 public 메서드(와 상수)만 선언하며 속성도 메서드 코드도 없고, 클래스는 여러 개를 구현할 수 있습니다. 추상 클래스는 추상 메서드 옆에 속성, 생성자, 완성된 메서드를 담을 수 있지만, 클래스는 하나만 상속할 수 있습니다. 능력("내보낼 수 있다")에는 인터페이스를, 클래스 가족의 공통 코드에는 추상 클래스를 쓰세요.
PHP 인터페이스에 속성이 있을 수 있나요?
PHP 8.3에서는 없습니다. 인터페이스에는 메서드 시그니처와 상수(const VERSION = 2;)는 들어갈 수 있지만 속성은 들어갈 수 없습니다. 인터페이스의 속성 선언(property hooks)은 PHP 8.4에서야 도입되었습니다.
PHP에서 객체가 인터페이스를 구현하는지 확인하려면 어떻게 하나요?
instanceof를 쓰세요. $obj instanceof Countable은 객체의 클래스가 직접 또는 부모 클래스를 통해 인터페이스를 구현하면 참입니다. class_implements($obj)는 클래스가 구현하는 모든 인터페이스의 이름을 반환합니다.