How do I create a copy of an object in PHP?
It appears that in PHP objects are passed by reference. Even assignment operators do not appear to be creating a copy of the Object. Here’s a simple, contrived proof:
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 both print cases I am getting ‘after’ So, how do I pass $a to set_b() by value, not by reference?
There are very few cases, where you would actually want this behaviour. So if you find your self using it often, then perhaps there is something more fundamental wrong with the way you write your code?
(object) ((array) $objectA) might result you same desired results with better performance then using clone $objectA or new stdClass .
Re «Even assignment operators do not appear to be creating a copy of the Object.» — I should hope not! If they did, the result would no longer be an OO language (for all practical purposes).
9 Answers 9
In PHP 5+ objects are passed by reference. In PHP 4 they are passed by value (that’s why it had runtime pass by reference, which became deprecated).
You can use the ‘clone’ operator in PHP5 to copy objects:
Also, it’s just objects that are passed by reference, not everything as you’ve said in your question.
Just want to add to anyone who is reading this, that cloning will keep reference to the original object. Running MySQL queries using the cloned object may have unpredictable results because of this, as execution may not take place in a linear fashion.
To correct a common misconception (I think even the PHP docs get it wrong!) PHP 5’s objects are not «passed by reference». As in Java, they have an additional level of indirection — the variable points to an «object pointer», and that points to an object. Thus two variables can point to the same object without being references to the same value. This can be seen from this example: $a = new stdClass; $b =& $a; $a = 42; var_export($b); here $b is a reference to the variable $a ; if you replace =& with a normal = , it is not a reference, and still points to the original object.
Runtime pass by reference is a bad idea, because it makes the effect of a function call depend on the implementation of the function, rather than on the specification. It’s got nothing to do with pass by value being the default.
@Alex Can you elaborate on your comment? (Either here or elsewhere.) Your point comes off a bit unclear IMO.
@Ælex — re «cloning will keep reference to original object» — More precisely, cloning does a shallow copy of the object’s properties. If the object contains other objects, then those nested objects will now have two references to them. Sometimes this is exactly what is wanted, sometimes it is a problem. To solve this, the object class needs to implement __clone() , as seen in Stanislav’s answer, and correct each field as needed.
The answers are commonly found in Java books.
- cloning: If you don’t override clone method, the default behavior is shallow copy. If your objects have only primitive member variables, it’s totally ok. But in a typeless language with another object as member variables, it’s a headache.
- serialization/deserialization
This achieves deep copy with a heavy cost depending on the complexity of the object.
+1 great, great, great way to do a DEEP copy in PHP, very easy too. Let me instead ask you something about the standard shallow copy offered by PHP clone keyword, you said that only primitive member variables gets copied: are PHP arrays/strings considered primitive member variables, so they get copied, am I right?
For anyone picking this up: a «shallow» copy ( $a = clone $b , with no magic __clone() methods in play) is equivalent to looking at each of the properties of object $b in term, and assigning to the same property in a new member of the same class, using = . Properties that are objects won’t get clone d, nor will objects inside an array; the same goes for variables bound by reference; everything else is just a value, and gets copied just like with any assignment.
Perfect! json_decode(json_encode($obj)); not clone private/protected properties and any method. unserialize(serialize not clone methods too.
This adds a lot of runtime overhead. You are serializing the object into a string and then parsing that string back into a new variable. While this does what you intend to do, it does it in an awfully slow way. Not only are you converting all your object into a string and back, using time and memory, you also break PHPs possible CopyOnWrite mechanism. A much better way is to implement your __clone method properly as suggested by stackoverflow.com/a/186191/1614903 below. See phpinternalsbook.com/php5/zvals/memory_management.html for a indepth explanation
According to the previous comment, if you have another object as a member variable, do the following:
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;
According to the doc, it only clones attributes which are not objects, for object attributes il only copy by reference. Which means its not a deep copy.
Just to clarify PHP uses copy on write, so basically everything is a reference until you modify it, but for objects you need to use clone and the __clone() magic method like in the accepted answer.
This code help clone methods
class Foo < private $run=10; public $foo=array(2,array(2,8)); public function hoo()public function __clone()< $this->boo=function()hoo();>; > > $obj=new Foo; $news= clone $obj; var_dump($news->hoo());
I was doing some testing and got this:
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" > ?>
Оператор присваивания
Базовый оператор присваивания обозначается как «=». На первый взгляд может показаться, что это оператор «равно». На самом деле это не так. В действительности, оператор присваивания означает, что левый операнд получает значение правого выражения, (т.е. устанавливается значением).
Результатом выполнения оператора присваивания является само присвоенное значение. Таким образом, результат выполнения «$a = 3» будет равен 3. Это позволяет делать трюки наподобие:
$a = ( $b = 4 ) + 5 ; // $a теперь равно 9, а $b было присвоено 4.
Для массивов ( array ), присвоение значения именованному ключу происходит с помощью оператора «=>». Приоритет этого оператора такой же, как и у остальных операторов присваивания.
В дополнение к базовому оператору присваивания имеются «комбинированные операторы» для всех бинарных арифметическихопераций, операций объединения массивов и строковых операций, которые позволяют использовать некоторое значение в выражении, а затем установить его как результат данного выражения. Например:
$a = 3 ;
$a += 5 ; // устанавливает $a в 8, как если бы мы написали: $a = $a + 5;
$b = «Hello » ;
$b .= «There!» ; // устанавливает $b в «Hello There!», как и $b = $b . «There!»;
Обратите внимание, что присвоение копирует оригинальную переменную в новую (присвоение по значению), таким образом все последующие изменения одной из переменных никак не отразятся на другой. Это также следует учитывать, если вам надо скопировать что-то типа большого массива в длинном цикле.
Исключением из обычного для PHP способа присваивания по значению являются объекты ( object ), которые, начиная с версии PHP 5, присваиваются по ссылке. Принудительно скопировать объекты по значению можно с помощью специального ключевого слова clone.
Присваивание по ссылке
Присваивание по ссылке также поддерживается, для него используется синтаксис $var = &$othervar; . ‘Присваивание по ссылке’ означает, что обе переменные указывают на одни и те же данные и никакого копирования не происходит.
Пример #1 Присваивание по ссылке
print » $a \n» ; // печатает 3
print » $b \n» ; // печатает 3
print » $a \n» ; // печатает 4
print » $b \n» ; // также печатает 4, так как $b является ссылкой на $a,
// а значение переменной $a успело измениться
?>
Начиная с версии PHP 5, оператор new автоматически возвращает ссылку, поэтому присваивание результата операции new по ссылке начиная с версии PHP 5.3 генерирует ошибку уровня E_DEPRECATED , а в более ранних версиях — ошибку уровня E_STRICT .
Например, следующий код выдаст предупреждение:
/* Следующая строка сгенерирует следующее сообщение об ошибке:
* Deprecated: Assigning the return value of new by reference is deprecated in.
* (Устаревший код: Присвоение результата работы new по ссылке устарело в. )
*/
$o = &new C ;
?>
Для получения более полной информации о ссылках и их возможностях обратитесь к разделу Подробно о ссылках.