💙
PHPのコピー(配列、オブジェクトの違い)
配列(スカラ値も同様)とオブジェクトのコピー方法による挙動の違いをまとめ。
変数に代入
配列:コピー元が参照されない。(ディープコピー)
オブジェクト:コピー元が参照される。(シャローコピー)
<?php
class Ingredient
{
public $sugar;
public $flour;
public function __construct(string $sugar)
{
$this->sugar = $sugar;
}
}
$ingredient = new Ingredient(300);
$cheeseCake = [
'name' => 'cheese cake',
'ingredient' => $ingredient,
];
// 代入(コピー)Copy on Write[※]
$spongeCake = $cheeseCake;
// 配列の値を変更
$spongeCake['name'] = 'sponge cake';
// オブジェクトの値を変更
$spongeCake['ingredient']->flour = 500;
echo "【実行結果】\n";
var_dump($cheeseCake);
var_dump($spongeCake);
【実行結果】
array(2) {
// ディープコピー。配列はコピー元まで影響を受けていない。
["name"]=>
string(11) "cheese cake"
["ingredient"]=>
object(Ingredient)#1 (2) {
["sugar"]=>
string(3) "300"
// シャローコピー。オブジェクトはコピー元まで影響を受けてしまっている。
["flour"]=>
int(500)
}
}
array(2) {
// 配列の値はコピー元まで影響を与えていない。
["name"]=>
string(11) "sponge cake"
["ingredient"]=>
object(Ingredient)#1 (2) {
["sugar"]=>
string(3) "300"
// オブジェクトの値はコピー元まで影響を与えてしまっている。
["flour"]=>
int(500)
}
}
[※]コピーオンライト (Copy-On-Write)
変数を代入しただけではメモリが新たに確保(コピー)されない。
この時点ではまだ同じメモリ領域をみている。
代入した変数を書き換えることによってコピーが実行される。
cloneメゾット
オブジェクトのコピーはcloneを使う。ただ、cloneだけではオブジェクト内の参照型のものはシャローコピーになる為、__clone()メゾット(※) を用いてディープコピーを実現します。
配列:cloneメソッドでコピーできない。(Fatal errorになる)
オブジェクト:コピー元が参照されない。(ディープコピー)
<?php
class Music {
private $genre;
private $song;
public function __construct(string $genre)
{
$this->genre = $genre;
}
public function getGenre(){
return $this->genre;
}
public function setGenre(string $genre){
$this->genre = $genre;
}
public function getSong(){
return $this->song;
}
public function setSong(Song $song=null){
$this->song = $song;
}
// (※)
public function __clone() {
$this->song = clone $this->song;
}
}
class Song {
private $title;
public function getTitle(){
return $this->title;
}
public function setTitle(string $title=null){
$this->title = $title;
}
}
$hipHop = new Music('hip-hop');
$songH = new Song();
$songH->setTitle('deep in the drank');
$hipHop->setSong($songH);
$trap = clone $hipHop;
$trap->setGenre('trap');
$songT = new Song();
$songT->setTitle('save that shit');
$trap->setSong($songT);
// ↓これだと上手くいかない
// $trap->getSong->setTitle('save that shit');
echo "【実行結果】\n";
var_dump($hipHop);
var_dump($trap);
【実行結果】
object(Music)#1 (2) {
["genre":"Music":private]=>
string(7) "hip-hop"
["song":"Music":private]=>
object(Song)#2 (1) {
["title":"Song":private]=>
string(17) "deep in the drank"
}
}
object(Music)#3 (2) {
["genre":"Music":private]=>
string(4) "trap"
["song":"Music":private]=>
object(Song)#5 (1) {
["title":"Song":private]=>
string(14) "save that shit"
}
}
また、以下の方法でもオブジェクトのディープコピーが実現可能みたい。
cloneメゾットいちいち書かなくていいの?か確認中...
$copyObject = unserialize(serialize($object));
Discussion