205

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:

<?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 both print cases I am getting 'after'

So, how do I pass $a to set_b() by value, not by reference?

tereško
  • 58,060
  • 25
  • 98
  • 150
Nick Stinemates
  • 41,511
  • 21
  • 59
  • 60
  • 3
    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? – troelskn Oct 09 '08 at 08:03
  • 1
    Nope, haven't needed to use it yet. – Nick Stinemates Oct 09 '08 at 15:10
  • `(object) ((array) $objectA)` might result you same desired results with better performance then using `clone $objectA` or `new stdClass`. – Binyamin Apr 02 '19 at 23:17
  • 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). – ToolmakerSteve Jul 21 '20 at 20:43

9 Answers9

332

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:

$objectB = clone $objectA;

Also, it's just objects that are passed by reference, not everything as you've said in your question...

Eran Galperin
  • 86,251
  • 24
  • 115
  • 132
  • 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. – Ælex Mar 26 '13 at 16:30
  • 22
    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. – IMSoP Jun 16 '13 at 21:14
  • 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. – Oswald Oct 04 '13 at 07:06
  • 1
    @Alex Can you elaborate on your comment? (Either here or elsewhere.) Your point comes off a bit unclear IMO. – Chris Middleton Apr 26 '15 at 03:11
  • @ChrisMiddleton Think of terms of C or C++: if you have cloned a reference to an object that is free'd, out of scope or released, then your cloned reference is invalidated. Thus you *may* get undefined behavior depending on what happened with the original object, to which you hold a reference via cloning. – Ælex Apr 27 '15 at 17:55
  • @Alex Have you tested that? I just checked returning a clone from a function (where the original goes out of scope and had no issues). `php -r 'class Foo {} function getFoo () { $f = new Foo(); $f->bar = 5; return clone $f; } $f = getFoo(); var_dump($f);'` Are you sure you aren't thinking of assignment by reference (`$f2 = &$f1`)? – Chris Middleton Apr 27 '15 at 18:41
  • @ChrisMiddleton I tested this a long time ago (PHP 5.4) on an ORM class, and found that it depends on the class implementation. Clone seems to do a shallow copy, so if you're working with something simple you would be ok. I'm afraid I can't be of much help, as it has been more than 2 years since I run into this issue. Objects in PHP are essentially pointers, so if the original object is released, then you run into this issue mentioned before. – Ælex Apr 27 '15 at 18:45
  • Friend is getting PHP parse error when doing it like: `$new_date = (clone $date_start)->subDays(1);` It fails with the `()`, if I remove them I get a different error. The thing is, we using the exact same php 7.2.3 and mine works fine. Any ideas? Searched everywhere.. – emotality Mar 28 '18 at 14:33
  • 1
    @Æ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. – ToolmakerSteve Jul 21 '20 at 20:49
121

The answers are commonly found in Java books.

  1. 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.

  2. serialization/deserialization

$new_object = unserialize(serialize($your_object))

This achieves deep copy with a heavy cost depending on the complexity of the object.

kamal pal
  • 4,187
  • 5
  • 25
  • 40
yogman
  • 4,021
  • 4
  • 24
  • 27
  • 4
    +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? – Marco Demaio Jul 11 '10 at 10:48
  • 4
    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. – IMSoP Jun 16 '13 at 21:10
  • 3
    Perfect! json_decode(json_encode($obj)); not clone private/protected properties and any method... unserialize(serialize not clone methods too... – zloctb Oct 03 '13 at 20:15
  • Awesome! I finally get rid of PhpStorm's error; `Call to method __clone from invalid context` :) – numediaweb Sep 07 '15 at 08:33
  • Friend is getting PHP parse error when doing it like: `$new_date = (clone $date_start)->subDays(1);` It fails with the `()`, if I remove them I get a different error. The thing is, we using the exact same php 7.2.3 and mine works fine. Any ideas? Searched everywhere.. – emotality Mar 28 '18 at 14:33
  • A co-worker and I kept running into this. The cloning didn't work really well on a more advanced object (kept having issues with it updating the wrong item in memory) but the unserialize/serialize does. :) Thanks for that save! – cbloss793 Jun 03 '19 at 19:43
  • 2
    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 https://stackoverflow.com/a/186191/1614903 below. See http://www.phpinternalsbook.com/php5/zvals/memory_management.html for a indepth explanation – Holger Böhnke Jan 20 '20 at 17:43
29

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

}

Now you can do cloning:

$bar = new MyClass();
$foo = clone $bar;
Srdjan Pazin
  • 103
  • 2
  • 5
Stanislav
  • 2,683
  • 2
  • 18
  • 14
12

According to the docs (http://ca3.php.net/language.oop5.cloning):

$a = clone $b;
rink.attendant.6
  • 44,500
  • 61
  • 101
  • 156
Leo
  • 2,860
  • 3
  • 24
  • 21
  • 1
    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. – Bruno Guignard Jan 28 '21 at 15:46
5

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.

Patricio Rossi
  • 167
  • 2
  • 5
1

This code help clone methods

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
  • 10,592
  • 8
  • 70
  • 89
1

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

?>
Pyetro
  • 155
  • 1
  • 8
1

In this example we will create iPhone class and make exact copy from it by cloning

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>";
reformed
  • 4,505
  • 11
  • 62
  • 88
-2

If you want to fully copy properties of an object in a different instance, you may want to use this technique:

Serialize it to JSON and then de-serialize it back to Object.

Flexo
  • 87,323
  • 22
  • 191
  • 272
diy_nunez
  • 97
  • 1
  • 7