この記事では、PHP5のオブジェクトコピー技術を簡単なものから詳細なものまで解説します。 間違いや不適切な点があれば、ご指摘いただければ幸いです。
オブジェクトコピーの起源なぜオブジェクトに「コピー」という概念があるのでしょうか? これは、PHP5 のオブジェクトの値の転送方法と密接に関係しています。次の簡単なコードを見てみましょう。
PHPコード
/**
echo 'tv1 の色は: ' . $tv1->getColor();//tv1 の色は黒です
エコー '
';
エコー '
まず、tv1 と tv2 の色が両方とも黒であることがわかります。次に、tv2 の色を変更したいので、その色を白に設定します。実際に、一致しているように見えます。要件は満たしていましたが、tv1 の色を確認すると、期待したほどスムーズではありませんでした。 tv1 の色をリセットしなかったのはなぜですか?なぜなら、PHP5におけるオブジェクトの代入や値の受け渡しはすべて「参照」によって行われるからです。 PHP5 は Zend Engine II を使用しており、オブジェクトは他の一般変数のように Zval に保存されるのではなく、別の構造のオブジェクト ストアに保存されます (PHP4 では、オブジェクトは一般変数と同様に Zval に保存されます)。オブジェクトの内容 (値) ではなく、オブジェクトのポインターのみが Zval に格納されます。オブジェクトをコピーする場合、またはオブジェクトをパラメーターとして関数に渡す場合、データをコピーする必要はありません。同じオブジェクト ポインターを保持し、この特定のオブジェクトが別の zval を介してポイントしていることをオブジェクト ストアに通知するだけです。オブジェクト自体はオブジェクト ストアに配置されているため、オブジェクトに加えた変更は、そのオブジェクトへのポインタを保持するすべての zval 構造に影響します。これは、ターゲット オブジェクトに対する変更がソース オブジェクトに影響するため、プログラムに明示されています。これにより、PHP オブジェクトが常に参照によって渡されるように見えます。したがって、上記の tv2 と tv1 は実際には同じ TV インスタンスを指しており、tv1 または tv2 で行う操作は実際にはこの同じインスタンスに対するものです。したがって、「コピー」は失敗しました。このため、PHP5 ではオブジェクトのコピーに特化した操作 (clone) が提供されています。ここでオブジェクトのコピーが登場します。
クローンを使用してオブジェクトをコピーします
PHP5 のクローン言語構造を使用してオブジェクトをコピーするコードは次のとおりです:
PHPコード
ここで、各テレビに独自の番号が必要であるという状況を考えます。この番号は、ID 番号と同様に一意である必要があるため、テレビをコピーするときに、トラブルを避けるために番号もコピーされることは望ましくありません。私たちが考え出した戦略の 1 つは、割り当てられている TV 番号をクリアし、必要に応じて番号を再割り当てすることです。
このような問題を解決するために、__clone マジック メソッドが特に使用されます。オブジェクトがコピーされる (つまり、クローン操作) ときに、__clone マジック メソッドがトリガーされます。 TV クラス Television のコードを変更し、number 属性と __clone メソッドを追加しました。コードは次のとおりです。
PHPコード
以下では、このようなテレビのオブジェクトを作成します。
PHP代
クローン操作の致命的な欠陥
クローンは本当に理想的なコピー効果を実現できるのでしょうか?場合によっては、クローン操作が想像したほど完璧ではないことがわかるはずです。上記の TV タイプを変更してテストしてみましょう。
各テレビにはリモコンが付属しているので、リモコンのクラスを開きます。リモコンとテレビは「集合」関係です (「組み合わせ」関係と比較すると、依存関係は弱いです。一般的に言えば、次のとおりです)。テレビはリモコンがなくても通常どおり使用できます)。これで、TV オブジェクトはすべてリモコン オブジェクトへの参照を保持するはずです。以下のコードを見てください
PHPコード
次に、そのような TV オブジェクトをコピーし、TV のリモコン オブジェクトを観察します。
PHPコード
$tv1 = 新しいテレビ ();
__clone マジック メソッドの使用についてはすでに紹介しました。コピーされたオブジェクト内の他のオブジェクトの参照を、__clone メソッド内の新しいオブジェクトに再参照できます。変更された __clone() マジック メソッドを見てみましょう:
PHPコード
04 行目では、コピーした TV オブジェクトのリモコンをリセットします。前の方法に従ってオブジェクト ID を確認すると、2 つの TV のリモコンのオブジェクト ID が異なることがわかり、問題は解決されました。
しかし、この方法はおそらくあまり良くありません。コピーされたオブジェクトに他のオブジェクトへの参照が複数ある場合、__clone メソッドでそれらを 1 つずつリセットする必要があります。さらに悪いことに、コピーされたオブジェクトのクラスが によって提供されている場合です。ただし、コードを変更することはできないため、コピー操作は基本的にスムーズに完了しません。
オブジェクトをコピーするにはクローンを使用します。この種のコピーは「浅いコピー」と呼ばれます。コピーされたオブジェクトのすべての変数には元のオブジェクトと同じ値が含まれており、他のオブジェクトへの参照はすべて元のオブジェクトを指し続けます。つまり、浅いコピーは、参照しているオブジェクトではなく、問題のオブジェクトのみをコピーします。 「浅いコピー」と比較すると、もちろん「深いコピー」もあります。コピーされたオブジェクトのすべての変数には、他のオブジェクトを参照する変数を除き、元のオブジェクトと同じ値が含まれます。つまり、ディープ コピーでは、コピー対象のオブジェクトが参照するすべてのオブジェクトがコピーされます。ディープ コピーでは、何層まで進むかを決定する必要がありますが、これは簡単に決定できない問題であり、さらに、循環参照の問題が発生する可能性があるため、慎重に処理する必要があります。オプション 2 はディープ コピー ソリューションです。
解決策 2: ディープコピーにシリアル化を使用する
PHP にはシリアル化 (シリアル化) 関数と逆シリアル化 (アンシリアル化) 関数があり、serialize() を使用してオブジェクトをストリームに書き込み、ストリームからオブジェクトを読み取るだけで、オブジェクトがコピーされます。 JAVA 言語では、このプロセスは「冷却」および「解凍」と呼ばれます。以下でこのメソッドをテストします:
PHPコード
$tv1 = 新しいテレビ ();
オブジェクトのコピー方法が異なれば効果も異なります。どの方法を使用するか、特定のアプリケーション要件に基づいてコピー方法を改善する方法を検討する必要があります。 PHP5 のオブジェクト指向機能は比較的 JAVA に近いので、JAVA から多くの貴重な経験を学ぶことができると思います
。