PHPでオブジェクトのコピーを作成するにはどうすればよいですか?


168

PHPオブジェクトでは参照によって渡されるようです。代入演算子でさえ、オブジェクトのコピーを作成しているようには見えません。

簡単な、不自然な証明があります:

<?php

class A {
    public $b;
}


function set_b($obj) { $obj->b = "after"; }

$a = new A();
$a->b = "before";
$c = $a; //i would especially expect this to create a copy.

set_b($a);

print $a->b; //i would expect this to show 'before'
print $c->b; //i would ESPECIALLY expect this to show 'before'

?>

どちらの印刷ケースでも「後」になります

では、$ aを参照ではなく値でset_b()に渡すにはどうすればよいですか?


2
実際にこの動作が必要になるケースはほとんどありません。自分で頻繁に使用している場合、コードの記述方法に根本的な問題があるのではないでしょうか。
troelskn 2008年

1
いいえ、まだ使用する必要はありません。
Nick Stinemates 2008年

(object) ((array) $objectA)clone $objectAまたはを使用した場合よりも優れたパフォーマンスで同じ望ましい結果が得られる可能性がありますnew stdClass
Binyamin

回答:


284

PHP 5以降では、オブジェクトは参照渡しされます。PHP 4では、値によって渡されます(そのため、参照渡しのランタイムがありましたが、これは非推奨になりました)。

PHP5で 'clone'演算子を使用してオブジェクトをコピーできます。

$objectB = clone $objectA;

また、参照で渡されるのは単なるオブジェクトであり、質問で述べたすべてのものではありません...


これを読んでいる人に追加したいだけで、そのクローンは元のオブジェクトへの参照を維持します。複製されたオブジェクトを使用してMySQLクエリを実行すると、実行が線形的に行われない可能性があるため、予測できない結果になる可能性があります。
Ælex

20
よくある誤解を修正するには(PHPのドキュメントでさえ誤解していると思います!)PHP 5のオブジェクトは「参照渡し」されていません。Javaの場合と同様に、追加レベルの間接指定があります。変数は「オブジェクトポインター」を指し、それがオブジェクトを指します。したがって、2つの変数は、同じ値を参照することなく、同じオブジェクト指すことができます。これは、この例から見ることができます。$a = new stdClass; $b =& $a; $a = 42; var_export($b);ここ$b変数 への参照があります$a=&通常の=に置き換えると、それは参照ではなく、元のオブジェクトを指します。
IMSoP 2013年

参照による実行時の受け渡しは、関数呼び出しの効果が仕様ではなく関数の実装に依存するため、悪い考えです。値渡しがデフォルトであることとは関係ありません。
Oswald

1
@アレックスコメントについて詳しく教えてください。(ここか他のどこかで。)あなたの要点は少し不明確なIMOから出てきます。
Chris Middleton、

@ChrisMiddleton CまたはC ++の用語について考えます。解放されたオブジェクト、スコープ外のオブジェクト、または解放されたオブジェクトへの参照を複製した場合、複製された参照は無効になります。したがって、クローン作成を通じて参照を保持している元のオブジェクトで何が起こったかによって、未定義の動作が発生する可能性があります。
Ælex

103

答えは一般的にJavaの本にあります。

  1. clone:cloneメソッドをオーバーライドしない場合、デフォルトの動作は浅いコピーです。オブジェクトにプリミティブメンバー変数しかない場合は、まったく問題ありません。しかし、メンバー変数として別のオブジェクトを使用する型なし言語では、それは頭痛の種です。

  2. シリアライゼーション/デシリアライゼーション

$new_object = unserialize(serialize($your_object))

これにより、オブジェクトの複雑さに応じて、コストのかかるディープコピーが実現します。


4
+1すばらしい、すばらしい、PHPでDEEPコピーを行うためのすばらしい方法。代わりに、PHPクローンキーワードによって提供される標準の浅いコピーについてお聞きします。プリミティブメンバー変数のみがコピーされるとのことですが、PHPの配列/文字列はプリミティブメンバー変数と見なされるため、コピーされますよね。
Marco Demaio 2010

