Как глубоко скопировать объект DateTime?

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

Теперь $date1и $date2содержат ту же дату - через три года. Я хотел бы создать две отдельные даты, одна из которых анализируется из строки, а другая - с добавлением трех лет. На данный момент взломал вот так:

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

но это кажется ужасным взломом. Есть ли «правильный» способ глубокого копирования объекта DateTime?

Билли Онил
источник

Ответы:

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

Обновить:

Если вы хотите скопировать, а не ссылаться на существующий объект DT, используйте clone, не =.

$a = clone $b;

Эми Б
источник
12
Я использовал новый DateTime в примере, чтобы продемонстрировать суть, но пока предположим, что DateTime возвращается из какого-то непрозрачного API, который я не могу просто вызвать снова. Например, у меня есть функция, которая обрабатывает заказы, которая возвращает DateTime, когда клиент может разместить заказ в следующий раз. Вызов функции для создания копии приводит к нежелательным побочным эффектам.
Билли Онил
На самом деле я не тестировал его, но на php.net упоминается, что это доступно только для PHP 5.3 и выше.
hugo der hungrige 01
@hugo: Да, класс DateTime требует PHP 5.3.
Билли Онил
11
Как только я подумал, что разбираюсь в PHP, я узнал о новом операторе.
kr094
Пришлось сделать это, чтобы скопировать существующий объект Carbon в другую переменную. Это сработало.
racl101
111

Клонируйте дату с помощью оператора клонирования :

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

По умолчанию клоны неглубокие, но достаточно глубокие для DateTime. В ваших собственных объектах вы можете определить __clone()волшебный метод для клонирования свойств (т.е. дочерних объектов), которые имеют смысл клонировать при изменении родительского объекта.

(Я не уверен, почему в документации говорится, что хорошим примером необходимости клонирования объекта является GTK. Кто использует GTK в PHP?)

rjmunro
источник
1
Спасибо за ответ, но откуда вы знаете, что он достаточно глубок для DateTime? Какие атрибуты остаются ссылками, а какие копируются по значению? Например, я могу изменить время и часовой пояс, и это не повлияет на клон?
Дэвид
1
@David: Я знаю, что для DateTime это достаточно глубоко, потому что я попробовал, и у меня это сработало. Я не пробовал менять часовой пояс или что-то еще, только время и дату.
rjmunro
3
Используя Xdebug, var_dump ($ date1) сообщает, что он содержит 'date' => string, 'timezone_type' => int & 'timezone' => string. Поскольку он не содержит массивов или объектов, только базовые скаляры, неглубокий клон вполне подойдет.
CJ Dennis
46

PHP 5.5.0 представил DateTimeImmutable . Методы добавления и изменения этого класса возвращают новые объекты.

$date1 = new DateTimeImmutable();
$date2 = $date1->add(new DateInterval('P3Y'));
Александровский сад
источник
4
Обратите внимание, что, к сожалению, вы не можете просто заменить a DateTimeна DateTimeImmutable. По крайней мере IntlDateFormatter::formatObject, это не любит неизменяемые (возвращает falseвместо форматированной строки).
user276648
1
ой! Я как-то никогда не знал, что такое существует, хотя давно мечтал об этом. и обратно в 5.5 ...
Бен
2
Как какой-то новичок, я только что столкнулся с объектно-ориентированной ловушкой, изменив свой DateTimeобъект в цикле for: D Это прекрасно решило проблему ...
Уилт
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');

(Неглубоко копия не является enaugh - Deep копии-кий DateTime марки ( в настоящее время) нет смысла )

Просто как тот :)

Объяснение «php создает объект datetime из другого datetime»:

  1. cloneКлючевые слова делают регулярную неглубокую копию - enaugh для этого случая (почему => смотри ниже)
  2. Обертывание его с помощью ()оценивает выражение, возвращающее вновь созданный объект, с помощьюclone
  3. ->modify() поэтому вызывается и изменяет новый объект
  4. DateTime::modify(...) документы:

    Возвращает объект 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"
}

поэтому нет ссылок, только простые типы => не нужно углубляться копии.

jave.web
источник
1

Вы должны изменить свой DateTimeнаDateTimeImmutable

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

тогда вы можете вызвать любой метод, DateTimeне беспокоясь об его изменении

Хоссейн Шахдуст
источник
Это действительно ответ на другой вопрос.
Билли
@BillyONeal я мог бы не объяснил полностью , как, но это решение этой проблемы в качестве источника этой проблемы заключается в том , как вызов метода addна date2изменения значения , date1и нет никакого способа , чтобы скопировать значение DateTimeпеременной , если не имеютDateTimeImmutable
Hossein Shahdoost