是否存在将PHP数组复制到另一个数组的功能?


529

是否存在将PHP数组复制到另一个数组的功能?

我已经被烧了好几次了,试图复制PHP数组。我想将对象内部定义的数组复制到对象外部的全局数组。


真的很晚,但是在我的环境中,我对此进行了测试(并且成功了):function arrayCopy(array $ a){return $ a; } $ a1 = array(); for($ i = 0; $ i <3; $ i ++){$ a1 [“ key- $ i”] =“值#$ i”; } $ a1 [“ key-sub-array”] = array(1、2、3、4); $ a2 = $ a1; $ a3 = arrayCopy($ a1); for($ i = 0; $ i <3; $ i ++){如果(!is_array($ a2 [“ key- $ i”])){{一世”; }} $ a2 [“ key-sub-array”] = array(“ changed sub-array 1”,“ changed sub-array 2”); var_dump($ a1); var_dump($ a2); var_dump($ a3); 诀窍是,不要将数组作为引用传递给函数;-)
Sven

Answers:


926

在PHP中,数组是通过副本分配的,而对象是通过引用分配的。这意味着:

$a = array();
$b = $a;
$b['foo'] = 42;
var_dump($a);

将产生:

array(0) {
}

鉴于:

$a = new StdClass();
$b = $a;
$b->foo = 42;
var_dump($a);

产量:

object(stdClass)#1 (1) {
  ["foo"]=>
  int(42)
}

您可能会对诸如的复杂性感到困惑ArrayObject,该对象的行为完全类似于数组。但是,作为对象,它具有引用语义。

编辑:@AndrewLarsson在下面的评论中提出了一个观点。PHP具有一个称为“引用”的特殊功能。它们在某种程度上类似于C / C ++等语言中的指针,但并不完全相同。如果您的数组包含引用,则当数组本身通过副本传递时,引用仍将解析为原始目标。当然,通常这是期望的行为,但是我认为值得一提。


104
您没有回答问题。您只说明了问题。对于OP来说,这很可能是他一直在寻找的东西。但是,对我(以及其他人)而言,将近四年后又遇到了类似的问题,我仍然没有一种好方法来克隆数组而不修改原始数组(也包括内部指针)。我想是时候问我自己的问题了。
安德鲁·拉尔森

28
@AndrewLarsson但是PHP默认情况下会这样做-这就是要点。但是,引用无法解析,因此,如果需要解析,则必须递归遍历该数组并构建一个新的数组-同样,如果源数组包含对象,并且想要克隆这些对象,则必须手动进行操作。还要记住,在PHP中引用是一样C.指针不知道你的情况下,任何东西,我可能认为它很奇怪有在第一种情况下引用数组,特别是如果你不原意治疗他们作为参考?用例是什么?
troelskn

1
@troelskn我为此问题添加了答案,并提供了解决方案:stackoverflow.com/a/17729234/1134804
Andrew Larsson

3
但是,当它不是所需的行为时该怎么办呢?问题询问如何制作深层副本。显然是不希望的。您的答案没有比:更好$copy = $original;。如果数组元素是引用,则不起作用。
doug65536

8
一如既往地为php我们提供了最少的预期结果,因为此解决方案并不总是有效$a=array(); $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];在打印array0$a=$GLOBALS; $b=$a; $b["x"]=0; $c=$b; $b["x"]=1; echo gettype($b), $c["x"];打印array1。显然,某些数组是通过引用复制的。
蒂诺2014年

186

PHP将默认复制该数组。PHP中的引用必须明确。

$a = array(1,2);
$b = $a; // $b will be a different array
$c = &$a; // $c will be a reference to $a

如果数组很大,使用引用可能很重要。我不确定,但是我认为这样做可以减少内存消耗,提高性能(无需将整个数组复制到内存中)。
robsch

11
@robsch-在程序逻辑级别,复制数组。但是在内存中,除非对其进行修改,否则实际上不会被复制-因为PHP对所有类型都使用写时复制语义。stackoverflow.com/questions/11074970/…–
杰西卡·奈特

@CoreyKnight很高兴知道。这次真是万分感谢。
robsch

4
请注意,对于嵌套数组而言,这不是正确的,它们是引用,因此您会陷入混乱
MightyPork

45

