오버로딩
오버로딩 (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