Wie erstelle ich eine Kopie eines Objekts in PHP?

168

Es scheint, dass in PHP Objekte als Referenz übergeben werden. Selbst Zuweisungsoperatoren scheinen keine Kopie des Objekts zu erstellen.

Hier ist ein einfacher, erfundener Beweis:

<?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'

?>

In beiden Druckfällen bekomme ich "nach"

Wie übergebe ich $ a an set_b () als Wert und nicht als Referenz?

Nick Stinemates
quelle
2
Es gibt sehr wenige Fälle, in denen Sie dieses Verhalten tatsächlich möchten. Wenn Sie es also häufig selbst verwenden, stimmt möglicherweise etwas grundlegenderes nicht mit der Art und Weise, wie Sie Ihren Code schreiben?
troelskn
1
Nein, ich musste es noch nicht benutzen.
Nick Stinemates
(object) ((array) $objectA)Möglicherweise erhalten Sie dieselben gewünschten Ergebnisse mit einer besseren Leistung als mit clone $objectAoder new stdClass.
Binyamin

Antworten:

284

In PHP 5+ werden Objekte als Referenz übergeben. In PHP 4 werden sie als Wert übergeben (aus diesem Grund wurde die Laufzeit als Referenz übergeben, was veraltet war).

Sie können den 'Klon'-Operator in PHP5 verwenden, um Objekte zu kopieren:

$objectB = clone $objectA;

Außerdem werden nur Objekte als Referenz übergeben, nicht alles, was Sie in Ihrer Frage gesagt haben ...

Eran Galperin
quelle
Ich möchte nur jedem hinzufügen, der dies liest, dass das Klonen weiterhin auf das ursprüngliche Objekt verweist. Das Ausführen von MySQL-Abfragen mit dem geklonten Objekt kann daher zu unvorhersehbaren Ergebnissen führen, da die Ausführung möglicherweise nicht linear erfolgt.
Ælex
20
Um ein häufiges Missverständnis zu korrigieren (ich denke, sogar die PHP-Dokumente verstehen es falsch!), Werden die Objekte von PHP 5 nicht "als Referenz übergeben". Wie in Java haben sie eine zusätzliche Indirektionsebene - die Variable zeigt auf einen "Objektzeiger" und diese zeigt auf ein Objekt. Somit können zwei Variablen auf dasselbe Objekt verweisen, ohne auf denselben Wert zu verweisen. Dies ist aus diesem Beispiel ersichtlich: $a = new stdClass; $b =& $a; $a = 42; var_export($b);Hier $bist ein Verweis auf die Variable $a ; Wenn Sie durch =&ein normales Objekt ersetzen =, handelt es sich nicht um eine Referenz und verweist weiterhin auf das ursprüngliche Objekt.
IMSoP
Die Referenzübergabe zur Laufzeit ist eine schlechte Idee, da die Auswirkung eines Funktionsaufrufs eher von der Implementierung der Funktion als von der Spezifikation abhängt. Es hat nichts damit zu tun, dass der Standardwert übergeben wird.
Oswald
1
@Alex Können Sie Ihren Kommentar näher erläutern? (Entweder hier oder anderswo.) Ihr Punkt kommt etwas unklar IMO.
Chris Middleton
@ChrisMiddleton Denken Sie an Begriffe aus C oder C ++: Wenn Sie einen Verweis auf ein Objekt geklont haben, das frei, außerhalb des Gültigkeitsbereichs oder freigegeben ist, ist Ihr geklonter Verweis ungültig. So Sie können nicht definiertes Verhalten, je nachdem , was mit dem ursprünglichen Objekt passierte, zu dem Sie eine Referenz über das Klonen halten.
Ælex
103

Die Antworten finden Sie häufig in Java-Büchern.

  1. Klonen: Wenn Sie die Klonmethode nicht überschreiben, ist das Standardverhalten eine flache Kopie. Wenn Ihre Objekte nur primitive Elementvariablen haben, ist dies völlig in Ordnung. Aber in einer typenlosen Sprache mit einem anderen Objekt als Mitgliedsvariablen bereitet es Kopfschmerzen.

  2. Serialisierung / Deserialisierung

