객체 복제
객체 복제 (Object Cloning)
객체를 복사할 때 모든 프로퍼티를 똑같이 복제하는 게 항상 원하는 동작은 아니에요. 복사 생성자(copy constructor)가 필요한 대표적인 경우로, GTK 창을 나타내는 객체가 있다고 생각해 봐요. 그 객체가 GTK 창의 리소스를 들고 있는데, 이걸 똑같이 복제하면 새 창을 만들고 싶을 때도 기존 창의 리소스를 공유해 버리게 되거든요. 이럴 땐 같은 프로퍼티를 가지되 새 창의 리소스를 새로 들고 있는 객체가 필요해요. 또 객체가 내부에서 쓰는 다른 객체에 대한 참조를 갖고 있을 때도 마찬가지예요. 부모 객체를 복제하면 참조된 그 객체도 새 인스턴스로 만들어서, 복제본이 자기만의 별도 사본을 가지도록 하고 싶을 때가 있죠.
객체의 복사본은 clone 키워드로 만들 수 있어요. 가능하면 이때 객체의 __clone() 메서드가 호출됩니다.
$copy_of_object = clone $object;
객체가 복제되면 PHP는 객체의 모든 프로퍼티를 얕은 복사(shallow copy) 합니다. 다른 변수를 가리키는 참조 프로퍼티들은 여전히 그 참조를 유지해요.
function __clone(): void
복제가 끝난 뒤에 __clone() 메서드가 정의되어 있다면, 새로 만들어진 객체의 __clone() 메서드가 호출되어 필요한 프로퍼티를 바꿀 수 있어요.
예제 #1 — 객체 복제하기
<?php
class SubObject
{
static $instances = 0;
public $instance;
public function __construct() {
$this->instance = ++self::$instances;
}
public function __clone() {
$this->instance = ++self::$instances;
}
}
class MyCloneable
{
public $object1;
public $object2;
function __clone()
{
// Force a copy of this->object, otherwise
// it will point to same object.
$this->object1 = clone $this->object1;
}
}
$obj = new MyCloneable();
$obj->object1 = new SubObject();
$obj->object2 = new SubObject();
$obj2 = clone $obj;
print "Original Object:\n";
print_r($obj);
print "Cloned Object:\n";
print_r($obj2);
?>
위 예제는 다음을 출력합니다.
Original Object:
MyCloneable Object
(
[object1] => SubObject Object
(
[instance] => 1
)
[object2] => SubObject Object
(
[instance] => 2
)
)
Cloned Object:
MyCloneable Object
(
[object1] => SubObject Object
(
[instance] => 3
)
[object2] => SubObject Object
(
[instance] => 2
)
)
복제된 직후의 객체 멤버를 하나의 표현식 안에서 바로 접근할 수도 있어요.
예제 #2 — 복제된 객체의 멤버에 접근하기
<?php
$dateTime = new DateTime();
echo (clone $dateTime)->format('Y');
?>
위 예제는 다음과 비슷한 결과를 출력합니다.
2016
프로퍼티 오버라이드와 함께 복제하기
PHP 8.5.0부터 clone은 함수로도 쓸 수 있어요. 두 번째 파라미터인 withProperties는 프로퍼티 이름을 복제본이 받게 될 값에 매핑한 배열을 받습니다. 나열된 프로퍼티만 덮어쓰이고, 다른 프로퍼티는 복제된 그대로의 값을 유지해요.
이 오버라이드들은 __clone()이 실행된 뒤에 적용되고, 모든 프로퍼티의 가시성(visibility)을 존중합니다. readonly 프로퍼티는 원본 객체에서 이미 설정되어 있었더라도 복제본에서 업데이트할 수 있어요. 그래서 이 함수는 수정된 복사본을 반환하는 메서드를 만들기에 잘 맞습니다.
예제 #3 — 프로퍼티 오버라이드와 함께 객체 복제하기
<?php
class Point {
public function __construct(
public readonly int $x,
public readonly int $y,
) {}
public function withX(int $x): static
{
return clone($this, ['x' => $x]);
}
}
$p = new Point(1, 2);
$q = $p->withX(10);
echo $q->x, ' ', $q->y;
?>
위 예제는 다음을 출력합니다.
10 2