MasterYodaの昼下がり

日々の備忘録的なアレ

cloneの挙動

http://php.net/manual/ja/language.oop5.cloning.php

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

勝手にディープコピーだと思い込んで、厄介なバグを生みかけた。 内部で保持してるオブジェクトも別のインスタンスになると思ってると厄介なことになる。 マニュアルはちゃんと読もう。

リファレンスが保持されていて、シャローコピーされるということは、cloneしても同じオブジェクトへの参照を保持してるわけで、 片方のオブジェクト経由で適用した変更は、もう片方のcloneしたオブジェクト経由で取得した際にも当然反映されてることになる。

サンプルコード

<?php

class A {

    private $object;

    public function getObject()
    {
        if (!$this->object) {
            $this->object = new B();
        }
        return $this->object;
    }

}

class B {

    private $count = 0;

    public function inc()
    {
        $this->count++;
    }

    public function getCount()
    {
        return $this->count;
    }

}

// $a->getObjectしてBのカウントをインクメント
$a = new A();
$a->getObject()->inc();

// $aをclone
$a_clone = clone $a;

// ここで一回$a、$a_cloneそれぞれのBオブジェクトのカウントを出力
// 両方1が出力される。
print($a->getObject()->getCount() . "\n");
print($a_clone->getObject()->getCount() . "\n");

// $aで保持してるBオブジェクトのカウントをインクリメント
$a->getObject()->inc();

// $a、$a_cloneそれぞれで保持してるBオブジェクトのカウントを出力
// $a,$a_cloneそれぞれで保持してるのは、Bオブジェクトへのリファレンスの為、
// 両方2が出力される
print($a->getObject()->getCount() . "\n");
print($a_clone->getObject()->getCount() . "\n”);

実行結果

$ php clone_test.php
1
1
2
2
$

サンプルコード中で$a->getObject()を実行する前に($aでBへの参照を保持する前に)、cloneすれば$a、$a_cloneそれぞれのBへの参照は別のインスタンスへの参照となる。