停止使用日期时间

他们专门为“后端PHP开发人员”课程的学生准备了一篇有关流行工具副作用的有趣文章的翻译。





在PHP中使用日期和时间有时会很烦人,因为这会导致代码中出现意外错误:

$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s')); //2019-06-30 10:03:00 var_dump($finishedAt->format('Ymd H:i:s')); //2019-06-30 10:03:00 

$startdate$finishdate都急着三分钟,因为诸如add ()sub()$finishdate modify()还会在返回之前对其进行修改的DateTime对象进行修改。 当然,以上示例显示了不良行为。

我们可以通过在与对象进行交互之前复制引用的对象来解决此错误,例如:

 $startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = clone $startedAt; $finishedAt->add(new DateInterval('PT3M')); 

每当我遇到一个PHP代码克隆时,它闻起来就像是有人的失败代码体系被黑客入侵。 在这种情况下,我们使用克隆来避免改变行为,但是与此同时,代码变得丑陋并获得了很多不必要的噪音。

或者,可以通过将原始DateTime实例转换为DateTime来解决此问题:

 $startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = DateTimeImmutable::createFromMutable($startedAt)->add(new DateInterval('PT3M')); 

为什么不从一开始就使用DateTimeImmutable

毫不妥协地使用DateTimeImmutable


使用DateTimeImmutable封装方法,而不是手动应用安全方法来防止在传递日期/时间对象时发生意外更改,该方法封装了方法,从而使您的代码更可靠。

 $startedAt = new DateTimeImmutable('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s')); //2019-06-30 10:00:00 var_dump($finishedAt->format('Ymd H:i:s')); //2019-06-30 10:03:00 

在大多数情况下,日期的概念被视为一个值,我们将日期与它们的值进行比较,并且当我们更改日期时,日期将变为不同的日期。 所有这些都与价值对象的概念完美相关, 价值对象的重要特征之一是它们是不可变的。

详细的编码风格


不变性迫使您每次与DateTimeImmutable对象进行交互时都要明确地重新分配它,因为它从不更改其值,而是返回一个副本。 在使用DateTime多年之后,由于可变性是许多命令式编程语言中的默认设置,因此很难摆脱使用它的习惯,并且要遵循促进重新映射的新代码编写风格:

 $this->expiresAt = $this->expiresAt->modify('+1 week'); 

如果我们忽略分配并错误地使用DateTimeImmutable ,则统计分析工具(例如PHPStan 及其扩展之一 )会警告我们。

但是,当我们对基元的值执行算术运算时,例如$a + 3;变化,这种对变异性的认知偏差就被抑制$a + 3; 。 就其本身而言,这被认为是毫无意义的陈述,显然缺少重新分配: $a = $a + 3;$A += 3; 。 在值对象的情况下使用这样的东西会很酷,对吧?

一些编程语言具有称为操作符重载的语法糖,它允许您在用户定义的类型和类中实现操作符,以便它们的行为就像原始数据类型一样。 我不介意PHP是否从其他编程语言中借用了这个技巧,我们可以这样编写:

 $this->expiresAt += '1 week'; 

一次性计算


有人认为,就性能而言,最好使用DateTime ,因为计算是在同一执行区域内执行的。 但是,这是可以接受的,如果您不需要执行数百个操作,并且您还记得垃圾收集器将收集到旧的DateTimeImmutable对象的链接,那么在大多数情况下,实际上,内存消耗不会成为问题。

日期/时间库


Carbon是一个非常流行的库,它扩展了PHP中的Date / Time API,并添加了丰富的功能集。 更准确地说,它扩展了DateTime可变类的API,其使用与本文的主题背道而驰。

因此,如果您喜欢使用Carbon,但更喜欢不变性,建议您熟悉Chronos 。 这是一个独立的库,最初基于Carbon,特别注意提供不可变的默认日期/时间对象,但在需要时还包括可变选项。
编辑(07/05/2019):事实证明Carbon拥有不可变的日期/时间选项,这是一个很大的优点。 但是,我喜欢Chronos的原因是,与Carbon不同,它鼓励并促进代码和文档中的默认不变性,而这是本文中的决定性因素。

最后的想法


DateTimeImmutable最早是在古老的PHP 5.5中引入的,但令我惊讶的是,许多开发人员刚刚才发现它。 尽可能在默认情况下默认使用DateTimeImmutable ,但请记住我谈到的一些权衡因素,我认为这更多是习惯问题和思维方式的转变。

仅此而已。 按照传统,我们正在等待您的评论,朋友。

Source: https://habr.com/ru/post/zh-CN464253/


All Articles