$new_object = unserialize(serialize($your_object))

Dies führt zu einer tiefen Kopie mit hohen Kosten, abhängig von der Komplexität des Objekts.

Yogman
quelle
4
+1 großartige, großartige, großartige Möglichkeit, eine DEEP-Kopie in PHP zu erstellen, auch sehr einfach. Lassen Sie mich stattdessen etwas über die standardmäßige flache Kopie des PHP-Klon-Schlüsselworts fragen. Sie sagten, dass nur primitive Elementvariablen kopiert werden: Werden PHP-Arrays / Strings als primitive Elementvariablen betrachtet, damit sie kopiert werden, habe ich Recht?
Marco Demaio
3
Für jeden, der dies aufgreift: Eine "flache" Kopie ( $a = clone $bohne magische __clone()Methoden im Spiel) entspricht dem Betrachten jeder Eigenschaft eines Objekts $bim Begriff und dem Zuweisen derselben Eigenschaft in einem neuen Mitglied derselben Klasse unter Verwendung von =. Eigenschaften, die Objekte sind, erhalten weder cloned noch Objekte innerhalb eines Arrays. das gleiche gilt für durch Referenz gebundene Variablen; Alles andere ist nur ein Wert und wird wie bei jeder Aufgabe kopiert.
IMSoP
3
Perfekt! json_decode (json_encode ($ obj)); keine privaten / geschützten Eigenschaften und keine Methode klonen ... unserialisieren (nicht auch Klonmethoden serialisieren ...
zloctb
Genial! Ich werde endlich den Fehler von PhpStorm los; Call to method __clone from invalid context:)
numediaweb
Freund bekommt einen PHP-Analysefehler, wenn er es wie folgt macht: $new_date = (clone $date_start)->subDays(1);Es schlägt fehl mit (), wenn ich sie entferne, bekomme ich einen anderen Fehler. Die Sache ist, wir verwenden genau das gleiche PHP 7.2.3 und meins funktioniert gut. Irgendwelche Ideen? Überall gesucht ..
Emotionalität
21

Wenn Sie laut vorherigem Kommentar ein anderes Objekt als Mitgliedsvariable haben, gehen Sie wie folgt vor:

class MyClass {
  private $someObject;

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

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

}

Jetzt können Sie klonen:

$bar = new MyClass();
$foo = clone $bar;
Stanislav
quelle
4

Nur um zu verdeutlichen, dass PHP beim Schreiben kopiert wird, ist im Grunde alles eine Referenz, bis Sie es ändern. Für Objekte müssen Sie jedoch den Klon und die magische Methode __clone () wie in der akzeptierten Antwort verwenden.

Patricio Rossi
quelle
1

Dieser Code hilft beim Klonen von Methoden

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());
zloctb
quelle
Dieser Code ist ein bisschen nutzlos, es würde auch funktionieren, wenn Sie die __clone-Methode entfernen :)
amik
1

Ich habe einige Tests durchgeführt und Folgendes erhalten:

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" }

?>
Pyetro
quelle
1

In diesem Beispiel erstellen wir eine iPhone- Klasse und erstellen durch Klonen eine exakte Kopie davon

class iPhone {

public $name;
public $email;

    public function __construct($n, $e) {

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

    }
}


$main = new iPhone('Dark', '[email protected]');
$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>";
Muhammad Ebrahim
quelle
-1

Wenn Sie die Eigenschaften eines Objekts in einer anderen Instanz vollständig kopieren möchten, können Sie diese Technik verwenden:

Serialisieren Sie es in JSON und de-serialisieren Sie es dann wieder in Object.

diy_nunez
quelle
7
Hmm, ich würde es höllisch vermeiden.
Jimmy Kane