Page 1 of 2
Cloning an array of objects
Posted: Mon Dec 04, 2006 7:29 am
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?
Posted: Mon Dec 04, 2006 7:31 am
by volka
php is not java. And
Array is a type not a class, see
http://de2.php.net/language.types.array

Posted: Mon Dec 04, 2006 8:06 am
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.
Posted: Mon Dec 04, 2006 10:07 am
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.
Posted: Mon Dec 04, 2006 10:34 am
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?

Posted: Mon Dec 04, 2006 11:09 am
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.
Posted: Mon Dec 04, 2006 11:46 am
by volka
Directly? No. But you might use array_map
Posted: Mon Dec 04, 2006 12:49 pm
by feyd
I don't think array_map() will accept "clone" as a callback. There's always foreach...
Posted: Mon Dec 04, 2006 6:20 pm
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);
?>
Posted: Mon Dec 04, 2006 7:25 pm
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);
}
Posted: Mon Dec 04, 2006 7:53 pm
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...
Posted: Mon Dec 04, 2006 8:06 pm
by Ollie Saunders
Yep, learnt that one by reading the change log.
Posted: Mon Dec 04, 2006 8:56 pm
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);
?>
Posted: Tue Dec 05, 2006 1:01 am
by Chris Corbyn
ole wrote:Yep, learnt that one by reading the change log.
Is this a PHP 5.2 feature?
EDIT | Reading the manual helps

Ok, so if they do it for arrays and classes surely they can do it for strings and the numeric types.
Posted: Tue Dec 05, 2006 4:52 am
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.