Specially for students of the Backend Developer in PHP course , they prepared a translation of an interesting article about the side effect of a popular tool.
Working with dates and times in PHP is sometimes annoying because it leads to unexpected bugs in the code:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s'));
Both the
$startdate
and
$finishdate
are in a hurry for three minutes, because methods like
add ()
,
sub()
or
modify()
also modify the DateTime object they are called for before returning it. The above example, of course, shows unwanted behavior.
We can fix this error by copying the object referenced before interacting with it, for example:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = clone $startedAt; $finishedAt->add(new DateInterval('PT3M'));
Every time I come across a clone in PHP code, it smells like a hack of someone's failed code architecture. In this case, we used cloning to avoid changing behavior, but at the same time, the code became ugly and acquired a lot of unnecessary noise.
Alternatively, the problem can be resolved by converting the original
DateTime
instance to
DateTimeImmutable
:
$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = DateTimeImmutable::createFromMutable($startedAt)->add(new DateInterval('PT3M'));
Why not use
DateTimeImmutable
from the very beginning?
Uncompromising Use of DateTimeImmutable
Instead of manually applying security methods to prevent unexpected changes when passing date / time objects, use
DateTimeImmutable
, which encapsulates methods, making your code more reliable.
$startedAt = new DateTimeImmutable('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s'));
In most cases, the concept of a date is considered as a value, we compare the dates by their values, and when we change the date, it becomes a different date. All this correlates perfectly with the concept of
Value Object , and one of the important characteristics of value objects is that they are immutable.
Detailed coding style
Immutability forces you to explicitly reassign a
DateTimeImmutable
object every time you interact with it, since it never changes its value, instead returning a copy. After many years of working with DateTime and because mutability is the default value in many imperative programming languages, itโs hard to get rid of the habit of using it and conform to a new style of writing code that promotes remapping:
$this->expiresAt = $this->expiresAt->modify('+1 week');
Statistical analysis tools such as
PHPStan and
one of its extensions can warn us if we omit the assignment and use
DateTimeImmutable
incorrectly.
However, such a cognitive bias towards variability is suppressed when we perform arithmetic operations on the values โโof primitives, for example:
$a + 3;
. In itself, this is perceived as a meaningless statement that clearly lacks a reassignment:
$a = $a + 3;
or
$A += 3;
. It would be cool to use something like this in the case of value objects, right?
Some programming languages โโhave syntactic sugar called
operator overloading, which allows you to implement operators in user-defined types and classes so that they behave just like primitive data types. I would not mind if PHP borrowed this trick from some other programming language, and we could write as follows:
$this->expiresAt += '1 week';
One-time calculations
Some people argue that in terms of performance, it is better to use
DateTime
, since the calculations are performed within the same execution area. This is acceptable, however, if you do not need to perform hundreds of operations, and you remember that links to old
DateTimeImmutable
objects will be collected by the garbage collector, in most cases, in practice, memory consumption will not be a problem.
Date / time libraries
Carbon is an extremely popular library that extends the Date / Time API in PHP, adding a rich feature set. More precisely, it extends the API of the
DateTime
mutable class, the use of which is contrary to the topic of this article.
Therefore, if you enjoy working with Carbon but prefer immutability, I suggest you familiarize yourself with
Chronos . This is a standalone library, which was originally based on Carbon, paying particular attention to providing immutable default date / time objects, but it also includes mutable options in case of need.
Edited (07/05/2019): It turned out that Carbon has an immutable date / time option, which is a big plus. However, the reason I prefer Chronos is that, unlike Carbon, it encourages and promotes default immutability, both in code and in documentation, and these are decisive factors in the context of this article.
Last thought
DateTimeImmutable
was first introduced back in ancient PHP 5.5, but to my surprise, many developers are discovering it just now. Use
DateTimeImmutable
by default whenever possible, but keep in mind some of the trade-offs that I talked about that I think are more a matter of habit and a shift in mindset.
That's all. By tradition, we are waiting for your comments, friends.