오버로딩

오버로딩 (Overloading)

PHP에서 오버로딩은 속성(property)과 메서드(method)를 동적으로 만들어 쓰는 기능이에요. 이렇게 동적으로 생긴 것들은 클래스 안에 미리 정의해 둔 매직 메서드(magic method)가 처리해 줘요. 오버로딩 메서드는 선언되지 않았거나 현재 스코프에서 보이지 않는 속성·메서드에 접근할 때 호출돼요. 이 문서에서는 이 상태를 가리켜 inaccessible 속성, inaccessible 메서드라고 부를게요. 선언 여부와 가시성(visibility)을 묶어서 일컫는 표현이에요.

오버로딩 메서드는 반드시 public으로 정의해야 해요.

참고: 이 매직 메서드들의 인자는 참조(reference)로 전달할 수 없어요.

참고: PHP의 오버로딩은 대부분의 객체지향 언어에서 말하는 오버로딩과 달라요. 전통적인 오버로딩은 같은 이름에 인자의 개수·타입이 다른 여러 메서드를 두는 걸 가리키는데, PHP는 그 의미가 아니에요.

속성 오버로딩 (Property overloading)

public function __set(string $name, mixed $value): void
public function __get(string $name): mixed
public function __isset(string $name): bool
public function __unset(string $name): void

속성 오버로딩은 대상 속성이 inaccessible(protected 또는 private)이거나 아예 존재하지 않을 때 각 상황에 맞는 매직 메서드가 호출돼요.

  • __set() — 속성에 값을 쓸 때 실행돼요.
  • __get() — 속성에서 값을 읽을 때 사용돼요.
  • __isset() — inaccessible 또는 존재하지 않는 속성에 isset()이나 empty()를 호출하면 트리거돼요.
  • __unset() — 그런 속성에 unset()을 쓰면 호출돼요.

$name 인자는 접근 대상 속성의 이름이고, __set()$value 인자는 그 속성에 설정할 값을 담아요.

속성 오버로딩은 객체 컨텍스트에서만 동작해요. 정적(static) 컨텍스트에서는 이 매직 메서드들이 호출되지 않으므로, static으로 선언해서는 안 돼요. 오버로딩 매직 메서드를 static으로 선언하면 경고(warning)가 나와요.

참고: __set()의 반환값은 PHP가 대입 연산자를 처리하는 방식 때문에 무시돼요. 마찬가지로 아래처럼 대입을 이어 붙이면 __get()은 호출되지 않아요.

 $a = $obj->b = 8; 

참고: PHP는 같은 오버로딩 메서드 안에서 그 메서드를 다시 호출하지 않아요. 예를 들어 __get() 안에서 return $this->foo;라고 쓰면, foo 속성이 정의돼 있지 않을 때 __get()을 두 번째로 부르는 대신 null을 돌려주고 E_WARNING을 발생시켜요. 다만 오버로딩 메서드가 다른 오버로딩 메서드를 암묵적으로 호출하는 것(예: __set()__get()을 트리거)은 가능해요.

예제 #1 — __get(), __set(), __isset(), __unset() 메서드로 속성 오버로딩하기

<?php
class PropertyTest
{
    /**  Location for overloaded data.  */
    private $data = array();

    /**  Overloading not used on declared properties.  */
    public $declared = 1;

    /**  Overloading only used on this when accessed outside the class.  */
    private $hidden = 2;

    public function __set($name, $value)
    {
        echo "Setting '$name' to '$value'\n";
        $this->data[$name] = $value;
    }

    public function __get($name)
    {
        echo "Getting '$name'\n";
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }

        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    public function __isset($name)
    {
        echo "Is '$name' set?\n";
        return isset($this->data[$name]);
    }

    public function __unset($name)
    {
        echo "Unsetting '$name'\n";
        unset($this->data[$name]);
    }

    /**  Not a magic method, just here for example.  */
    public function getHidden()
    {
        return $this->hidden;
    }
}


$obj = new PropertyTest;

$obj->a = 1;
echo $obj->a . "\n\n";

var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";

echo $obj->declared . "\n\n";

echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
?>

위 예제의 출력은 다음과 같아요.

Setting 'a' to '1'
Getting 'a'
1

Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)

1

Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'

Notice: Undefined property via __get(): hidden in script on line 71 in script on line 27

메서드 오버로딩 (Method overloading)

public function __call(string $name, array $arguments): mixed
public static function __callStatic(string $name, array $arguments): mixed
  • __call()객체 컨텍스트에서 inaccessible 메서드를 호출할 때 트리거돼요.
  • __callStatic()정적 컨텍스트에서 inaccessible 메서드를 호출할 때 트리거돼요.

$name 인자는 호출된 메서드의 이름이고, $arguments 인자는 그 메서드에 전달된 인자들을 담은 열거형 배열이에요.

예제 #2 — __call()__callStatic() 메서드로 메서드 오버로딩하기

<?php
class MethodTest
{
    public function __call($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling object method '$name' "
             . implode(', ', $arguments). "\n";
    }

    public static function __callStatic($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling static method '$name' "
             . implode(', ', $arguments). "\n";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');

MethodTest::runTest('in static context');
?>

위 예제의 출력은 다음과 같아요.

Calling object method 'runTest' in object context
Calling static method 'runTest' in static context