Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep copy of PHP array of references

Tags:

php

deep-copy

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?

like image 377
Chad Avatar asked May 21 '09 19:05

Chad


People also ask

How do I make a copy of an array in PHP?

The getArrayCopy() function of the ArrayObject class in PHP is used to create a copy of this ArrayObject. This function returns the copy of the array present in this ArrayObject.

Does PHP pass array by reference?

With regards to your first question, the array is passed by reference UNLESS it is modified within the method / function you're calling. If you attempt to modify the array within the method / function, a copy of it is made first, and then only the copy is modified.

What are the 3 types of PHP arrays?

In PHP, there are three types of arrays: Indexed arrays - Arrays with a numeric index. Associative arrays - Arrays with named keys. Multidimensional arrays - Arrays containing one or more arrays.


1 Answers

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);
like image 93
Lepidosteus Avatar answered Oct 20 '22 18:10

Lepidosteus