如果您有一个包含对象的数组,则需要在不触摸其内部指针的情况下复制该数组,并且需要克隆所有对象(以便在对复制的对象进行更改时不会修改原始对象)数组),使用它。

不触及数组内部指针的诀窍是确保使用的是数组的副本,而不是原始数组(或对其的引用),因此使用function参数可以完成工作(因此,这是一个接受数组的函数)。

请注意,如果您还希望克隆对象的属性,则仍然需要在对象上实现__clone()

此函数适用于任何类型的数组(包括混合类型)。

function array_clone($array) {
    return array_map(function($element) {
        return ((is_array($element))
            ? array_clone($element)
            : ((is_object($element))
                ? clone $element
                : $element
            )
        );
    }, $array);
}

1
请记住,这是一个特例。另外,请注意,这只会克隆第一级引用。如果您有一个深层数组,则不会克隆较深的节点(如果它们是引用)。在您的情况下,可能不是问题,但请记住这一点。
troelskn

4
@troelskn我通过添加一些递归来修复它。现在,该函数可以在任何类型的数组上使用,包括混合类型。它也适用于简单数组,因此不再进行本地化。它基本上是一台通用的阵列克隆机。如果对象很深,您仍然需要在对象中定义__clone()函数,但这超出了该函数的“范围”(对不起的双关语很抱歉)。
安德鲁·拉尔森

2
我坚信这是该问题的实际答案,这是我看到的深度复制包含对象的数组的唯一方法。
Patrick

它不会迭代可能具有其他数组和引用对象的对象属性。
ya.teck

6
这种用法__FUNCTION__是出色的。
zessx

29

当你做

$array_x = $array_y;

PHP复制了数组,所以我不确定您会怎么被烧死。对你来说

global $foo;
$foo = $obj->bar;

应该工作正常。

为了被烧毁,我认为您要么必须一直使用引用,要么期望克隆数组中的对象。


12
为此+1:“或期望复制阵列中的对象”
Melsi 2013年


18

简单,使深度复制破坏所有链接

$new=unserialize(serialize($old));

4
通常它可以正常工作,但是在某些情况下它可能会引发异常,因为并非所有变量都可以序列化(例如,闭包和数据库连接)。
ya.teck

要注意的另一件事是,如果类实现__wakeup magic方法,则可以恢复对象引用。
ya.teck

谢谢,最后是真正有效的方法,而不是其他的bollock答案有很多反对意见,他们肯定没有处理有问题的对象数组,数组中的元素数量可能会发生变化,但是绝对不会引用该对象的引用。里面的物体
FentomX1

12

我喜欢array_replace(或array_replace_recursive)。

$cloned = array_replace([], $YOUR_ARRAY);

它的工作方式类似于Object.assignJavaScript。

$original = [ 'foo' => 'bar', 'fiz' => 'baz' ];

$cloned = array_replace([], $original);
$clonedWithReassignment = array_replace([], $original, ['foo' => 'changed']);
$clonedWithNewValues = array_replace([], $original, ['add' => 'new']);

$original['new'] = 'val';

将导致

// original: 
{"foo":"bar","fiz":"baz","new":"val"}
// cloned:   
{"foo":"bar","fiz":"baz"}
// cloned with reassignment:
{"foo":"changed","fiz":"baz"}
// cloned with new values:
{"foo":"bar","fiz":"baz","add":"new"}

1
array_slice($arr, 0)或者什么时候你不在乎钥匙,那array_values($arr)呢?我想他们可能比在数组中搜索要快。另外,在javascript中,Array.slice()克隆数组非常流行。
基督教徒

在JS中,我们有用于键值对的ObjectArray。PHP并没有改变。对于带有编号索引的PHP数组,array_slice这里提到的所有其他方法都可以很好地工作。但是,如果您想合并多个键值对(因为JS-Objects也可以通过Object.assignspread-syntax合并),则array_replace可能会更有用。
Putzi San

@Christian感谢您的建议,array_values()该建议在我的用例中非常有效。
bigsee '19

11

如果您的数组中只有基本类型,则可以执行以下操作:

$copy = json_decode( json_encode($array), true);

您不需要手动更新参考,
我知道它不适用于所有人,但是对我有用


4
+1这确实是一件坏事,但从技术上来说是正确且聪明的。如果我在代码中看到了这个,我会面无表情,但是我不禁喜欢它。
Reactgular '16

