DateTimeオブジェクトをディープコピーするにはどうすればよいですか?


118
$date1 = $date2 = new DateTime();
$date2->add(new DateInterval('P3Y'));

$date1$date2今から3年-同じ日付が含まれています。2つの別々の日時を作成します。1つは文字列から解析され、もう1つは3年が追加されます。現在、私はそれを次のようにハッキングしています:

$date2 =  new DateTime($date1->format(DateTime::ISO8601));

しかし、それは恐ろしいハックのようです。DateTimeオブジェクトをディープコピーする「正しい」方法はありますか?

回答:


171
$date1 = new DateTime();
$date2 = new DateTime();
$date2->add(new DateInterval('P3Y'));

更新:

既存のDTオブジェクトを参照するのではなくコピーしたい場合はclone、ではなくを使用してください=

$a = clone $b;


12
ポイントを示すために例で新しいDateTimeを使用しましたが、今のところ、もう一度呼び出すだけでは不可能な不透明なAPIからDateTimeが返されると仮定します。たとえば、顧客が次に注文できるようになる日時を返す注文を処理する関数があります。関数を呼び出してコピーを作成すると、不要な副作用が発生します。
Billy ONeal

私は実際にはテストしていませんが、php.netでは、これはPHP 5.3以降でのみ利用できると述べられています。
hugo der hungrige 2013

@hugo:はい、DateTimeクラスにはPHP 5.3が必要です。
ビリーONeal 2013年

11
PHPを理解していると思ったとき、新しい演算子について学びました。
kr094 2014年

既存のCarbonオブジェクトを別の変数にコピーするには、これを行わなければなりませんでした。これはうまくいきました。
racl101 2015

111

クローン演算子を使用して日付をクローンします。

$date1 = new DateTime();
$date2 = clone $date1;
$date2->add(new DateInterval('P3Y'));

クローンはデフォルトでは浅いですが、DateTimeには十分な深さです。独自のオブジェクトで__clone()、親オブジェクトが変更されたときに複製するのに意味のあるプロパティ(つまり子オブジェクト)を複製する魔法のメソッドを定義できます。

(ドキュメントがオブジェクトのクローンを作成する必要のある良い例がGTKであると考える理由がわかりません。誰がPHPでGTKを使用していますか?)


1
答えてくれてありがとうございます。しかし、DateTimeにとって十分な深さがあることをどうやって知っていますか?どの属性が参照のままで、どれが値によってコピーされますか?たとえば、時間とタイムゾーンを変更できますが、クローンには影響しませんか?
デビッド

1
@David:試してみたので、DateTimeには十分な深さだとわかっています。タイムゾーンなどを変更するのではなく、基本的な時刻と日付を変更しました。
rjmunro 2013

3
Xdebugを使用すると、var_dump($ date1)は、 'date' =>文字列、 'timezone_type' => int& 'timezone' =>文字列を含むと報告します。配列やオブジェクトは含まれておらず、基本的なスカラーだけが含まれているため、浅いクローンでも問題ありません。
CJデニス

46

PHP 5.5.0でDateTimeImmutableが導入されました。このクラスのaddおよびmodifyメソッドは、新しいオブジェクトを返します。

$date1 = new DateTimeImmutable();
$date2 = $date1->add(new DateInterval('P3Y'));

4
残念ながらあなただけ入れ替えることができないことに注意してくださいDateTimeDateTimeImmutable。少なくともIntlDateFormatter::formatObject、不変(falseフォーマットされた文字列の代わりに返される)は好きではありません。
user276648 2016

1
ああ!私は長い間夢を見ていましたが、どういうわけかこれが存在することを知りませんでした。そしてずっとずっと5.5に戻って...
ベン

2
noobのようDateTimeに、forループでオブジェクトを変更してオブジェクト指向の落とし穴に遭遇しました:Dこれはうまく解決しました...
Wilt

3
@ user276648このバグはphp 7.1.5で修正されましたphp.net/ChangeLog-7.php#7.1.5
jontro

11

TLDR:

$date1 = new DateTime();
$date2 = (clone $date1)->modify('+3 years');

(浅いコピーは十分です- 深いコピーを行うDateTimeは(現在のところ)意味がありません

そのような単純な :)

説明「phpは別の日時から日時オブジェクトを作成します」:

  1. cloneキーワードは、定期的に行い、浅いと、この場合のenaugh(理由=>は、以下を参照してください) -コピー
  2. でラップすると()、新しく作成されたオブジェクトを返す式が評価されます。clone
  3. ->modify() したがって、呼び出され、新しいオブジェクトを変更します
  4. DateTime::modify(...) docs:

    メソッドチェーンのDateTimeオブジェクトを返します。失敗した場合はFALSEを返します。

  5. $date2新しく作成および変更されたクローン/コピーが含まれますが、変更され$date1ません。

ここにディープコピーする必要がない理由:

ディープコピー/クローンが必要なのは、参照であるプロパティのターゲットをコピーする必要がある場合のみですが、これは次のとおりです。

class TestDateTime extends DateTime{
  public function test(){
   //*this* way also outputs private variables if any...
   var_dump( get_object_vars($this) );    
  }
}
$test = (new TestDateTime())->test();

出力:

array(3) {
  ["date"]=>
  string(26) "2019-08-21 11:38:48.760390"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}

したがって、参照はなく、単純な型=> ディープコピーの必要はありません。


1

あなたは変更する必要がありますDateTimeにしますDateTimeImmutable

// from date time
$date = \DateTimeImmutable::createFromMutable($mutableDate)

次にDateTime、変更を心配せずに任意のメソッドを呼び出すことができます


これは実際には別の質問に対する答えです。
Billy ONeal 2017

@BillyONeal私はどのように十分に説明されていないかもしれないが、しかし、この問題の原因は、メソッドを呼び出す方法ですので、これはこの問題を解決するadd上でdate2の変化の値date1との値をコピーする方法はありませんDateTimeあなたが持っていない限り、変数はDateTimeImmutable
ホセインシャードオスト2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.