개발블로그

[객체지향] PHP 매직 메서드 본문

STUDY/PHP

[객체지향] PHP 매직 메서드

devmel 2026. 9. 5. 18:55
Contents 접기
 

개념

 

개념

특정 상황이 발생했을 때 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