214

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?

4
  • 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? Commented Oct 9, 2008 at 8:03
  • 1
    Nope, haven't needed to use it yet. Commented Oct 9, 2008 at 15:10
  • (object) ((array) $objectA) might result you same desired results with better performance then using clone $objectA or new stdClass. Commented Apr 2, 2019 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). Commented Jul 21, 2020 at 20:43

9 Answers 9

352

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

Sign up to request clarification or add additional context in comments.

9 Comments

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

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.

7 Comments

+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 cloned, 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...
Awesome! I finally get rid of PhpStorm's error; Call to method __clone from invalid context :)
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
|
30

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;

Comments

11

According to the docs (https://www.php.net/language.oop5.cloning):

$a = clone $b;

1 Comment

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

Comments

2

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', '[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>";

1 Comment

It's worth to mention that the clone performs a shallow cloning operation. That is all the properties of the object that are passed by value, and if there's a property that represents another object, it becomes not available in the cloned entity, unless specifically handled, e.g. via method __clone.
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());

1 Comment

this code is a bit useless, it would work even if you remove the __clone method :)
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" }

?>

Comments

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

2 Comments

Hmm I would avoid it like hell.
@JimmyKane Would you care to explain why?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.