PHP'de bir nesnenin kopyasını nasıl oluştururum?


168

Görünüşe göre PHP nesnelerine referansla aktarılmıştır. Atama işleçleri bile Nesnenin bir kopyasını oluşturuyor gibi görünmüyor.

İşte basit ve anlaşılmaz bir kanıt:

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

?>

Her iki basılı durumda da 'sonra' alıyorum

Peki, $ a değerini set_b () 'ye değere göre değil, referansa göre nasıl iletirim ?


3
Bu davranışı gerçekten istediğiniz çok az durum vardır. Eğer kendini sık sık kullanırsanız, o zaman belki de kodunuzu yazma şeklinizde daha temel bir yanlış var mı?
troelskn

1
Hayır, henüz kullanmaya gerek yok.
Nick Stinemates

(object) ((array) $objectA)clone $objectAveya kullandığınızdan daha iyi performansla aynı sonuçlarla sonuçlanabilir new stdClass.
Binyamin

Re "Atama işleçleri bile Nesnenin bir kopyasını oluşturuyor gibi görünmüyor." - Umarım yapmamalıyım! Eğer öyleyse, sonuç artık bir OO dili olmayacaktır (tüm pratik amaçlar için).
ToolmakerSteve

Yanıtlar:


285

PHP'de 5+ nesneler referans ile iletilir. PHP 4'te değere göre geçirilirler (bu yüzden kullanımdan kaldırılmış çalışma zamanı geçişine sahipti).

Nesneleri kopyalamak için PHP5'teki 'klon' işlecini kullanabilirsiniz:

$objectB = clone $objectA;

Ayrıca, sadece referansla iletilen nesneler, sorunuzda söylediğiniz her şey değil ...


Sadece bunu okuyan herkese eklemek istiyorum, bu klonlama orijinal nesneye referans tutacaktır. Klonlanmış nesneyi kullanarak MySQL sorgularını çalıştırmak, yürütme doğrusal bir şekilde gerçekleşmeyebileceğinden, bu nedenle öngörülemeyen sonuçlar doğurabilir.
Marlex

20
Ortak bir yanlış anlama düzeltmek için (Ben bile PHP dokümanlar yanlış olsun düşünüyorum!) PHP 5 nesneleri "referans tarafından geçti" değildir. Java'da olduğu gibi, ek bir dolaylılık düzeyine sahiptirler - değişken bir "nesne işaretçisi" ne işaret eder ve bu bir nesneyi gösterir. Böylece iki değişken , aynı değere referans olmadan aynı nesneyi gösterebilir . Bu, bu örnekten görülebilir: $a = new stdClass; $b =& $a; $a = 42; var_export($b);burada değişkene$b bir referans ; normal ile değiştirirseniz , bu bir referans değildir ve yine de orijinal nesneyi gösterir. $a=&=
IMSoP

Bir çalışma çağrısının etkisini, belirtim yerine işlevin uygulanmasına bağlı kıldığından, başvuru yoluyla çalışma zamanı geçişi kötü bir fikirdir. Varsayılan değer olarak pass ile ilgisi yoktur.
Oswald

1
@Alex Yorumunuzu ayrıntılarıyla açıklayabilir misiniz? (Ya burada ya da başka bir yerde.) Demek istediğin biraz belirsiz IMO geliyor.
Chris Middleton

@ChrisMiddleton C veya C ++ terimlerini düşünün: özgür, kapsam dışında veya serbest bırakılmış bir nesneye referans klonladıysanız, klonlanmış referansınız geçersiz kılınır. Böylece , klonlama yoluyla bir referansı tuttuğunuz orijinal nesne ile ne olduğuna bağlı olarak tanımlanmamış davranışlar elde edebilirsiniz .
Aprlex

104

