オブジェクトの配列があります。オブジェクトは「参照」によって割り当てられ、配列は「値」によって割り当てられることを知っています。しかし、配列を割り当てるとき、配列の各要素はオブジェクトを参照しているため、いずれかの配列のオブジェクトを変更すると、変更はもう一方に反映されます。
配列を複製する簡単な方法はありますか、それともループを介して各オブジェクトを複製する必要がありますか?
配列をコピーすると、同じオブジェクトへの参照がすでにコピーされます。しかし、あなたがしたいように聞こえます 浅いコピー 2番目の配列を作成するときに、最初の配列で参照されているオブジェクトをディープコピーします。これにより、異なるが類似したオブジェクトの2つの配列を取得できます。
私が今思いつく最も直感的な方法はループです。よりシンプルな、またはよりエレガントなソリューションがあるかもしれません:
$new = array();
foreach ($old as $k => $v) {
$new[$k] = clone $v;
}
$array = array_merge(array(), $myArray);
同じオブジェクトへの参照を避けるために、オブジェクトを複製する必要があります。
function array_copy($arr) {
$newArray = array();
foreach($arr as $key => $value) {
if(is_array($value)) $newArray[$key] = array_copy($value);
else if(is_object($value)) $newArray[$key] = clone $value;
else $newArray[$key] = $value;
}
return $newArray;
}
AndreKRで提案されているように、配列にオブジェクトが含まれていることが既にわかっている場合は、array_map()を使用するのが最善の方法です。
$clone = array_map(function ($object) { return clone $object; }, $array);
クローンも選択しました。 array clonewith array_map)のように、配列の複製は機能しません(いくつかのarrayaccessの実装を考慮してください)。:
class foo {
public $store;
public function __construct($store) {$this->store=$store;}
}
$f = new foo('moo');
$a = array($f);
$b = array_map(function($o) {return clone $o;}, $a);
$b[0]->store='bar';
var_dump($a, $b);
オブジェクトがシリアル化をサポートしている場合、ディープシャローコピー/クローンをスリープ状態に戻したり戻したりすることもできます:
$f = new foo('moo');
$a = array($f);
$b = unserialize(serialize($a));
$b[0]->store='bar';
var_dump($a, $b);
しかし、それは少し冒険的なものです。
私はこのようにしました:
function array_clone($array) {
array_walk_recursive($array, function(&$value) {
if(is_object($value)) {
$value = clone $value;
}
});
return $array;
}
関数argは、オブジェクトを複製せずに配列をコピーし、ネストされた各オブジェクトが複製されます。そのため、アルゴリズムが関数内で使用されていない場合は機能しません。
この関数は、配列を再帰的に複製することに注意してください。これを行わない場合は、array_walk
の代わりにarray_walk_recursive
を使用できます。
オブジェクトの配列とクローン作成に関するベストプラクティスを次に示します。通常、配列で使用されるオブジェクト(またはインターフェイス)の各クラスにCollectionクラスを用意することをお勧めします。マジック関数を使用すると、___clone
_クローニングは正式なルーチンになります。
_class Collection extends ArrayObject
{
public function __clone()
{
foreach ($this as $key => $property) {
$this[$key] = clone $property;
}
}
}
_
アレイを複製するには、コレクションとして使用してから複製します。
_$arrayObject = new Collection($myArray);
$clonedArrayObject = clone $arrayObject;
_
さらに一歩進んで、クラスと各サブクラスにもcloneメソッドを追加する必要があります。これはディープクローン作成にとって重要です。そうしないと、意図しない副作用が生じる可能性があります。
_class MyClass
{
public function __clone()
{
$this->propertyContainingObject = clone $this->propertyContainingObject;
}
}
_
ArrayObjectの使用に関する重要な注意点は、is_array()
を使用できなくなったことです。そのため、コードのリファクタリングでこれに注意してください。
ループする必要があります(おそらくそのためにarray_map()
のような関数を使用します)、PHP配列のディープコピーを自動的に実行する関数はありません。
PHP 5以上の場合、ArrayObject
cunstructurを使用して、次のような配列を複製できます。
$myArray = array(1, 2, 3);
$clonedArray = new ArrayObject($myArray);
オブジェクトはデフォルトでポイントされて渡され、特に循環参照を持っている可能性があるため、クローンを作成するのは必ずしも簡単ではありません。データ構造の選択が異なる方が適しています。
浅いコピーの解決策を提供している人にとって、簡単な方法は次のとおりです。
$b = (array)$a;
ディープコピーの場合、このソリューションはお勧めしません。
$ nuarr = json_decode(json_encode($ array));
これはディープコピー用です。 PHPタイプのサブセットのみをサポートし、オブジェクトを配列にスワップしたり、必要に応じてオブジェクトを配列にスワップしたり、バイナリ値を破損したりする可能性があります。
ディープコピー用に手動の再帰関数を作成すると、スカラー値とキーのメモリ使用量がその後少なくなるため、jsonまたはシリアライザーを使用すると、実行ポイントを超えた影響があります。
オブジェクトなどの幅広いサポートを備えたパフォーマンスが問題にならない場合は、ディープコピーにunserialize(serialize($ a))を使用した方が良いかもしれませんが、循環参照やその他のいくつかの異常なものが壊れても驚かないでしょう。
array_merge_recursiveまたはarray_walk_recursiveも配列に使用できます。
Is_objectとis_arrayを使用して適切なコピー方法を選択する独自の再帰関数を簡単に作成できます。
またはまた
$nuarr = json_decode(json_encode($array));
しかし、それは高価です、私はセバスチャンバージョン(array_map)を好む