¿Cómo creo una copia de un objeto en PHP?


168

Parece que en PHP los objetos se pasan por referencia. Incluso los operadores de asignación no parecen estar creando una copia del objeto.

Aquí hay una prueba simple y artificial:

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

?>

En ambos casos impresos me sale 'after'

Entonces, ¿cómo paso $ a a set_b () por valor, no por referencia?


2
Hay muy pocos casos en los que realmente desearía este comportamiento. Entonces, si te encuentras usándolo a menudo, ¿tal vez hay algo más fundamental en la forma en que escribes tu código?
troelskn

1
No, no he necesitado usarlo todavía.
Nick Stinemates

(object) ((array) $objectA)podría obtener los mismos resultados deseados con un mejor rendimiento que usar clone $objectAo new stdClass.
Binyamin

Respuestas:


284

En PHP 5+ los objetos se pasan por referencia. En PHP 4 se pasan por valor (es por eso que tenía tiempo de ejecución por referencia, que quedó obsoleto).

Puede usar el operador 'clonar' en PHP5 para copiar objetos:

$objectB = clone $objectA;

Además, son solo objetos que se pasan por referencia, no todo como has dicho en tu pregunta ...


Solo quiero agregar a cualquiera que esté leyendo esto, que la clonación mantendrá referencia al objeto original. La ejecución de consultas MySQL utilizando el objeto clonado puede tener resultados impredecibles debido a esto, ya que la ejecución puede no tener lugar de manera lineal.
Ælex

20
Para corregir un error común (¡creo que incluso los documentos de PHP se equivocan!) Los objetos de PHP 5 no se "pasan por referencia". Al igual que en Java, tienen un nivel adicional de indirección: la variable apunta a un "puntero de objeto" y eso apunta a un objeto. Por lo tanto, dos variables pueden apuntar al mismo objeto sin ser referencias al mismo valor. Esto se puede ver en este ejemplo: $a = new stdClass; $b =& $a; $a = 42; var_export($b);aquí $bhay una referencia a la variable $a ; si se reemplaza =&con un normales =, es no una referencia, y aún apunta al objeto original.
IMSoP

El tiempo de ejecución por referencia es una mala idea, porque hace que el efecto de una llamada a función dependa de la implementación de la función, en lugar de la especificación. No tiene nada que ver con pasar por valor siendo el valor predeterminado.
Oswald

1
@ Alex ¿Puedes dar más detalles sobre tu comentario? (Ya sea aquí o en otro lugar). Su punto se desprende un poco confuso de la OMI.
Chris Middleton

@ChrisMiddleton Piense en los términos de C o C ++: si ha clonado una referencia a un objeto que está libre, fuera de alcance o liberado, su referencia clonada se invalida. Por lo tanto, puede obtener un comportamiento indefinido dependiendo de lo que sucedió con el objeto original, al que tiene una referencia a través de la clonación.
Ælex

103

Las respuestas se encuentran comúnmente en los libros de Java.

  1. clonación: si no anula el método de clonación, el comportamiento predeterminado es una copia superficial. Si sus objetos solo tienen variables miembro primitivas, está totalmente bien. Pero en un lenguaje sin tipo con otro objeto como variables miembro, es un dolor de cabeza.

  2. serialización / deserialización

$new_object = unserialize(serialize($your_object))

Esto logra una copia profunda con un alto costo dependiendo de la complejidad del objeto.


44
+1 excelente, excelente, excelente manera de hacer una copia PROFUNDA en PHP, muy fácil también. Permítame preguntarle algo sobre la copia superficial estándar ofrecida por la palabra clave de clonación PHP, dijo que solo se copian las variables miembro primitivas: ¿las matrices / cadenas PHP se consideran variables miembro primitivas, por lo que se copian, estoy en lo cierto?
Marco Demaio

3
Para cualquiera que tome esto: una copia "superficial" ( $a = clone $bsin __clone()métodos mágicos en juego) es equivalente a mirar cada una de las propiedades del objeto $ben términos y asignar a la misma propiedad en un nuevo miembro de la misma clase, usando =. Las propiedades que son objetos no obtendrán cloned, ni los objetos dentro de una matriz; lo mismo ocurre con las variables vinculadas por referencia; todo lo demás es solo un valor, y se copia como con cualquier tarea.
IMSoP

3
¡Perfecto! json_decode (json_encode ($ obj)); no clonar propiedades privadas / protegidas y cualquier método ... deserializar (serializar métodos no clonar también ...
zloctb

¡Increíble! Finalmente me deshago del error de PhpStorm; Call to method __clone from invalid context:)
numediaweb

Friend está recibiendo un error de análisis de PHP cuando lo hace así: $new_date = (clone $date_start)->subDays(1);falla con el (), si los elimino me sale un error diferente. La cuestión es que usamos exactamente el mismo php 7.2.3 y el mío funciona bien. ¿Algunas ideas? Buscado en todas partes ..
emotality

21

Según el comentario anterior, si tiene otro objeto como variable miembro, haga lo siguiente:

class MyClass {
  private $someObject;

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

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

}

Ahora puedes hacer clonación:

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


4

Solo para aclarar que PHP usa copia al escribir, por lo que básicamente todo es una referencia hasta que lo modifique, pero para los objetos debe usar clone y el método mágico __clone () como en la respuesta aceptada.


1

Este código ayuda a clonar métodos

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

este código es un poco inútil, funcionaría incluso si elimina el método __clone :)
amik

1

Estaba haciendo algunas pruebas y obtuve esto:

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

En este ejemplo crearemos una clase de iPhone y haremos una copia exacta de ella clonando

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

Si desea copiar completamente las propiedades de un objeto en una instancia diferente, puede utilizar esta técnica:

Serialízalo a JSON y luego desconéctalo a Object.


77
Hmm, lo evitaría como el infierno.
Jimmy Kane
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.