17

So $array is an array of which all elements are references.

I want to append this array to another array called $results (in a loop), but since they are references, PHP copies the references and $results is full of identical elements.

So far, the best working solution is:

$results[] = unserialize(serialize($array));

which I fear to be incredibly inefficient. Is there a better way to do this?

AstroCB
  • 12,337
  • 20
  • 57
  • 73
Chad
  • 2,064
  • 1
  • 22
  • 29

2 Answers2

9

You can use the fact that functions dereferences results when returning, for exemple here $array_by_myclone will still have a reference to $original ($array_by_myclone[0][0] == 'foo') while $array_by_assignment will have a cloned value ($array_by_assignment[0][0] == 'bar')

$original = 'foo';
$array_of_reference = array(&$original);

function myclone($value)
{
  return $value;
}

$array_by_myclone = array();
$array_by_myclone[] = array_map('myclone', $array_of_reference);

$array_by_assignment = array();
$array_by_assignment[] = $array_of_reference;

$original = 'bar';

var_dump($array_by_myclone[0][0]); // foo, values were cloned                                                                                                                                   
var_dump($array_by_assignment[0][0]); // bar, still a reference                     

EDIT: I wanted to check if the comment saying unserialize(serialize()) was faster was right so I did the test using php 5.5, and it turns out this is wrong: using the serialization method is slower even with a small dataset, and the more data you have the slower it gets.

lepidosteus@server:~$ php -v
PHP 5.5.1-1~dotdeb.1 (cli) (built: Aug  3 2013 22:19:30) 
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies
    with Zend OPcache v7.0.2-dev, Copyright (c) 1999-2013, by Zend Technologies
lepidosteus@server:~$ php reference.php 1
myclone:   0.000010 seconds
serialize: 0.000012 seconds
lepidosteus@server:~$ php reference.php 1000000
myclone:   0.398540 seconds
serialize: 0.706631 seconds

Code used:

<?php
$iterations = 1000000;
if (isset($argv[1]) && is_numeric($argv[1])) {
  $iterations = max(1, (int)$argv[1]);
}

$items = array();
for ($i = 0; $i < $iterations; $i++) {
  $items[] = 'item number '.$i;
}

$array_of_refs = array();
foreach ($items as $k => $v) {
  $array_of_refs[] = &$items[$k];
}

function myclone($value)
{
  return $value;
}

$start = microtime(true);

$copy = array_map('myclone', $array_of_refs);

$time = microtime(true) - $start;

printf("%-10s %2.6f seconds\n", 'myclone:', $time);

$start = microtime(true);

$copy = unserialize(serialize($array_of_refs));

$time = microtime(true) - $start;

printf("%-10s %2.6f seconds\n", 'serialize:', $time);
Lepidosteus
  • 11,779
  • 4
  • 39
  • 51
  • 7
    I did a bit of benchmarking and unserialize(serialize()) is slightly faster – Chad May 21 '09 at 21:04
  • 4
    It's highly doubtful that serialize is faster than array_map. Maybe if your array is only one element that is a few chars long. – Daniel Beardsley Nov 08 '12 at 11:13
  • 1
    @DanielBeardsley It's probably overhead in calling the `myclone` function instead of being entirely internal like serialize/unserialize. – Izkata Aug 16 '13 at 01:12
  • After all that time I finally did a benchmark and it turns out serialize is slower, not faster like the first comment says. Code and results added in the answer. – Lepidosteus Sep 25 '13 at 19:47
0

no need to compare array_map with serialize cause array_map is not useful.

$original = array('key'=>'foo');
$array_of_reference = array(&$original);
function myclone($value)
{
  return $value;
}
$array_by_myclone = array();
$array_by_myclone[] = array_map('myclone', $array_of_reference);

$array_by_assignment = array();
$array_by_assignment[] = $array_of_reference;

$original['key'] = 'bar';

var_dump($array_by_myclone[0][0]['key']); // bar, still a reference                                                                                                                                   
var_dump($array_by_assignment[0][0]['key']); // bar, still a reference   

array_map Applies the callback to the elements of the given arrays, just like foreach. if the array you want to copy has more than 1 nest, array_map does not work.

Sky Ye
  • 31
  • 2