19

I have the following code:

$data['x'] = $this->x->getResults();  

$data['y'] = $data['x'];

//some code here to modify $data['y']
//this causes (undesirably) $data['x] to be modified as well

I guess since all the elements of $data are themselves references, modifying $data['y'] also modifies $data['x']..which is NOT what I want. I want $data['x'] to remain the same. Is there any way to dereference the elements here so that I can copy the elements by value?

Thanks.

Update: $this->x->getResults(); returns an object array. So I can then do something like: $data['x'][0]->date_create ...

Update: my latest attempt to clone the array looks something like this:

   $data['x'] = $this->x->getResults();     
   $data['y'] = $data['y'];
   foreach($data['x'] as $key=>$row) {
       $data['y'][$key]->some_attr = clone $row->some_attr;
   }

Am I way off here? I keep getting a "__clone method called on non-object" error. From reading the responses it seems like my best option is to iterate over each element and clone it (which is what I was trying to do with that code..).

UPDATE: Just solved it!: inside the foreach loop I just needed to change the line to:

$data['y'][$key] = clone $row;

And it works! Thanks to everyone for the help.

oym
  • 6,983
  • 16
  • 62
  • 88
  • What is `$this->x->getResults()` returning? An object? – Ionuț G. Stan Jul 27 '09 at 19:13
  • It would be important to know what `$this->x->getResults()` returns... – jason Jul 27 '09 at 19:14
  • yes, I am using codeigniter so that is a call to a model (x) which returns an object array of database query results – oym Jul 27 '09 at 19:14
  • Then you must clone every object in that array of objects. – Ionuț G. Stan Jul 27 '09 at 19:35
  • 1
    When writing your own classes, internal variable references are not cloned by default. You must implement the clone function, eg: `public function __clone() { $this->widget = clone $this->widget(); }` for each reference inside your object that should be cloned as well. – ReactiveRaven Jul 15 '11 at 09:34

7 Answers7

11

You can take advantage of the fact that PHP will dereference the results of a function call.

Here's some example code I whipped up:

$x = 'x';
$y = 'y';
$arr = array(&$x,&$y);
print_r($arr);

echo "<br/>";
$arr2 = $arr;
$arr2[0] = 'zzz';
print_r($arr);
print_r($arr2);

echo "<br/>";
$arr2 = array_flip(array_flip($arr));
$arr2[0] = '123';
print_r($arr);
print_r($arr2);

The results look like this:

Array ( [0] => x [1] => y )
Array ( [0] => zzz [1] => y ) Array ( [0] => zzz [1] => y )
Array ( [0] => zzz [1] => y ) Array ( [0] => 123 [1] => y ) 

You can see that the results of using array_flip() during the assigment of $arr to $arr2 results in differences in the subsequent changes to $arr2, as the array_flip() calls forces a dereference.

It doesn't seem terribly efficient, but it might work for you if $this->x->getResults() is returning an array:

$data['x'] = array_flip(array_flip($this->x->getResults()));
$data['y'] = $data['x'];

See this (unanswered) thread for another example.

If everything in your returned array is an object however, then the only way to copy an object is to use clone(), and you would have to iterate through $data['x'] and clone each element into $data['y'].

Example:

$data['x'] = $this->x->getResults();
$data['y'] = array();
foreach($data['x'] as $key => $obj) {
    $data['y'][$key] = clone $obj;
}
Community
  • 1
  • 1
zombat
  • 92,731
  • 24
  • 156
  • 164
  • I am having some trouble with this: like you mentioned in your last sentence, everything in my returned array is an object..could you maybe post some code showing how to iterate over the array and clone each element? I keep getting a "_clone method called on non-object" error. Thanks. – oym Jul 27 '09 at 22:19
  • No problem, I added an example. `clone` only works on actual objects, so if the return result of `$this->x->getResults();` is an array of objects instead of an object, then you can't clone the array, you have to clone each element of the array separately. – zombat Jul 27 '09 at 22:26
  • 1
    `array_flip` is not a good idea!! If you have 2 keys with the same value then the first key will be lost when the `array_flip` function is called. – Mihai Matei Jul 20 '14 at 14:01
  • a bit of mis-information here. the function return doesn't do a deep-dereferencing. the magic above is the `array_flip(array_flip($array))`.. – Brad Kent Dec 09 '16 at 22:56
