Cloning an array of objects

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Cloning an array of objects

Post by Ollie Saunders »

Am I right in thinking that the only way to clone an array of objects it to recursively go through it finding objects and cloning them.

Code: Select all

$c = array(new stdClass(), new stdClass());
$d = clone $c;
Produces

Code: Select all

Warning: __clone method called on non-object
But why?! What were they thinking?
Last edited by Ollie Saunders on Mon Dec 04, 2006 10:02 am, edited 1 time in total.
User avatar
volka
DevNet Evangelist
Posts: 8391
Joined: Tue May 07, 2002 9:48 am
Location: Berlin, ger

Post by volka »

php is not java. And Array is a type not a class, see http://de2.php.net/language.types.array
;)
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

PHP is not OO on the underbelly such as it's primitive types like array. There is ArrayObject in SPL but it doesn't work like an ordinary array.
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

cmon volka! its not some php n00b you are talking to here.
d11wtq wrote:PHP is not OO on the underbelly such as it's primitive types like array. There is ArrayObject in SPL but it doesn't work like an ordinary array.
If they can make it so it triggers an error when you use clone on a array then they could have it return the array with cloned objects. I personally feel its a hole in the functionality of the language.
User avatar
volka
DevNet Evangelist
Posts: 8391
Joined: Tue May 07, 2002 9:48 am
Location: Berlin, ger

Post by volka »

ole wrote:
cmon volka! its not some php n00b you are talking to here.
The manual is not only for n00bs.
d11wtq wrote:If they can make it so it triggers an error when you use clone on a array then they could have it return the array with cloned objects. I personally feel its a hole in the functionality of the language.
It's a simple non-object trigger and has nothing more to do with passing an array to clone than passing a number or a resource, you'll get the same message. Does that mean there must also be extra code for cloining numbers and resources? ;)
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

The manual is not only for n00bs.
The bit on the array type is.
It's a simple non-object trigger and has nothing more to do with passing an array to clone than passing a number or a resource, you'll get the same message. Does that mean there must also be extra code for cloining numbers and resources?
Yes you are probably right but does that mean that cloning objects in arrays directly clone isn't doable? I seriously doubt it.
User avatar
volka
DevNet Evangelist
Posts: 8391
Joined: Tue May 07, 2002 9:48 am
Location: Berlin, ger

Post by volka »

Directly? No. But you might use array_map
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

I don't think array_map() will accept "clone" as a callback. There's always foreach...
User avatar
volka
DevNet Evangelist
Posts: 8391
Joined: Tue May 07, 2002 9:48 am
Location: Berlin, ger

Post by volka »

it will not, but you can always wrap a function around it. e.g.

Code: Select all

<?php
class MyClass {
	public function __clone() {
		echo 'cloning';
	}
}

function fnclone($a) {
	if (is_object($a))
		return clone $a;
	else
		return null;
}

$arr = array(new MyClass, new MyClass);
$a2 = array_map('fnclone', $arr);
?>
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

Code: Select all

/**
 * Recursively iterates over an array cloning any objects encountered
 *
 * @param array $toClone
 * @return array
 */
static public function arrayRecursiveClone(array $toClone)
{
    foreach ($toClone as $k => $v) {
        if (is_object($v)) {
            $toClone[$k] = clone $v;
            continue;
        }
        if (is_array($v)) {
            $toClone[$k] = self::arrayRecursiveClone($v);
        }
    }
    return $toClone;
}

Code: Select all

public function testArrayRecursiveClone()
{
    $zim = new stdClass();
    $zim->gir = 10;
    $foo = array(true, 10, array('string', array($zim)), 'string');
    $bar = OF::arrayRecursiveClone($foo);
    $zim->gir = 20;
    $this->assertNotIdentical($bar[2][1][0], $zim);
    $this->assertEqual($bar[2][1][0]->gir, 10);
}
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Since when can you type hint for arrays?

Code: Select all

static public function arrayRecursiveClone(array $toClone)
EDIT: The manual answers my question... wow I could have sworn type hinting only worked to test objects... hmm...
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

Yep, learnt that one by reading the change log.
User avatar
volka
DevNet Evangelist
Posts: 8391
Joined: Tue May 07, 2002 9:48 am
Location: Berlin, ger

Post by volka »

But why limiting it to an array? Now I have to put extra-code like

Code: Select all

if ( is_object($o) ) {
	$o2 = clone $o;
}
else {
	$o2 = arrayRecursiveClone($o);
}
I find it easier to have something like

Code: Select all

<?php
class MyClass {
	private $prop;
	public function __construct() { $this->prop = rand(1,99); }
	public function __clone() {	echo 'cloning '. $this->prop . "\n";	}
} 

function clonex($toClone)
{
	if (is_object($toClone)) {
		return clone $toClone;
	}
	else if (is_array($toClone)) {
		return array_map('clonex', $toClone);
	}
	else if (is_null($toClone)) {
		return null;
	}
	else {
		trigger_error('clonex: cloning not implemented for type:'.gettype($toClone), E_USER_WARNING);
		return $toClone;
	}
}

$arr = array(new MyClass, array(new MyClass, new MyClass), 3);
print_r($arr);
$arr2 = clonex($arr);
?>
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

ole wrote:Yep, learnt that one by reading the change log.
Is this a PHP 5.2 feature? 8O :D

EDIT | Reading the manual helps :P Ok, so if they do it for arrays and classes surely they can do it for strings and the numeric types.
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

But why limiting it to an array?
Because you can't clone primitives and you can already clone objects with the reserved word. You may have need for a handle-all cloner, I suppose, but my situation didn't require it.
Post Reply