IT
PHP 인스턴스 변수
1) 인스턴스 변수란?
-
클래스의 각 객체가 개별적으로 보유하는 상태입니다.
-
문법적으로는 프로퍼티(property) 라고 부르며, static이 아닌 프로퍼티가 인스턴스 변수입니다.
-
반대로 static 프로퍼티는 클래스 전체가 공유합니다(인스턴스 변수 아님).
class Counter {
// 인스턴스 변수(객체마다 따로 있음)
private int $value = 0;
public function inc(int $step = 1): void { $this->value += $step; }
public function value(): int { return $this->value; }
}
$a = new Counter();
$b = new Counter();
$a->inc(2);
echo $a->value(); // 2
echo $b->value(); // 0 ← 서로 다른 상태
2) 선언 방법과 가시성(접근 제어자)
class User {
public int $id; // 어디서나 접근 가능(권장 X)
protected string $role; // 자기 자신 + 자식 클래스에서 접근
private string $name; // 해당 클래스 내부에서만 접근
public function __construct(int $id, string $name, string $role = 'user') {
$this->id = $id;
$this->name = $name;
$this->role = $role;
}
public function name(): string { return $this->name; } // 게터
}
-
기본 권장: 프로퍼티는 private, 읽기/변경은 메소드로 캡슐화.
-
외부 직접 접근이 꼭 필요할 때만 public을 신중히 사용.
3) 타입 선언(필수 습관)
PHP 7.4+부터 타입드 프로퍼티를 지원합니다.
class Product {
public int $id;
public string $name;
public ?string $description = null; // nullable
public int|float $price; // union 타입(8.0+)
public function __construct(int $id, string $name, int|float $price) {
$this->id = $id;
$this->name = $name;
$this->price = $price;
}
}
-
타입이 있는 프로퍼티는 초기화 전에 접근하면 오류가 납니다. 생성자에서 반드시 채우세요.
-
프로퍼티 기본값은 상수 표현식만 가능(객체 생성 등은 안 됨) → 생성자에서 처리.
4) 초기화 패턴
4.1 일반적인 방식
class Point {
private int $x;
private int $y;
public function __construct(int $x, int $y) {
$this->x = $x;
$this->y = $y;
}
}
4.2 생성자 프로퍼티 승격(PHP 8.0+)
생성자 매개변수에 접근 제어자와 타입을 붙여 선언+초기화를 한 줄로 처리합니다.
class Point {
public function __construct(
private int $x,
private int $y,
) {}
public function x(): int { return $this->x; }
public function y(): int { return $this->y; }
}
4.3 지연 초기화/검증
class Session {
private ?string $token = null;
public function token(): string {
if ($this->token === null) {
$this->token = bin2hex(random_bytes(16));
}
return $this->token;
}
}
5) 읽기 전용(Readonly) 인스턴스 변수(PHP 8.1+)
생성 이후 수정 불가한 불변 상태가 필요할 때 사용합니다.
final class Money {
public function __construct(
public readonly int $amount,
public readonly string $currency
) {}
}
$m = new Money(1000, 'KRW');
// $m->amount = 2000; // 오류: readonly는 재할당 불가
6) 접근/수정 방법
-
클래스 내부: $this->prop
-
외부: $obj->prop (단, public이어야 함)
-
보통은 메소드로 캡슐화합니다.
class Profile {
private string $nickname;
public function __construct(string $nickname) {
$this->nickname = $nickname;
}
public function rename(string $new): void {
if ($new === '') throw new InvalidArgumentException('empty');
$this->nickname = $new;
}
public function nickname(): string { return $this->nickname; }
}
$p = new Profile('kim');
$p->rename('lee');
echo $p->nickname(); // lee
7) nullsafe 연산자와 체이닝(PHP 8.0+)
객체가 null일 수 있을 때 안전하게 프로퍼티/메소드에 접근합니다.
$city = $user?->address?->city; // 중간이 null이면 전체가 null
8) 동적 프로퍼티(지양)
-
예전엔 선언하지 않은 프로퍼티를 런타임에 추가해도 됐지만, PHP 8.2+에서 폐지(Deprecated) 경고가 발생합니다.
-
꼭 필요하면 #[\AllowDynamicProperties] 또는 stdClass 활용, 권장 대안은 명시적 선언 혹은 __get/__set 매직 메소드.
class Box {
private array $data = [];
public function __get(string $name) { return $this->data[$name] ?? null; }
public function __set(string $name, $value) { $this->data[$name] = $value; }
}
9) static 프로퍼티와의 차이
class Example {
public static int $count = 0; // 모든 인스턴스가 공유
public function __construct(private int $id) {
self::$count++;
}
}
$a = new Example(1);
$b = new Example(2);
echo Example::$count; // 2
-
인스턴스 변수: 각 객체가 독립 보유
-
static 변수: 클래스 단위로 공유
-
상태를 공유해야 하는 특수한 경우가 아니라면 static 남용 금지.
10) 상속과 인스턴스 변수
-
부모의 protected 프로퍼티는 자식에서 사용 가능.
-
같은 이름의 프로퍼티를 재선언하는 일은 피하세요(혼동과 호환성 이슈 유발).
-
공통 상태는 부모에 protected로 두고, 동작은 자식에서 확장하는 구조가 안전합니다.
11) 컬렉션/객체 타입, 제네릭 표기 관례
PHP 언어 차원의 제네릭은 없지만, DocBlock으로 의도를 명시합니다.
/** @var list<User> */
private array $members = [];
/** @param list<User> $members */
public function __construct(array $members = []) { $this->members = $members; }
정적 분석기(phpstan/psalm)와 IDE가 이 정보를 활용합니다.
12) 실무 예제: 불변 값 + 가변 엔티티
final class Email {
public function __construct(public readonly string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('invalid email');
}
}
}
final class Account {
public function __construct(
private int $id,
private Email $email,
private DateTimeImmutable $createdAt = new DateTimeImmutable()
) {}
public function changeEmail(Email $email): void {
$this->email = $email; // 도메인 규칙 검증은 Email에서 담당
}
public function id(): int { return $this->id; }
public function email(): Email { return $this->email; }
public function createdAt(): DateTimeImmutable { return $this->createdAt; }
}
포인트
-
**값 객체(Email)**는 readonly로 불변.
-
**엔티티(Account)**는 도메인 규칙을 따르며 인스턴스 변수를 통해 상태 변화를 관리.
13) 흔한 실수와 체크리스트
-
타입드 프로퍼티를 초기화 전에 접근 → 오류. 생성자/팩토리에서 반드시 채우기.
-
선언 없이 $obj->foo = ... 추가(동적 프로퍼티) → 8.2+에서 경고. 반드시 선언.
-
외부에서 public 프로퍼티를 직접 바꿈 → 불변식 깨짐. 메소드로 검증/수정.
-
프로퍼티 기본값에 new나 함수 호출 사용 → 불가. 생성자에서 처리.
-
공유 상태가 필요한 게 아닌데 static 사용 → 테스트/동시성 문제 유발.
14) 요약
-
인스턴스 변수는 객체마다 독립된 상태를 담는 프로퍼티입니다.
-
private + 타입 선언 + 생성자 초기화가 기본기.
-
변경을 통제하고 싶다면 게터/세터 대신 의미 있는 도메인 메소드를 제공하세요.
-
불변이 적합하면 readonly를, 동적 필드가 필요하면 명시적 선언 또는 매직 메소드로.
댓글
아직 댓글이 없습니다.
로그인 후 댓글을 남길 수 있습니다.