Jak głęboko skopiować obiekt DateTime?


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

Teraz $date1i $date2zawierają tę samą datę - za trzy lata. Chciałbym utworzyć dwa oddzielne czasy danych, jeden, który jest analizowany z ciągu, a drugi z dodanymi do niego trzema latami. Obecnie zhakowałem to w ten sposób:

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

ale to wydaje się strasznym włamaniem. Czy istnieje „poprawny” sposób głębokiego skopiowania obiektu DateTime?

Odpowiedzi:


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

Aktualizacja:

Jeśli chcesz skopiować, a nie odnosić się do istniejącego obiektu ID, użyj clone, nie =.

$a = clone $b;


12
W przykładzie użyłem nowego DateTime, aby zademonstrować ten punkt, ale na razie załóżmy, że DateTime jest zwracany z jakiegoś nieprzejrzystego interfejsu API, którego nie mogę po prostu ponownie wywołać. Na przykład mam funkcję, która obsługuje zamówienia, która zwraca DateTime, kiedy klient może złożyć zamówienie. Wywołanie funkcji tworzenia kopii powoduje efekty uboczne, których nie chcę.
Billy ONeal

Właściwie nie testowałem tego, ale na php.net jest napisane, że jest to dostępne tylko dla PHP 5.3 i nowszych.
hugo der hungrige

@hugo: Tak, klasa DateTime wymaga PHP 5.3.
Billy ONeal

11
Gdy wydawało mi się, że opanowałem PHP, dowiaduję się o nowym operatorze.
kr094

Musiałem to zrobić, aby skopiować istniejący obiekt Carbon do innej zmiennej. To zadziałało.
racl101

111

Sklonuj datę za pomocą operatora klonowania :

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

Klony są domyślnie płytkie, ale wystarczająco głębokie dla DateTime. W swoich własnych obiektach możesz zdefiniować __clone()magiczną metodę klonowania właściwości (tj. Obiektów potomnych), które mają sens przy klonowaniu po zmianie obiektu nadrzędnego.

(Nie jestem pewien, dlaczego w dokumentacji za dobrym przykładem potrzeby sklonowania obiektu jest GTK. Kto używa GTK w PHP?)


1
Dziękuję za odpowiedź, ale skąd wiesz, że jest wystarczająco głęboka dla DateTime? Które atrybuty pozostają odniesieniami, a które są kopiowane według wartości? Na przykład, mogę zmienić czas i strefę czasową i nie wpłynie to na klon?
David,

1
@David: Wiem, że jest wystarczająco głęboki dla DateTime, ponieważ wypróbowałem i zadziałało. Nie próbowałem zmieniać strefy czasowej ani niczego innego, tylko podstawową godzinę i datę.
rjmunro,

3
Używając Xdebug, var_dump ($ date1) raportuje, że zawiera on „date” => string, „timezone_type” => int & „timezone” => string. Ponieważ wygląda na to, że nie zawiera żadnych tablic ani obiektów, tylko podstawowe skalary, płytki klon powinien wystarczyć.
CJ Dennis

46

PHP 5.5.0 wprowadziło DateTimeImmutable . dodawanie i modyfikowanie metod tej klasy zwraca nowe obiekty.

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

4
Zwróć uwagę, że niestety nie możesz po prostu zamienić pliku DateTimena DateTimeImmutable. Przynajmniej IntlDateFormatter::formatObjectto nie lubi immutables (zwraca falsezamiast sformatowanego ciągu).
user276648

1
O! Jakoś nigdy nie wiedziałem, że to istnieje, chociaż od dawna o tym marzyłem. a całą drogę powrotną w 5.5 ...
Ben

2
Jak jakiś noob właśnie napotkałem pułapkę zorientowaną obiektowo, modyfikując mój DateTimeobiekt w pętli for: D To ładnie to rozwiązało ...
Wilt

3
@ user276648 Ten błąd jest już naprawiony w php 7.1.5 php.net/ChangeLog-7.php#7.1.5
jontro

11

TLDR:

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

(Shallow kopia jest enaugh - Głębokie copy-ing DateTime marek (obecnie) nie sens )

Proste :)

Wyjaśnienie „php tworzy obiekt datetime z innej daty i godziny”:

  1. cloneSłów kluczowych sprawia, że regularne płytkie kopię - enaugh w tym przypadku (dlaczego => patrz poniżej)
  2. Zawijanie go oblicza ()wyrażenie zwracające nowo utworzony obiekt przezclone
  3. ->modify() jest zatem wywoływana i modyfikuje nowy obiekt
  4. DateTime::modify(...) dokumenty:

    Zwraca obiekt DateTime dla łączenia metod lub FALSE w przypadku niepowodzenia.

  5. $date2zawiera teraz nowo utworzony i zmodyfikowany klon / kopię, ale $date1pozostaje niezmieniony

Dlaczego nie musisz tutaj kopiować głęboko :

Głębokie kopiowanie / klonowanie jest konieczne tylko wtedy, gdy trzeba skopiować cele właściwości, które są referencjami , ale to:

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();

wyjścia:

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

więc nie ma odniesień, tylko proste typy => nie ma potrzeby głębokiego kopiowania.


1

Powinieneś zmienić DateTimenaDateTimeImmutable

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

wtedy możesz wywołać dowolną metodę DateTimebez obawy o jej zmianę


To naprawdę odpowiedź na inne pytanie.
Billy ONeal

@BillyONeal Mogłem nie wyjaśnić w pełni, jak, ale jest to rozwiązanie tego problemu, ponieważ źródłem tego problemu jest to, jak wywołanie metody addna date2zmienia wartość date1i nie ma możliwości skopiowania wartości DateTimezmiennej, chyba że maszDateTimeImmutable
Hossein Shahdoost
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.