オブジェクトのクローン作成

オブジェクトのコピーを作成する際、そのプロパティも全て二重化することが、 常に望ましい動作であるわけではありません。 コピーコンストラクタが必要となる例として、 GTKウインドウを表すオブジェクトを有しており、 そのオブジェクトがGTKウインドウのリソースを保持している際、 コピーを作成する時に、同じプロパティを有するウインドウを作成し、 その新しいオブジェクトがその新しいウインドウのリソースを保持する ようにしたい場合が考えられます。 他の例としては、 オブジェクトがそのオブジェクトが使用する他のオブジェクトへのリファレンスを 保持しており、親オブジェクトをコピーする際に、そのコピーが独立したオブジェクトの コピーを有するように、そのオブジェクトのインスタンスを新たに作成したい場合が 考えられます。

オブジェクトのコピーは、clone キーワード (これは、そのオブジェクトの __clone() メソッドがある場合にこれをコールします)により作成されます。 オブジェクトの __clone() メソッドを直接コールすることはできません。

$copy_of_object = clone $object;

オブジェクトのクローンが作成される際、PHP は、そのオブジェクトのプロパティを 全てシャローコピーします。他の変数へのリファレンスを保持する全てのプロパティは、 リファレンスのままとなります。

__clone ( void ) : 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()
    {
        
// this->object のコピーを作成します。こうしないと、
        // 同じオブジェクトを指すことになってしまいます。
        
$this->object1 = clone $this->object1;
    }
}

$obj = new MyCloneable();

$obj->object1 = new SubObject();
$obj->object2 = new SubObject();

$obj2 = clone $obj;


print(
"元のオブジェクト\n");
print_r($obj);

print(
"クローンオブジェクト\n");
print_r($obj2);

?>

上の例の出力は以下となります。

元のオブジェクト
MyCloneable Object
(
    [object1] => SubObject Object
        (
            [instance] => 1
        )

    [object2] => SubObject Object
        (
            [instance] => 2
        )

)
クローンオブジェクト
MyCloneable Object
(
    [object1] => SubObject Object
        (
            [instance] => 3
        )

    [object2] => SubObject Object
        (
            [instance] => 2
        )

)

PHP 7.0.0 以降では、新しくクローンしたオブジェクトのメンバーに、作成したその式の中でもアクセスできるようになりました。

例2 クローンしたオブジェクトのメンバーへのアクセス

<?php
$dateTime 
= new DateTime();
echo (clone 
$dateTime)->format('Y');
?>

上の例の出力は、 たとえば以下のようになります。

2016