Come si copia in profondità un oggetto DateTime?


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

Ora $date1e $date2contengono la stessa data - tre anni da adesso. Vorrei creare due datetimes separati, uno che viene analizzato da una stringa e uno con tre anni aggiunti. Attualmente l'ho hackerato in questo modo:

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

ma sembra un orrendo hack. Esiste un modo "corretto" per copiare in profondità un oggetto DateTime?

Risposte:


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

Aggiornare:

Se vuoi copiare piuttosto che fare riferimento a un oggetto DT esistente, usa clone, not =.

$a = clone $b;


12
Ho usato un nuovo DateTime nell'esempio per dimostrare il punto, ma per ora presumo che DateTime venga restituito da qualche API opaca che non posso semplicemente richiamare di nuovo. Ad esempio, ho una funzione che gestisce gli ordini che restituisce un DateTime che è il momento in cui il cliente può effettuare un ordine successivo. Chiamare la funzione per creare una copia produce effetti collaterali che non voglio.
Billy ONeal

In realtà non l'ho testato, ma su php.net è indicato che è disponibile solo per PHP 5.3 e versioni successive.
hugo der hungrige

@hugo: Sì, la classe DateTime richiede PHP 5.3.
Billy ONeal

11
Proprio quando pensavo di avere un'idea di PHP, ho scoperto un nuovo operatore.
kr094

Ho dovuto farlo per copiare un oggetto Carbon esistente su un'altra variabile. Questo ha funzionato.
racl101

111

Clona la data con l' operatore clone :

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

I cloni sono poco profondi per impostazione predefinita, ma abbastanza profondi per un DateTime. Nei tuoi oggetti, puoi definire il __clone()metodo magico per clonare le proprietà (cioè gli oggetti figli) che hanno senso essere clonati quando l'oggetto genitore cambia.

(Non sono sicuro del motivo per cui la documentazione ritiene che un buon esempio di necessità di clonare un oggetto sia GTK. Chi usa GTK in PHP?)


1
Grazie per la risposta, ma come fai a sapere che è abbastanza profondo per DateTime? Quali attributi rimangono riferimenti e quali vengono copiati per valore? Ad esempio, posso modificare l'ora e il fuso orario e non influirà sul clone?
David,

1
@ David: So che è abbastanza profondo per DateTime perché l'ho provato e ha funzionato per me. Non ho provato a cambiare il fuso orario o altre cose, solo l'ora e la data di base.
rjmunro

3
Utilizzando Xdebug, var_dump ($ date1) segnala che contiene 'date' => string, 'timezone_type' => int & 'timezone' => string. Dal momento che non sembra contenere array o oggetti, solo scalari di base, un clone superficiale dovrebbe andare bene.
CJ Dennis

46

PHP 5.5.0 ha introdotto DateTimeImmutable . i metodi di aggiunta e modifica di questa classe restituiscono nuovi oggetti.

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

4
Nota che sfortunatamente non puoi semplicemente scambiare un DateTimecon un DateTimeImmutable. Almeno a IntlDateFormatter::formatObjectquesto non piacciono gli immutabili (restituisce falseinvece della stringa formattata).
user276648

1
Oh! In qualche modo non ho mai saputo che esistesse, anche se l'ho sognato a lungo. e tutto il ritorno in 5.5 ...
Ben

2
Come un noob, ho appena incontrato una trappola orientata agli oggetti modificando il mio DateTimeoggetto in un ciclo for: D Questo l'ha risolto bene ...
Wilt

3
@ user276648 Questo bug è stato corretto in PHP 7.1.5 php.net/ChangeLog-7.php#7.1.5
jontro

11

TLDR:

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

(La copia poco profonda è sufficiente: la copia profonda di DateTime (attualmente) non ha senso )

Semplice come quella :)

Spiegazione "php crea un oggetto datetime da un altro datetime":

  1. La cloneparola chiave crea una copia superficiale regolare - sufficiente per questo caso (perché => vedi sotto)
  2. Avvolgendolo con ()valuta l'espressione che restituisce l'oggetto appena creato daclone
  3. ->modify() viene quindi richiamato e modifica il nuovo oggetto
  4. DateTime::modify(...) docs:

    Restituisce l'oggetto DateTime per il concatenamento di metodi o FALSE in caso di errore.

  5. $date2ora contiene il clone / copia appena creato e modificato, mentre $date1rimane invariato

Perché non è necessario eseguire una copia approfondita qui:

Copia profonda / clone è necessaria solo quando è necessario copiare obiettivi di proprietà che sono riferimenti , ma questo:

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

uscite:

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

quindi non ci sono riferimenti, solo tipi semplici => non c'è bisogno di copia approfondita .


1

Dovresti cambiare il tuo DateTimeinDateTimeImmutable

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

quindi puoi chiamare qualsiasi metodo su DateTimesenza preoccuparti che cambi


Questa è davvero una risposta a una domanda diversa.
Billy ONeal

@BillyONeal Potrei non aver spiegato completamente come, ma questa è una soluzione a questo problema poiché la fonte di questo problema è il modo in cui la chiamata al metodo addsu date2modifica il valore di date1e non c'è modo di copiare il valore della DateTimevariabile a meno che tu non abbia unDateTimeImmutable
Hossein Shahdoost
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.