• ベストアンサー

PHP5 オブジェクトのリファレンスとデストラクタ

PHP5.0.4を使っています。 原因不明の現象に悩まされていましたが、再現する最小限のコードがわかりました。 class Foo { function __destruct() { } } $bar=&new Foo(); $bar=&new Foo(); というコードで、最初の$barへの代入ではちゃんとFooクラスのオブジェクトへのリファレンスが代入されますが、2回目の代入では結果として$barはNULLになってしまいます。 (1)リファレンスでなく値の代入にする (2)変数名を変える (3)2回目の代入の前に$barをunset()する (4)デストラクタの宣言を削除する のいずれかで2回目もFooオブジェクトが$barに代入されるのですが、(1)は実際はそこそこ大きいオブジェクトなのでコピーするのは無駄、(2)実際はループになっており2回目以降の変数名を変えるのは不可能 ということで、現在は(3)の対処をしています。 Q1.何故こういう現象が起こるのか Q2.デストラクタ宣言の有無で変わるのは何故か Q3.どういう対処が正しいのか 実際の処理は、こんな感じです。 foreach($aaa as $x) {  $bar=&new Foo($x);  if( $bar->test() ) $value[]=&$bar;  /*ここに else unset($bar); を入れるとOK */ }

質問者が選んだベストアンサー

  • ベストアンサー
  • php504
  • ベストアンサー率42% (926/2160)
回答No.3

ちなみにPHP5ではerrorレベルをstrictまで上げていたらオブジェクトをリファレンス渡しすると Strict Standards: Assigning the return value of new by reference is deprecated というエラーになります。 PHP5でPEAR使ってプログラムするときはstrictは表示しないようにしています。上記エラーが出まくりますので。

notnot
質問者

お礼

直接の回答は得られませんでしたが、strictにすると警告が出ることを教えていただきありがとうございました。

その他の回答 (2)

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.2

Q1とQ2については、バグと言えるんじゃないですかね。 本来的に言えば $bar=&new Foo(); $bar=&new Foo(); は、別におかしくないですもんね。 多分、タイミング的なものなんだと思います。 推測ですが、 __destructが呼び出される時には、 領域的には、解放されたことになっているが、 (destructの説明でも「オブジェクトへの全てのリファレンスが 削除された直後」によびだされるとありますし、リファレンスが無くなった時点で解放されたことになるのだと思う) 実際にはまだ(destruct)処理中であって、その解放されるはずの領域が参照の領域として新たに(ダブって)割り当てられてそこが解放されてしまうみたいな感じでしょうかね

notnot
質問者

お礼

回答ありがとうございました。明確な答えは得られませんでしたが、多分そんなところだとは思います。ただバグなのか動きとしては間違っていないのかは正確な文法規定書を見ないと何ともいえないと思います。

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.1

クラスはもともと参照型だから $bar=new Foo($x); とすればいいです。 それを&new Foo(); としたのが間違い

notnot
質問者

お礼

明確な回答は得られませんでしたが、「どうすべきか」については&をつけないということで間違い無いと思います。ありがとうございました。

notnot
質問者

補足

回答ありがとうございます。 質問後に再確認したら、オブジェクトは clone で明示しない限りリファレンス代入だということがわかり、&をつけないでもよいことがわかりました。 ただ、PEAR のソースを見てもかなりの箇所で =&new しているんですよね。手元の環境だと、&無しが158箇所、&有りが87箇所でした。PEAR/DBを読んでいるとオブジェクトを返す関数に&がついていたりします。 コピーが発生しない以上、 Q3.どういう対処が正しいのか については、お書きのとおりなんでしょう。 引き続きQ1,Q2がわかる方、お願いします。

関連するQ&A