static 메서드나 속성은 어떤 객체가 아니라 클래스 자체에 속합니다. 먼저 객체를 만들지 않고, 정적 메서드는 ClassName::method()로 호출하고 정적 속성은 ClassName::$property로 읽습니다. 클래스 안에서는 self::가 같은 멤버를 가리킵니다.
어디에도 new Temperature()가 없습니다. 정적 멤버는 객체 상태가 필요 없는 헬퍼(변환, 팩토리, 검증)와 카운터처럼 클래스 전체에 속하는 데이터에 유용합니다.
정적 속성은 모든 객체가 공유한다
일반 속성은 객체마다 값이 하나입니다. 정적 속성은 클래스 전체에 값이 하나이므로 모든 객체가 같은 값을 봅니다. 여기서는 새 User가 만들어질 때마다 공유 카운터를 늘리고 다음 ID를 받습니다.
self::$nextId의 $에 주의하세요. 정적 속성은 :: 뒤에도 $를 유지하고, 상수(self::MAX)는 그렇지 않습니다. 정적 속성은 한 번의 PHP 요청 동안만 살아 있습니다. 일반적인 웹사이트(PHP-FPM이나 Apache)에서는 페이지를 불러올 때마다 처음부터 다시 시작하므로 방문자 사이에 데이터를 저장하는 곳이 아닙니다.
정적 메서드에는 $this가 없다
정적 메서드는 객체 없이 실행되므로 그 안에는 $this가 존재하지 않습니다. 정적 메서드는 self::로 다른 정적 메서드를 호출할 수 있지만 일반 속성은 읽을 수 없습니다. 일반 메서드를 정적으로 호출하는 것도 실패합니다.
반대 방향은 괜찮습니다. withSeparator()가 self::make()로 하듯이 일반 메서드는 정적 메서드를 호출할 수 있습니다.
self와 static: 늦은 정적 바인딩
self::는 코드가 작성된 클래스를 가리킵니다. static::은 실행 시점에 메서드가 호출된 클래스를 가리킵니다. 차이는 상속에서만 드러나며, 팩토리 메서드에서 가장 중요합니다.
Post::createWithSelf()는 Model을 반환하는데, 호출한 쪽이 원한 것은 거의 아닙니다. static은 호출에 이름이 쓰인 클래스이므로 Post로 연결됩니다. PHP는 이를 늦은 정적 바인딩이라고 부릅니다. 상속될 메서드에서는 static::과 new static()을 쓰고, 정말 이 클래스를 뜻할 때는 self::를 쓰세요. 상수에도 같은 규칙이 적용됩니다. static::TABLE은 자식 자신의 TABLE을 가져오고, self::TABLE은 항상 코드가 작성된 클래스의 것을 읽습니다.
상속된 정적 속성은 공유된다
자식 클래스는 자신의 것을 선언하지 않는 한 부모의 정적 속성을 공유합니다. 이것이 고전적인 static의 함정입니다. 두 하위 클래스가 숫자 하나에 함께 셉니다.
Apples에는 자신의 $count가 없으므로 Apples::$count는 Counter::$count이고, 둘 다 2를 출력합니다. Pears는 속성을 다시 선언해 따로 셉니다. 트레이트에 선언한 정적 속성은 다르게 동작합니다. 트레이트를 쓰는 각 클래스가 별도의 복사본을 받습니다.
함수 안의 정적 변수
함수 본문 안에서 static은 두 번째 의미를 가집니다. 정적 변수는 호출 사이에 값을 유지합니다. 초기값은 함수가 처음 실행될 때만 대입됩니다.
두 번째 slowSquare(12)는 계산을 건너뜁니다. 정적 속성처럼 이 값들도 각 요청이 끝나면 초기화됩니다. 변수 스코프 전반은 변수 페이지에서 다룹니다.
static이 잘못된 선택일 때
정적 메서드는 편리해서 남용하기 쉽습니다. 모든 메서드가 정적 속성을 읽고 쓰는 클래스는 클래스 이름이 붙은 전역 상태입니다. 모든 호출자가 그것을 공유하고, 테스트는 새 복사본을 얻을 수 없으며, 다른 구현으로 바꿔 끼울 수 없습니다.
static을 잘 쓰는 경우: 상태 없는 순수 헬퍼(Slug::make()), 이름 있는 생성자(Color::fromHex()), 클래스 전체의 상수. 메서드가 두 번 쓸 때 서로 다를 수 있는 데이터를 필요로 한다면 객체가 필요한 것입니다.
자주 묻는 질문
PHP에서 정적 메서드를 호출하려면 어떻게 하나요?
클래스 이름과 ::를 쓰세요: MathHelper::square(4). 같은 클래스 안에서는 self::square(4)나 static::square(4)를 씁니다. 객체가 필요 없고, 정적 메서드 안에는 $this가 없습니다.
PHP에서 self와 static의 차이는 무엇인가요?
self::는 항상 코드가 작성된 클래스를 뜻합니다. static::은 메서드가 실제로 호출된 클래스를 뜻하며 자식 클래스일 수 있습니다. 이를 늦은 정적 바인딩이라고 합니다. 그래서 부모의 create() 메서드에서 new self()는 항상 부모를 만들고, new static()은 호출한 자식을 만듭니다.
PHP의 정적 속성은 무엇인가요?
public static int $count = 0;으로 선언한 속성은 클래스에 속하므로, 그 클래스의 모든 객체(자식이 다시 선언하지 않는 한 자식 클래스 포함)가 공유하는 값이 하나 있습니다. ClassName::$count나 self::$count로 읽고 쓰세요. $에 주의하세요.
PHP에서 객체로 정적 메서드를 호출할 수 있나요?
네. $obj->square(4)와 $obj::square(4) 둘 다 객체 클래스의 정적 메서드를 호출하지만, 그 안에는 여전히 $this가 없습니다. 실패하는 것은 반대 방향인 정적이 아닌 메서드를 ClassName::method()로 호출하는 것이며, PHP 8부터 Error를 던집니다.
정적 메서드에서 $this를 쓸 수 있나요?
아니요. 정적 메서드는 객체 없이 실행되므로 그 안에는 $this가 정의되지 않고, 쓰면 Error("Using $this when not in object context")를 던집니다. 다른 정적 멤버에 닿으려면 self::나 static::을 쓰세요.