【问题标题】:How do I deep copy a DateTime object?如何深复制 DateTime 对象?
【发布时间】:2011-02-04 11:40:44
【问题描述】:
$date1 = $date2 = new DateTime();
$date2->add(new DateInterval('P3Y'));

现在$date1$date2 包含相同的日期——三年后。我想创建两个单独的日期时间,一个是从字符串中解析出来的,另一个是添加了三年。目前我已经这样破解了它:

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

但这似乎是一个可怕的黑客行为。是否有“正确”的方法来深复制 DateTime 对象?

【问题讨论】:

    标签: php datetime


    【解决方案1】:
    $date1 = new DateTime();
    $date2 = new DateTime();
    $date2->add(new DateInterval('P3Y'));
    

    更新:

    如果您想复制而不是引用现有的 DT 对象,请使用 clone,而不是 =

    $a = clone $b;

    【讨论】:

    • 我在示例中使用了一个新的 DateTime 来演示这一点,但现在假设 DateTime 是从一些我不能再次调用的不透明 API 返回的。例如,我有一个处理订单的函数,该函数返回一个 DateTime,即客户下次可以下订单的时间。调用该函数来创建副本会产生我不想要的副作用。
    • 我还没有实际测试过,但在 php.net 上提到这仅适用于 PHP 5.3 及更高版本。
    • @hugo:是的,DateTime 类需要 PHP 5.3。
    • 就在我以为我已经掌握了 PHP 的时候,我学习了一个新的运算符。
    • 必须这样做才能将现有的 Carbon 对象复制到另一个变量。这行得通。
    【解决方案2】:

    使用clone 操作符克隆日期:

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

    默认情况下克隆很浅,但对于 DateTime 来说足够深。在您自己的对象中,您可以定义 __clone() 魔术方法来克隆在父对象更改时有意义的属性(即子对象)。

    (我不确定为什么文档认为需要克隆对象的一个​​很好的例子是 GTK。谁在 PHP 中使用 GTK?)

    【讨论】:

    • 谢谢你的回答,但你怎么知道它对 DateTime 来说足够深?哪些属性仍然是引用,哪些是按值复制的?比如我可以更改时间和时区,不会影响克隆?
    • @David:我知道它对于 DateTime 来说已经足够深了,因为我试过了,它对我有用。我没有尝试更改时区或任何其他内容,只是更改了基本时间和日期。
    • 使用 Xdebug,var_dump($date1) 报告它包含 'date' => 字符串,'timezone_type' => int & 'timezone' => 字符串。由于它似乎不包含任何数组或对象,仅包含基本标量,因此浅克隆应该没问题。
    • 这是最清晰、最能自我记录的答案,在代码库中遇到时非常有用,不会让同事感到困惑。
    【解决方案3】:

    PHP 5.5.0 引入了DateTimeImmutable。此类的addmodify 方法返回新对象。

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

    【讨论】:

    • 请注意,很遗憾,您不能将DateTimeDateTimeImmutable 交换。至少有 IntlDateFormatter::formatObject 不喜欢不可变(返回 false 而不是格式化字符串)。
    • 哦!我不知何故从来不知道它的存在,尽管我一直梦想着它。并一直回到 5.5...
    • 像一些菜鸟一样,我刚刚通过在 for 循环中修改我的 DateTime 对象遇到了一个面向对象的陷阱:D 这很好地解决了它......
    • @user276648 此错误现已在 php 7.1.5 php.net/ChangeLog-7.php#7.1.5 中修复
    【解决方案4】:

    您应该将DateTime 更改为DateTimeImmutable

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

    然后你可以调用DateTime 上的任何方法,而不用担心它会改变

    【讨论】:

    • 这确实是一个不同问题的答案。
    • @BillyONeal 我可能没有完全解释如何,但这是解决此问题的方法,因为此问题的根源是如何在 date2 上调用方法 add 更改 @987654327 的值@ 并且没有办法复制 DateTime 变量的值,除非你有一个 DateTimeImmutable
    【解决方案5】:

    TLDR:

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

    (浅拷贝就够了 - 深拷贝 DateTime (目前)没有意义

    就这么简单:)

    解释“php create datetime object from another datetime”:

    1. clone 关键字使常规 shallow 复制 - 足以应付这种情况(为什么 => 见下文)
    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"
    }
    

    所以没有引用,只有简单类型 => 不需要复制。

    【讨论】:

    • 我想看看克隆做浅拷贝的源码。这是否意味着也可以在 PHP 中进行深度复制?我认为只有“克隆”可以复制对象,而不仅仅是引用,这对我来说听起来像是深拷贝?
    • @barell 不确定我是否理解您的要求,但 clone 关键字会 shallow copy => 复制简单的值,例如 ints ,字符串,...,如果有对另一个对象的引用,它只复制对该对象的引用。深拷贝意味着为每个复制的对象创建一个新对象等 - 现在更清楚了吗?
    • 我不知道克隆对象部分中的对象引用。谢谢,知道这很有用。我希望我早点查看php.net/manual/en/language.oop5.cloning.php
    • @barell 是的,这就是浅拷贝和深拷贝之间的区别 :) 很高兴我能帮上忙! :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-25
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 2012-12-15
    相关资源
    最近更新 更多