4

由于任何答案都未涉及到此问题,因此现在可以在PHP 5.3中使用(假定原始帖子使用的是5.2)。

为了维护数组结构并更改其值,我更喜欢使用array_replacearray_replace_recursive根据我的用例。

http://php.net/manual/zh/function.array-replace.php

这是一个使用array_replacearray_replace_recursive证明它能够保持索引顺序并能够删除引用的示例。

http://ideone.com/SzlBUZ

以下代码是使用PHP 5.4以来的可用短数组语法编写的,该语法替换array()[]http://php.net/manual/zh/language.types.array.php

适用于偏移索引和名称索引数组

$o1 = new stdClass;
$a = 'd';
//This is the base array or the initial structure
$o1->ar1 = ['a', 'b', ['ca', 'cb']];
$o1->ar1[3] = & $a; //set 3rd offset to reference $a

//direct copy (not passed by reference)
$o1->ar2 = $o1->ar1; //alternatively array_replace($o1->ar1, []);
$o1->ar1[0] = 'z'; //set offset 0 of ar1 = z do not change ar2
$o1->ar1[3] = 'e'; //$a = e (changes value of 3rd offset to e in ar1 and ar2)

//copy and remove reference to 3rd offset of ar1 and change 2nd offset to a new array
$o1->ar3 = array_replace($o1->ar1, [2 => ['aa'], 3 => 'd']);

//maintain original array of the 2nd offset in ar1 and change the value at offset 0
//also remove reference of the 2nd offset
//note: offset 3 and 2 are transposed
$o1->ar4 = array_replace_recursive($o1->ar1, [3 => 'f', 2 => ['bb']]);

var_dump($o1);

输出:

["ar1"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "ca"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    &string(1) "e"
  }
  ["ar2"]=>
  array(4) {
    [0]=>
    string(1) "a"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "ca"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    &string(1) "e"
  }
  ["ar3"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(1) {
      [0]=>
      string(2) "aa"
    }
    [3]=>
    string(1) "d"
  }
  ["ar4"]=>
  array(4) {
    [0]=>
    string(1) "z"
    [1]=>
    string(1) "b"
    [2]=>
    array(2) {
      [0]=>
      string(2) "bb"
      [1]=>
      string(2) "cb"
    }
    [3]=>
    string(1) "f"
  }


2

这是我在PHP中复制数组的方式:

function equal_array($arr){
  $ArrayObject = new ArrayObject($arr);
  return $ArrayObject->getArrayCopy();  
}

$test = array("aa","bb",3);
$test2 = equal_array($test);
print_r($test2);

输出:

Array
(
[0] => aa
[1] => bb
[2] => 3
)

2
为什么不说呢$test2 = $test;ArrayObject这里要解决什么问题?
2015年

1
<?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;
}
?>

1

我发现的最安全,最便宜的方法是:

<?php 
$b = array_values($a);

这也有利于重新索引数组。

这在关联数组(哈希)上将无法正常工作,但是先前答案的大部分都不会。


1

创建ArrayObject的副本

<?php
// Array of available fruits
$fruits = array("lemons" => 1, "oranges" => 4, "bananas" => 5, "apples" => 10);

$fruitsArrayObject = new ArrayObject($fruits);
$fruitsArrayObject['pears'] = 4;

// create a copy of the array
$copy = $fruitsArrayObject->getArrayCopy();
print_r($copy);

?>

来自https://www.php.net/manual/en/arrayobject.getarraycopy.php



0

在php数组中,您只需要将它们分配给其他变量即可获取该数组的副本。但是首先,您需要确定其类型,无论是array还是arrayObject或stdObject。

对于简单的php数组:

$a = array(
'data' => 10
);

$b = $a;

var_dump($b);

output:

array:1 [
  "data" => 10
]

0
private function cloneObject($mixed)
{
    switch (true) {
        case is_object($mixed):
            return clone $mixed;
        case is_array($mixed):
            return array_map(array($this, __FUNCTION__), $mixed);
        default:
            return $mixed;
    }
}

0

$arr_one_copy = array_combine(array_keys($arr_one), $arr_one);

只是发布另一个解决方案;)


-1
foreach($a as $key => $val) $b[$key] = $val ;

保留键和值。数组“ a”是数组“ b”的精确副本

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.