9

array_merge() can accept any number of parameters, even 1, then produce a new array. So just do following:

$new_array = array_merge($existing_array);
6

array_flip() won't work when array values are not strings nor integers. I found a simple solution, however:

$clonedArr = (array)clone(object)$arr;

This works thanks to the properties of clone on an object.

  • 5
    This does not work. If `$arr` has reference variables in it, the new array will still have them. References survive cloning. – y o Jan 02 '17 at 22:44
3

Not simple. Read about clone

BUT! if your elements are not objects and not refence type variables you have no problem.

Example for reference types:

$v=11;
$arr[]=&$v;
Itay Moav -Malimovka
  • 52,579
  • 61
  • 190
  • 278
  • why is it that I only have a problem if the elements are objects? is it because objects are themselves references? – oym Jul 27 '09 at 19:17
  • 1
    In PHP, (as of version 5 IICRC) all objects are passed by reference by default. – Mike B Jul 27 '09 at 19:18
  • 1
    This answer is not quite true. It should read "If your elements are not references, you have no problem.". You can have references that are not objects. – zombat Jul 27 '09 at 19:35
  • 1
    Objects aren't references, they're technically pointers, but they appear to act like references. – Halfstop Jan 27 '17 at 18:42
2

If you are working with objects, you might want to take a look at clone, to create a copy of an object, instead of a reference.

Here is a very short example :

First, with an array, it works by value :

$data['x'] = array(
    'a' => 'test',
    'b' => 'glop',
);
$data['y'] = $data['x'];
$data['y'][0] = 'Hello, world!';
var_dump($data['x']); // a => test : no problem with arrays

By default, with objects, it works by reference :

$data['x'] = (object)array(
    'a' => 'test',
    'b' => 'glop',
);
$data['y'] = $data['x'];
$data['y']->a = 'Hello, world!';
var_dump($data['x']); // a => Hello, world! : objects are by ref

But, if you clone the object, you work on a copy :
I guess this is your case ?

$data['x'] = (object)array(
    'a' => 'test',
    'b' => 'glop',
);
$data['y'] = clone $data['x'];
$data['y']->a = 'Hello, world!';
var_dump($data['x']); // a => test : no ref, because of cloning

Hope this helps,

Pascal MARTIN
  • 395,085
  • 80
  • 655
  • 663
  • thanks for the code sample..do you know if the clone function is relatively efficient? I ask because I know that in java it is usually best to avoid this if possible. – oym Jul 27 '09 at 19:19
  • I have no idea about the efficiency, but if you need the functionnality, it doesn't really matter, does it ? (and, considering you are doing a DB query just a while before, it shouldn't be that long, in comparison with the rest of the script :-) ) – Pascal MARTIN Jul 27 '09 at 19:24
  • I think the only inherent inefficiency would be due to the fact that you are doubling the amount of memory in order to store the same object twice. Thus, it is only as inefficient as copying any other variable would be. – njbair May 22 '11 at 03:12
1

I just discovered that if you simply want a copy of an array of values (no references) from a constant then you can just write:

$new_array = (array) (object) self::old_array;

Not an exact answer to the OP's question but it helped me and might help someone else.

Rex the Strange
  • 133
  • 1
  • 6
0

You could use this function to copy multidimensional arrays containing objects.

<?php
function arrayCopy( array $array ) {
    $result = array();
    foreach( $array as $key => $val ) {
        if( is_array( $val ) ) {
            $result[$key] = arrayCopy( $val );
        } elseif ( is_object( $val ) ) {
            $result[$key] = clone $val;
        } else {
            $result[$key] = $val;
        }
    }
    return $result;
}
?>
uKolka
  • 36,422
  • 4
  • 33
  • 44
  • Doesn't `clone` do a shallow copy? So it won't recurse to any array properties. – Mike Samuel May 05 '12 at 22:42
  • 2
    Yes it won't. So i guess if there are any array properties containing some more objects you could use `__clone()` to let the objects themselves figure out how exactly they should be cloned. – uKolka May 06 '12 at 10:41