web-dev-qa-db-ja.com

PHPのオブジェクトクローニングとは

誰かが私を説明できますか

  • pHPのオブジェクトクローニングとは

  • PHPでcloneキーワードを使用する必要があるのはいつですか?

40
user213559

オブジェクトの複製とは、オブジェクトのコピーを作成することです。 Cody が指摘したように、PHPでのクローン作成は、オブジェクトの浅いコピーを作成することによって行われます。これは、クローンされたオブジェクトの内部オブジェクトがnotマジックメソッド__clone()を定義して、これらの内部オブジェクトも複製するようにオブジェクトに明示的に指示しない限り、複製されます。

__cloneメソッドを使用しない場合、新しいオブジェクトの内部オブジェクトは、複製された元のオブジェクトの内部オブジェクトと同じメモリ内のオブジェクトへの参照になります。

次の例を検討してください。

// in this exampe the internal member $_internalObject of both objects
// reference the same instance of stdClass in memory.
class CloneableClass
{
    private $_internalObject;

    public function __construct()
    {
        // instantiate the internal member
        $this->_internalObject = new stdClass();
    }
}

$classA = new CloneableClass();
$classB = clone $classA;


// in this exampe the internal member $_internalObject of both objects
// DON'T reference the same instance of stdClass in memory, but are inividual instances
class CloneableClass
{
    private $_internalObject;

    public function __construct()
    {
        // instantiate the internal member
        $this->_internalObject = new stdClass();
    }

    // on clone, make a deep copy of this object by cloning internal member;
    public function __clone()
    {
        $this->_internalObject = clone $this->_internalObject;
    }
}

$classA = new CloneableClass();
$classB = clone $classA;

クローンの使用例は、たとえば、オブジェクトの内部状態を外部オブジェクトに混乱させたくない場合です。

内部オブジェクトAddressを持つクラスUserがあるとします。

class Address
{
    private $_street;
    private $_streetIndex;
    private $_city;
    // etc...

    public function __construct( $street, $streetIndex, $city /* etc.. */ )
    {
        /* assign to internal values */
    }
}

class User
{
    // will hold instance of Address
    private $_address;

    public function __construct()
    {
        $this->_address = new Address( 'somestreet', '1', 'somecity' /* etc */ );
    }

    public function getAddress()
    {
        return clone $this->_address;
    }
}

議論のために、外部オブジェクトにユーザーオブジェクトの内部アドレスを混乱させたくないが、アドレスオブジェクトのコピーを提供できるようにしたいとします。上記の例はこれを示しています。 getAddressメソッドは、アドレスオブジェクトのクローンを呼び出し元のオブジェクトに返します。つまり、呼び出し側オブジェクトがアドレスオブジェクトを変更しても、ユーザーの内部アドレスは変更されません。クローンを指定しなかった場合、外部オブジェクトwouldは、参照ではなくデフォルトで参照が提供されるため、ユーザーの内部アドレスを変更できます。

これがすべて理にかなっているといいのですが。

PS.:
ただし、Addressにも内部オブジェクトがある場合、__clone()を定義して、Addressが自分自身の複製を(この投稿の2番目の例のように)複製することを確認する必要があります。アドレス。そうしないと、データがねじ込まれている理由を理解しようとすることで頭痛がするでしょう。

40
Decent Dabbler

クローニングは、オブジェクトの本物のコピーを作成するために使用されます。オブジェクトを別の変数に割り当ててもコピーは作成されません。むしろ、オブジェクトと同じメモリ位置への参照が作成されます。

<?php

$o= new stdclass;
$o->a= 'b';
$o->b= 'c';

$o2= $o;
$o2->a= 'd';

var_dump($o);
var_dump($o2);

$o3= clone $o;
$o3->a= 'e';
var_dump($o);
var_dump($o3);

?>

このサンプルコードは次のように出力します。

object(stdClass)#1 (2) {
  ["a"]=>
  string(1) "d"
  ["b"]=>
  string(1) "c"
}
object(stdClass)#1 (2) {
  ["a"]=>
  string(1) "d"
  ["b"]=>
  string(1) "c"
}
object(stdClass)#1 (2) {
  ["a"]=>
  string(1) "d"
  ["b"]=>
  string(1) "c"
}
object(stdClass)#2 (2) {
  ["a"]=>
  string(1) "e"
  ["b"]=>
  string(1) "c"
}
27
leepowers

PHP 5の観点からのオブジェクトの複製は、 "浅いコピー" と呼ばれるものです。次に、複製されるオブジェクトで__clone()メソッドを呼び出します。

7
Cody Haines

ディープクローン(つまり、子オブジェクトのクローンと孫オブジェクトのクローン)が必要な場合は、__clone各クラスで、または単にオブジェクトをシリアライズ+シリアライズ解除します。

function deepClone($object)
{
    return unserialize(serialize($object));
}
6
cweiske

他の回答で説明されているように、cloneはオブジェクトの浅いコピーを作成します。

ディープコピー(つまり、再帰的コピー)を作成する必要がある場合は、__clone()メソッドをオーバーロードできます。

このライブラリを使用することもできます:MyCLabs\DeepCopyこれは、単純なクローンよりも単純で強力です。

1
Matthieu Napoli