본문 바로가기
PHP

[PHP] Magic Methods (__call, __get, __invoke)

by teamnova 2025. 11. 24.
728x90

매직 메소드(Magic Methods)는 PHP 클래스 내에서 특정 이벤트가 발생할 때 자동으로 호출되는 예약된 메소드들입니다. 
이를 활용하면 간결한 객체 지향 코드를 작성할 수 있습니다.

 

__get($name)
정의: 접근할 수 없는 속성(private, protected)이나 존재하지 않는 속성을 읽으려고 할 때 자동으로 호출됩니다.
용도: 속성을 동적으로 생성하거나, Private 속성을 읽기 전용(Read-only)으로 외부에 노출할 때 유용합니다.

 

<?php
class UserProfile
{
    // 실제 데이터는 이 배열에 저장됩니다.
    private array $data = [];

    // 데이터를 설정하는 메소드 (일반 메소드)
    public function setInfo(string $key, $value): void
    {
        $this->data[$key] = $value;
    }

    /**
     * __get: 존재하지 않거나 접근 불가능한 속성을 읽을 때 실행
     */
    public function __get(string $name)
    {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }
       
        return "속성 '$name'은 존재하지 않습니다.";
    }
}

// 사용 예시
$user = new UserProfile();
$user->setInfo('name', '홍길동');
$user->setInfo('email', 'hong@test.com');

// $user->name 이라는 실제 멤버 변수는 없지만, __get이 호출되어 배열 값을 반환
echo $user->name . PHP_EOL;   // 출력: 홍길동
echo $user->email . PHP_EOL;  // 출력: hong@test.com
echo $user->age . PHP_EOL;    // 출력: 속성 'age'은 존재하지 않습니다.

 

 

 

__call($name, $arguments)
정의: 객체 컨텍스트에서 접근할 수 없는 메소드(private, protected)나 존재하지 않는 메소드를 호출할 때 실행됩니다.
용도: 같은 기능을 하는 메소드 이름을 여러 개 허용하거나, 다른 객체의 메소드를 대신 실행해주는 프록시 패턴 구현에 많이 쓰입니다.

 

<?php
class ApiClient
{
    /**
     * __call: 정의되지 않은 메소드 호출 시 실행
     * @param string $name 호출한 메소드 이름 (예: get, post)
     * @param array $arguments 메소드에 전달된 인자들
     */
    public function __call(string $name, array $arguments)
    {
        // 허용된 메소드 이름인지 확인
        $allowedMethods = ['get', 'post', 'put', 'delete'];
        $method = strtolower($name);

        if (!in_array($method, $allowedMethods)) {
            throw new BadMethodCallException("메소드 $name() 은 지원하지 않습니다.");
        }

        // 첫 번째 인자는 URL, 두 번째 인자는 데이터라고 가정
        $url = $arguments[0] ?? '';
        $payload = $arguments[1] ?? [];

        echo "[요청 전송] Method: " . strtoupper($method) . "\n";
        echo " - URL: " . $url . "\n";
        echo " - Data: " . json_encode($payload) . "\n";
        echo "-------------------------\n";
    }
}

// 사용 예시
$client = new ApiClient();

// ApiClient 클래스에는 get() 이나 post() 메소드가 없지만 호출 가능
$client->get('/api/users');
$client->post('/api/users', ['name' => '철수', 'age' => 25]);

// 지원하지 않는 메소드 호출 시 예외 발생
// $client->patch('/api/users'); // BadMethodCallException 발생

 

 

__invoke(...)
정의: 객체를 함수처럼 호출하려 할 때 실행됩니다.
용도: '단일 책임 원칙'을 따르는 클래스(Single Action Controller)나, 콜백 함수로 객체를 전달해야 할 때 유용합니다.

 

<?php
class Multiplier
{
    private int $factor;

    public function __construct(int $factor)
    {
        $this->factor = $factor;
    }

    /**
     * __invoke: 객체를 함수처럼 호출할 때 실행 ($obj(10))
     */
    public function __invoke(int $number): int
    {
        return $number * $this->factor;
    }
}

// 사용 예시 1: 기본 사용
$double = new Multiplier(2); // 2배를 해주는 객체 생성
$triple = new Multiplier(3); // 3배를 해주는 객체 생성

echo $double(10) . PHP_EOL; // 출력: 20 (객체를 함수처럼 사용)
echo $triple(10) . PHP_EOL; // 출력: 30


// 사용 예시 2: 고차 함수(array_map 등)의 콜백으로 활용
$numbers = [1, 2, 3, 4, 5];

// $double 객체 자체가 콜백 함수(callable)로 작동함
$result = array_map($double, $numbers);

print_r($result);
/* 출력:
Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 8
    [4] => 10
)
*/