| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- 객체지향
- MySQL
- 디자인패턴
- 소프트웨어설계
- Tool Calling
- MCP
- mcp client
- 데코레이터패턴
- docker network
- 인덱스
- LLM
- Agent Loop
- Laravel
- dhgrp
- mcp server
- SQL
- SQL문법
- 매직메서드
- ai 에이전트
- PHP
- docker
- 라라벨
- PHP객체지향
- 어댑터패턴
- 객체복제
- SQL기초
- linux 권한
- OOP
- 데이터베이스
- ai agent
- Today
- Total
개발블로그
[객체지향] PHP 매직 메서드 본문
개념
개념
특정 상황이 발생했을 때 PHP가 자동으로 호출하는 특수 메서드
ex) 존재하지 않거나 접근할 수 없는 속성을 읽으려고 하면 => __get()이 호출될 수 있음
이름이 __로 시작함
사용목적
객체의 기본 동작을 원하는 방식으로 제어할 때 사용
종류
| 종류 | 메서드 | 호출 시점 |
| 생성 | __construct() | new로 객체 생성 시 |
| 소멸 | __destruct() | 객체 소멸 시 |
| 속성 읽기 | __get() | 존재하지 않거나 접근할 수 없는 속성 읽기 |
| 속성 쓰기 | __set() | 존재하지 않거나 접근할 수 없는 속성에 대입 |
| 객체 메서드 호출 | __call() | 존재하지 않거나 접근할 수 없는 객체 메서드 호출 |
| 정적 메서드 호출 | __callStatic() | 존재하지 않거나 접근할 수 없는 정적 메서드 호출 |
| 문자열 변환 | __toString() | 객체를 문자열로 사용 |
| 함수형 호출 | __invoke() | 객체를 함수처럼 호출 |
| 복제 | __clone() | clone으로 복제한 후 복제본에서 실행 |
생성과 소멸
생성자 __construnct()
객체를 생성할 때 자동으로 실행되는 메서드
=> 객체가 처음부터 필요한 데이터를 가진 상태로 사용할 수 있도록 초기 상태를 설정함
| 용도 | 설명 |
| 초기값 설정 | 객체가 사용할 속성값 설정 |
| 필수 데이터 전달 | 객체 생성에 필요한 정보 전달 |
| 초기 데이터 검증 | 전달받은 값이 올바른지 확인 |
[ 문법 ]
class ClassName
{
public function __construct($value)
{
// 객체 생성 시 수행할 초기화
}
}
$object = new ClassName($value);
// new ClassName($value)에 전달한 값은 __construct($value)의 매개변수로 전달된다.
[ 예시 ]
class UserProfile
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
echo "프로필 생성: {$this->name}\n";
}
public function getName(): string
{
return $this->name;
}
}
$user = new UserProfile("Kim");
echo $user->getName();
/* [결과]
프로필 생성: Kim
Kim
*/
소멸자 __destruct()
객체가 소멸할 때 자동으로 실행되는 메서드
=> 객체가 사용하던 자원을 정리하는 등의 마무리 작업에 사용.
[ 문법 ]
public function __destruct()
{
// 객체 소멸 시 수행할 처리
}
[ 관련 메서드 : unset($변수) ]
변수를 해제하는 동작.
※ 다른 변수가 같은 객체를 참조하고 있다면 바로 소멸하지 않음.
[ 예시 ]
public function __destruct()
{
echo "프로필 소멸: {$this->name}\n";
}
$user = new UserProfile("Kim");
echo "프로필 사용 중\n";
unset($user);
/*
프로필 생성: Kim
프로필 사용 중
프로필 소멸: Kim
*/
속성 접근
속성 읽기 __get()
존재하지 않거나 현재 위치에서 접근할 수 없는 속성을 읽을 때 자동으로 실행되는 메서드
- 읽으려는 속성의 이름을 전달받고, 어떤 값을 반환할지 직접 정의함
[ 문법 ]
public function __get(string $name): mixed
{
// 속성을 읽을 때 반환할 값
}
// $name = 읽으려는 속성 이름
// $mixed = 여러 타입의 값을 반환할 수 있다는 의미
[ 예시 ]
class UserProfile
{
private array $data = [
"name" => "Kim"
];
public function __get(string $name): mixed
{
return $this->data[$name] ?? null;
// 해당 키가 없거나 값이 null이면 null을 반환함
}
}
$user = new UserProfile();
echo $user->name; // Kim
// UserProfile에는 $data 속성만 있고, $name 속성은 선언되어 있지 않음.
// 따라서 $user->name을 읽으면 __get("name")을 호출함
// $this->data["name"] 을 조회
// "Kim" 반환
속성 쓰기 __set()
존재하지 않거나 현재 위치에서 접근할 수 없는 속성에 값을 대입할 때 자동으로 실행되는 메서드
- 속성 이름과 대입할 값을 전달받고, 그 값을 어떻게 처리할지 정의 함
[ 문법 ]
public function __set(string $name, mixed $value): void
// $name = 대입하려는 속성 이름
// $value = 대입할 값
// void = 반환값 없음
{
// 전달받은 값을 저장하거나 검증
}
[ 예시 ]
class UserProfile
{
private array $data = [
"name" => "Kim"
];
public function __get(string $name): mixed
{
return $this->data[$name] ?? null;
}
// 이와 같은 메서드를 추가하면 => 외부에서 값을 대입하고 다시 읽을 수 있음
public function __set(string $name, mixed $value): void
{
$this->data[$name] = $value;
}
}
$user = new UserProfile();
$user->name = "Lee"; // __set("name", "Lee")
echo $user->name; // __get("name") → Lee
/*
$user->name = "Lee"
→ __set("name", "Lee")
→ 내부 배열의 "name" 값을 "Lee"로 변경
echo $user->name
→ __get("name")
→ 변경된 값 "Lee" 반환
*/
메서드 호출
객체 메서드 호출 __call()
존재하지 않거나 현재 위치에서 접근할 수 없는 메서드를 호출할 때 자동으로 실행되는 메서드
[ 문법 ]
class ClassName
{
public function __call(string $name, array $arguments): mixed
{
// $name과 $arguments를 사용해 호출 처리
}
}
// $name = 호출하려던 메서드 이름
// $arguments = 전달할 인수를 담은 배열, 인수가 없으면 []
// mixed = 처리 결과로 여러 타입의 값을 반환할 수 있음
$object = new ClassName();
$object->methodName($argument1, $argument2);
/*
$object->methodName($argument1, $argument2)
→ 접근 가능한 methodName()이 없는 경우
→ __call("methodName", [$argument1, $argument2])
*/
[ 예시 ]
class UserProfile
{
private array $data = ["name" => "Kim"];
public function __call(string $name, array $arguments): mixed
{
if ($name === "getName" && $arguments === []) {
return $this->data["name"];
}
throw new BadMethodCallException("지원하지 않는 호출: $name");
}
}
$user = new UserProfile();
echo $user->getName(); // Kim
// getName()은 정의되어 있지 않음 => PHP가 대신 __call()을 호출함
// __call("getName", [])
// 작성해 둔 조건에 따라 "Kim" 반환
정적 메서드 호출 __callStatic()
존재하지 않거나 현재 위치에서 접근할 수 없는 정적 메서드를 호출할 때 자동으로 실행되는 메서드
- 클래스명::메서드() 형태의 호출을 처리하며, 메서드 선언에 static을 붙임
[ 문법 ]
class ClassName
{
public static function __callStatic(
string $name,
array $arguments
): mixed {
// $name과 $arguments를 사용해 호출 처리
}
}
ClassName::methodName($argument1, $argument2);
/*
ClassName::methodName($argument1, $argument2)
→ 접근 가능한 정적 methodName()이 없는 경우
→ __callStatic("methodName", [$argument1, $argument2])
*/
[예시]
public static function __callStatic(string $name, array $arguments): mixed
{
if ($name === "create" && $arguments === []) {
return new self();
}
throw new BadMethodCallException("지원하지 않는 호출: $name");
}
$user = UserProfile::create();
echo $user->getName(); // Kim
/*
UserProfile::create()
→ 접근 가능한 정적 create() 메서드가 없음
→ __callStatic("create", [])
→ new self()로 UserProfile 객체 생성 후 반환
*/
문자열 반환
__toString()
객체를 문자열로 사용할 때 실행되는 메서드.
- 객체를 어떤 문자열로 표현할지 개발자가 정의함
- echo로 객체를 출력하거나 (string)으로 변환할 때 호출 됨
[ 문법 ]
class ClassName
{
public function __toString(): string
// public => 외부에서 접근 가능한 메서드로 선언
// 매개변수 없음 => 문자열 변환 시 별도 인수를 받지 않음
// : string => 문자열을 반환하도록 선언
{
return "객체를 표현할 문자열";
}
}
$object = new ClassName();
echo $object;
$text = (string) $object;
[ 예시 ]
class UserProfile
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function __toString(): string
{
return "사용자: {$this->name}";
}
}
$user = new UserProfile("Kim");
echo $user; // 사용자: Kim
/*
echo $user
→ 객체를 문자열로 표현해야 함
→ PHP가 __toString() 호출
→ "사용자: Kim" 반환
→ echo가 반환된 문자열 출력
*/
함수형 호출
__invoke()
객체를 함수처럼 호출할 때 실행되는 메서드.
- 객체 변수 뒤에 ()를 붙여 호출할 수 있음
[ 문법 ]
class ClassName
{
public function __invoke($parameter)
// $parameter = 호출 시 전달받을 값
{
// 객체를 함수처럼 호출했을 때 수행할 처리
return $parameter;
}
}
$object = new ClassName();
$result = $object($argument); // $object($argument) = 객체를 함수처럼 호출하는 문법
/*
$object($argument)
→ PHP가 __invoke($argument) 호출
→ 메서드 내부 코드 실행
→ 반환값을 $result에 저장
*/
[ 예시 ]
class UserProfile
{
private array $data = [
"name" => "Kim",
"role" => "admin"
];
public function __invoke(string $key): mixed
{
return $this->data[$key] ?? null;
}
}
$user = new UserProfile();
echo $user("name") . "\n";
echo $user("role");
/*
Kim
admin
*/
객체 복제
__clone()
clone으로 객체를 복제한 뒤, 복제본에서 실행되는 메서드
복제된 객체의 일부 값을 변경하거나, 내부에 포함된 객체를 추가로 복제하는 데 사용함.
[ 문법 ]
class ClassName
{
public function __clone(): void
{
// 복제본에 필요한 추가 처리
}
}
$original = new ClassName();
$copy = clone $original; // 기존 객체를 복제하여 새 객체 생성
[특징]
- __clone() 안의 $this는 복제본이므로 원본 이름은 변경되지 않음
=> clone이 먼저 새로운 객체를 만든다음, __clone()을 그 새 객체에서 실행함
(새로운 객체가 아닌, 같은 객체를 두 변수가 가리킴)
- 복제할 때 __construnct()가 다시 실행되지 않음.
- 기본적으로 얕은 복사를 수행함.
속성에 다른 객체가 들어 있다면 그 내부 객체까지 자동으로 복제되지는 않는다.
내부 객체도 분리해야 한다면 __clone()안에서 해당 객체를 다시 clone해야 함.
class Address
{
public string $city;
public function __construct(string $city)
{
$this->city = $city;
}
}
class User
{
public string $name;
// User 객체 안에 Address 객체 있음.
public Address $address;
public function __construct(string $name, Address $address)
{
$this->name = $name;
$this->address = $address;
}
}
$address = new Address("Seoul");
$user = new User("Kim", $address);
$copy = clone $user;
/*
$user -> User 객체 A
$copy -> User 객체 B
*/
// 단순한 속성인 name을 변경하면 서로 영향을 주지 않음
$copy->name = "Lee";
echo $user->name; // Kim
echo $copy->name; // Lee
// address의 경우
/*
$user
└─ User 객체 A
├─ name = "Kim"
└─ address ──────┐
↓
Address 객체 C
city = "Seoul"
$copy
└─ User 객체 B
├─ name = "Kim"
└─ address ──────┘
=> 두 속성이 같은 Address 객체를 바라보고 있음.
*/
// 복제본에서 주소를 변경하면
$copy->address->city = "Busan";
// 결과는 => 둘 다 바뀜
// why) Address 객체 자체는 복제되지 않음
echo $user->address->city; // Busan
echo $copy->address->city; // Busan
[ 예시 ]
class UserProfile
{
public string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function __clone(): void
{
$this->name .= " (복사본)";
}
}
$user = new UserProfile("Kim");
$copy = clone $user;
echo $user->name . "\n";
echo $copy->name;
/*
Kim
Kim (복사본)
*/
주의점
호출 흐름의 명확성
매직 메서드는 코드에 메서드 이름이 직접 나타나지 않아, 실제로 어떤 코드가 실행되는지 파악하기 어려울 수 있음
=> 일반 속성이나 메서드로 충분히 표현할 수 있다면 직접 선언하는 편이 명확함
ex]
$user->name; // __get()으로 연결될 수 있음
$user->getRole(); // __call()로 연결될 수 있음
$user("role"); // __invoke() 실행
오타와 잘못된 호출
존재하지 않는 속성이나 메서드를 처리할 때, 모든 요청을 받아 주면 오타를 발견하기 어려워질 수 있음
상태 변경 규칙
__set()을 정의했다고 해서 데이터가 자동으로 보호되는 것은 아님
=> 허용할 항목, 값의 타입, 수정 가능 여부가 중요하다면 메서드 내부에 검증 규칙을 직접 작성해야 함
동작의 예측 가능성
매직 메서드의 동작은 해당 문법에서 자연스럽게 예상할 수 있는 범위로 유지하는 것이 좋음.
| 메서드 | 설계 시 고려사항 |
| __get() | 단순 조회에 예상치 못한 상태 변경이 포함되지 않는지 |
| __set() | 저장할 값과 수정 가능한 항목을 검증하는지 |
| __call() | 지원하는 호출과 잘못된 호출을 구분하는지 |
| __toString() | 객체를 표현하는 문자열을 일관되게 반환하는지 |
| __invoke() | 객체를 함수처럼 호출하는 의미가 명확한지 |
| __clone() | 내부 객체도 별도로 복제해야 하는지 |
소멸 시점의 의존성
객체는 다른 곳에서 참조하는 동안 유지될 수 있으므로, 원하는 업무 시점과 소멸 시점이 일치한다고 가정하면 안 된다.
파일 저장이나 작업완료처럼 실행 시점이 중요한 처리는 일반 메서드로 명시적으로 호출하고, 소멸자에는 적절한 정리 자겅븡ㄹ 두는 편이 좋음.
'STUDY > PHP' 카테고리의 다른 글
| [객체지향] PHP Trait (1) | 2026.09.05 |
|---|---|
| PHP 변수 대입과 객체 복제 (1) | 2026.09.05 |
| [객체지향] PHP static과 스코프 해석 연산자 (0) | 2026.09.05 |
| PHP 네임스페이스와 use (0) | 2026.09.05 |
| Web Server에서 PHP가 실행되는 구조 (0) | 2026.08.29 |