3
これを手にした人にとって: "浅い"コピー($a = clone $b、魔法の__clone()メソッドがない)は$b、用語のオブジェクトの各プロパティを調べ、同じクラスの新しいメンバーの同じプロパティに割り当てることと同じです。=。オブジェクトであるプロパティはclonedを取得せず、配列内のオブジェクトも取得しません。参照によってバインドされた変数についても同様です。他のすべては単なる値であり、割り当てと同様にコピーされます。
IMSoP 2013年

3
パーフェクト!json_decode(json_encode($ obj)); プライベート/保護されたプロパティと任意のメソッドを複製しない... unserialize(serialize not clone methods ......
zloctb

驚くばかり!私はようやくPhpStormのエラーを取り除きました。Call to method __clone from invalid context:)
numediaweb

友人が次のようにすると、PHP解析エラーが発生します:$new_date = (clone $date_start)->subDays(1);で失敗し()ます。それらを削除すると、別のエラーが発生します。問題は、私たちはまったく同じphp 7.2.3を使用していて、私はうまく機能することです。何か案は?あらゆる場所で検索..
絵文字

21

以前のコメントによると、メンバー変数として別のオブジェクトがある場合は、次のようにします。

class MyClass {
  private $someObject;

  public function __construct() {
    $this->someObject = new SomeClass();
  }

  public function __clone() {
    $this->someObject = clone $this->someObject;
  }

}

これでクローンを作成できます:

$bar = new MyClass();
$foo = clone $bar;


4

PHPがコピーオンライトを使用することを明確にするために、基本的には変更するまですべてが参照ですが、オブジェクトの場合は、受け入れられた回答のようにcloneおよび__clone()マジックメソッドを使用する必要があります。


1

このコードは、メソッドの複製に役立ちます

class Foo{

    private $run=10;
    public $foo=array(2,array(2,8));
    public function hoo(){return 5;}


    public function __clone(){

        $this->boo=function(){$this->hoo();};

    }
}
$obj=new Foo;

$news=  clone $obj;
var_dump($news->hoo());

このコードは少し役に立たなく、__ cloneメソッドを削除しても機能します:)
amik

1

私はいくつかのテストをしていて、これを得ました:

class A {
  public $property;
}

function set_property($obj) {
  $obj->property = "after";
  var_dump($obj);
}

$a = new A();
$a->property = "before";

// Creates a new Object from $a. Like "new A();"
$b = new $a;
// Makes a Copy of var $a, not referenced.
$c = clone $a;

set_property($a);
// object(A)#1 (1) { ["property"]=> string(5) "after" }

var_dump($a); // Because function set_property get by reference
// object(A)#1 (1) { ["property"]=> string(5) "after" }
var_dump($b);
// object(A)#2 (1) { ["property"]=> NULL }
var_dump($c);
// object(A)#3 (1) { ["property"]=> string(6) "before" }

// Now creates a new obj A and passes to the function by clone (will copied)
$d = new A();
$d->property = "before";

set_property(clone $d); // A new variable was created from $d, and not made a reference
// object(A)#5 (1) { ["property"]=> string(5) "after" }

var_dump($d);
// object(A)#4 (1) { ["property"]=> string(6) "before" }

?>

1

この例では、iPhoneクラスを作成し、それを複製することで、そこから正確なコピーを作成します

class iPhone {

public $name;
public $email;

    public function __construct($n, $e) {

       $this->name = $n;
       $this->email = $e;

    }
}


$main = new iPhone('Dark', 'm@m.com');
$copy = clone $main;


// if you want to print both objects, just write this    

echo "<pre>"; print_r($main);  echo "</pre>";
echo "<pre>"; print_r($copy);  echo "</pre>";

-1

別のインスタンスのオブジェクトのプロパティを完全にコピーする場合は、この手法を使用できます。

それをJSONにシリアル化してから、逆シリアル化してObjectに戻します。


7
うーん、私は地獄のようにそれを避けます。
ジミー・ケイン、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.