Làm cách nào để sao chép sâu một đối tượng DateTime?


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

Bây giờ $date1$date2chứa cùng một ngày - ba năm kể từ bây giờ. Tôi muốn tạo hai lịch ngày riêng biệt, một lịch được phân tích cú pháp từ một chuỗi và một với ba năm được thêm vào. Hiện tại tôi đã hack nó lên như thế này:

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

nhưng đó có vẻ như là một vụ hack khủng khiếp. Có cách nào "đúng" để sao chép sâu đối tượng DateTime không?

Câu trả lời:


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

Cập nhật:

Nếu bạn muốn sao chép thay vì tham chiếu một đối tượng DT hiện có, hãy sử dụng clone, không =.

$a = clone $b;


12
Tôi đã sử dụng DateTime mới trong ví dụ để chứng minh quan điểm, nhưng bây giờ giả sử DateTime được trả về từ một API không rõ ràng nào đó mà tôi không thể gọi lại. Ví dụ: tôi có một chức năng xử lý các đơn đặt hàng trả về DateTime là thời điểm khách hàng có thể đặt hàng tiếp theo. Việc gọi hàm tạo bản sao sẽ tạo ra những tác dụng phụ mà tôi không muốn.
Billy ONeal

Tôi chưa thử nghiệm nó thực sự, nhưng nó được đề cập tại php.net rằng điều này chỉ khả dụng cho PHP 5.3 trở lên.
hugo der hungrige

@hugo: Có, lớp DateTime yêu cầu PHP 5.3.
Billy ONeal

11
Ngay khi tôi nghĩ rằng mình đã hiểu về PHP, tôi học về một toán tử mới.
kr094

Phải làm điều này để sao chép một đối tượng Carbon hiện có sang một biến khác. Điều này đã hiệu quả.
racl101

111

Sao chép ngày tháng với toán tử nhân bản :

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

Các bản sao là nông theo mặc định, nhưng đủ sâu cho một DateTime. Trong các đối tượng của riêng bạn, bạn có thể xác định __clone()phương thức ma thuật để sao chép các thuộc tính (tức là các đối tượng con) có nghĩa là được sao chép khi đối tượng mẹ thay đổi.

(Tôi không chắc tại sao tài liệu cho rằng một ví dụ điển hình về việc cần sao chép một đối tượng là GTK. Ai sử dụng GTK trong PHP?)


1
Cảm ơn bạn vì câu trả lời, nhưng làm thế nào bạn biết nó đủ sâu cho DateTime? Thuộc tính nào vẫn là tham chiếu và thuộc tính nào được sao chép theo giá trị? Ví dụ, tôi có thể thay đổi thời gian và múi giờ và nó sẽ không ảnh hưởng đến bản sao?
David

1
@David: Tôi biết nó đủ sâu cho DateTime vì tôi đã thử nó và nó phù hợp với tôi. Tôi đã không thử thay đổi múi giờ hoặc bất kỳ thứ nào khác, chỉ là ngày và giờ cơ bản.
rjmunro

3
Sử dụng Xdebug, var_dump ($ date1) báo cáo rằng nó chứa 'date' => string, 'timezone_type' => int & 'timezone' => string. Vì nó dường như không chứa bất kỳ mảng hoặc đối tượng nào, chỉ là vô hướng cơ bản, một bản sao nông sẽ ổn.
CJ Dennis

46

PHP 5.5.0 giới thiệu DateTimeImmutable . thêmsửa đổi các phương thức của lớp này trả về các đối tượng mới.

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

4
Lưu ý rằng rất tiếc bạn không thể hoán đổi a DateTimevới a DateTimeImmutable. Có ít nhất IntlDateFormatter::formatObjectlà không thích bất biến (trả về falsethay vì chuỗi được định dạng).
user276648 14/09/16

1
Oh! Bằng cách nào đó, tôi không bao giờ biết điều này tồn tại, mặc dù tôi đã mơ về nó từ lâu. và tất cả các con đường trở lại trong 5.5 ...
Ben

2
Giống như một số Noob Tôi vừa gặp cạm bẫy hướng đối tượng bằng cách sửa đổi của tôi DateTimeđối tượng trong một vòng lặp for: D này độc đáo giải quyết nó ...
Wilt

3
@ user276648 lỗi này bây giờ là cố định trong php 7.1.5 php.net/ChangeLog-7.php#7.1.5
jontro

11

TLDR:

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

(Bản sao nông được ban hành - DateTime sao chép sâu khiến (hiện tại) không có ý nghĩa )

Đơn giản như vậy :)

Giải thích "php tạo đối tượng datetime từ datetime khác":

  1. Các clonetừ khóa làm thường xuyên cạn copy - enaugh đối với trường hợp này (tại sao => xem dưới đây)
  2. Bao bọc nó bằng ()đánh giá biểu thức trả về đối tượng mới được tạo bằngclone
  3. ->modify() do đó được gọi và sửa đổi đối tượng mới
  4. DateTime::modify(...) tài liệu:

    Trả về đối tượng DateTime cho chuỗi phương thức hoặc FALSE khi bị lỗi.

  5. $date2hiện chứa bản sao / bản sao mới được tạo & sửa đổi, trong khi $date1vẫn không thay đổi

Tại sao bạn không cần phải sao chép sâu ở đây:

Deep copy / clone chỉ cần thiết khi bạn cần sao chép mục tiêu của các thuộc tính là tham chiếu , nhưng điều này:

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

kết quả đầu ra:

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

nên không có tài liệu tham khảo, chỉ cần các loại đơn giản => không cần phải copy sâu .


1

Bạn nên đổi DateTimethànhDateTimeImmutable

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

thì bạn có thể gọi bất kỳ phương thức nào trên DateTimemà không cần lo lắng về việc nó thay đổi


Đây thực sự là một câu trả lời cho một câu hỏi khác.
Billy ONeal

@BillyONeal Tôi có thể chưa giải thích đầy đủ cách thực hiện, Nhưng đây là giải pháp cho vấn đề này vì nguồn gốc của vấn đề này là cách gọi phương thức addtrên date2thay đổi giá trị của date1và không có cách nào để sao chép giá trị của DateTimebiến trừ khi bạn cóDateTimeImmutable
Hossein Shahdoost 21/09/17
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.