Cevaplar genellikle Java kitaplarında bulunur.

  1. klonlama: Klonlama yöntemini geçersiz kılmazsanız, varsayılan davranış sığ kopyadır. Nesnelerinizde yalnızca ilkel üye değişkenleri varsa, sorun yoktur. Ancak üye değişkenler olarak başka bir nesneye sahip olan tipik bir dilde, bu bir baş ağrısıdır.

  2. seri / seri kaldırma

$new_object = unserialize(serialize($your_object))

Bu, nesnenin karmaşıklığına bağlı olarak ağır bir maliyetle derin kopya elde eder.


4
+ 1 PHP'de DEEP kopyasını yapmak için harika, harika, harika bir yol, çok kolay. Bunun yerine size PHP klon anahtar kelimesi tarafından sunulan standart sığ kopya hakkında bir şey sormama izin verin, sadece ilkel üye değişkenlerin kopyalandığını söylediniz: PHP dizileri / dizeleri ilkel üye değişkenler olarak kabul edildi, bu yüzden kopyalanıyorlar, değil mi?
Marco Demaio

3
Bunu alan herkes için: "sığ" bir kopya ( oyunda $a = clone $bsihirli __clone()yöntemler olmadan ), nesnenin özelliklerinin her birine bakmak $bve aynı sınıfın yeni bir üyesinde aynı özelliğe atamakla eşdeğerdir. =. Nesne olan özellikler cloned veya bir dizi içindeki nesneler elde edilmez ; aynı şey referans ile bağlı değişkenler için de geçerlidir; diğer her şey bir değerdir ve herhangi bir ödevde olduğu gibi kopyalanır.
IMSoP

3
Mükemmel! json_decode (json_encode ($ obj)); özel / korumalı özellikleri ve herhangi bir yöntemi klonlamayın ...
emperyalize etmeyin

Müthiş! Sonunda PhpStorm hata kurtulmak; Call to method __clone from invalid context:)
numediaweb

1
Bu , çok fazla çalışma zamanı yükü ekler . Nesneyi bir dizeye serileştiriyorsunuz ve sonra bu dizeyi yeni bir değişkene yeniden ayrıştırıyorsunuz. Bu yapmak istediğinizi yaparken, bunu oldukça yavaş bir şekilde yapar. Zaman ve bellek kullanarak tüm nesnenizi bir dizgiye ve geriye dönüştürmekle kalmaz, PHP'lerin olası CopyOnWrite mekanizmasını da kırmış olursunuz. Çok daha iyi bir yöntem, aşağıdaki stackoverflow.com/a/186191/1614903__clone tarafından önerildiği gibi yönteminizi doğru bir şekilde uygulamaktır . Ayrıntılı bir açıklama için phpinternalsbook.com/php5/zvals/memory_management.html adresine bakın
Holger Böhnke

22

Önceki yoruma göre, üye değişkeni olarak başka bir nesneniz varsa aşağıdakileri yapın:

class MyClass {
  private $someObject;

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

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

}

Şimdi klonlama yapabilirsiniz:

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


4

PHP'nin yazma üzerine kopya kullandığını açıklığa kavuşturmak için, temelde her şeyi değiştirene kadar bir referanstır, ancak nesneler için klon ve kabul edilen yanıttaki gibi __clone () sihirli yöntemini kullanmanız gerekir.


1

Bu kod klonlama yöntemlerine yardımcı olur

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());

Bu kod biraz işe yaramaz, __clone yöntemini kaldırsanız bile işe yarayacak :)
amik

1

Bazı testler yapıyordum ve bunu aldım:

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

Bu örnekte, iPhone sınıfını oluşturacağız ve klonlayarak tam kopyasını yapacağız

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

Bir nesnenin özelliklerini farklı bir örnekte tam olarak kopyalamak istiyorsanız, bu tekniği kullanmak isteyebilirsiniz:

JSON için seri hale getirin ve sonra tekrar Object nesnesine serileştirin.


7
Hmm cehennemden kaçınırdım.
Jimmy Kane
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.