PHPのオブジェクトコピー

CakePHPのqueryビルダで、こんな感じで条件を変えて連続して結果を取得したいなぁ‥というとき。
例えばこんな感じのことをしたいとすると

$query = $this->find()->where(共通の複雑な条件);
$a = $query->where(['category'=>1]);
$b = $query->where(['category'=>2]);
$c = $query->where(['category'=>3]);

これはうまくいきません。
a,b,cともに中身が空になります。
それは当たり前で、a,b,cともにこんなqueryと同義になってしまいます。

$query->where(['category'=>1])
->where(['category'=>2])
->where(['category'=>3]);

なぜこんなことになるかというと

objectは参照型なので,代入先でプロパティを変更したときに代入元と同じ操作がなされる

ということです。つまり、C言語で言うところの参照渡しになるので、代入した変数はすべて同じものを指しているということです。
そりゃそうですよ。オブジェクトなんだから値渡しなんかできないです。

じゃ、どうするかというと、一つ一つインスタンスを作り直していくしか方法がありません。

と、私は今まで思い込んでいましたが、実はオブジェクトのコピーってできるんです。

clone というのを使います。

$tmp= $this->find()->where(共通の複雑な条件); 
$query = clone $tmp;
$a = $query->where(['category'=>1]);
$query = clone $tmp;
$b = $query->where(['category'=>2]);
$query = clone $tmp;
$c = $query->where(['category'=>3]);

すくなくとも、cakephpのqueryビルダについてはうまくいきましたが、これはかなりの曲者。できれば使わないほうがいいっぽい。

まずオブジェクトの複製は単純に処理コストが高そうです。

また単純なクラスであれば特に問題はないですが、クラス内で他のクラスをインスタンス生成している場合はどうなるの?という問題があります。
cloneされたときにどのようにふるまうかをクラス内で定義することもできるらしい(__clone()マジックメソッド)ので、クラス側でcloneの配慮がされていないクラスを安易にコピーするのはかなり危険が伴いそうです。

ということで、”こういうこともできるんだ”という知識にとどめて、cloneは基本使わないほうがよさそうです。

タイトルとURLをコピーしました