All of you probably use the Carbon class in Laravel for date/time operations. But have you heard about CarbonImmutable? It may rescue you from one potential bug. Let me explain its use case in this short tutorial.
Let's say we have a variable, which is a carbon instance. It shows 9 am.
$now = now()= Illuminate\Support\Carbon @1701769227 {#8589 date: 2023-12-05 09:40:27.931975 UTC (+00:00), }
The
now()
function is aCarbon\Carbon::now()
shorter version.
For example, let's introduce a variable $threeHoursLater
.
$threeHoursLater = $now->addHours(3);= Illuminate\Support\Carbon @1701780027 {#8589 date: 2023-12-05 12:40:27.931975 UTC (+00:00), }
We can see the result three hours later, at 12 pm.
But now, what if from the original $now
you want to create a four hours later. We expect the result to be 13 because 9+4, but we get 16.
$threeHoursLater = $now->addHours(4);= Illuminate\Support\Carbon @1701794427 {#8589 date: 2023-12-05 16:40:27.931975 UTC (+00:00), }
It is because of how Carbon works. It not only assigns the value to the new variable but also changes the original object. This means whatever new operation you launch on the same object, it will be from the last change of that object.
That's why there is a separate class called CarbonImmutable
. Assigning the now value, you won't see any difference.
$now = Carbon\CarbonImmutable::now();= Carbon\CarbonImmutable @1701770010 {#8596 date: 2023-12-05 09:53:30.203054 UTC (+00:00), }
After adding three hours later, we see the same 12 pm result.
$threeHoursLater = $now->addHours(3);= Carbon\CarbonImmutable @1701780810 {#8590 date: 2023-12-05 12:53:30.203054 UTC (+00:00), }
But the main difference is setting the third variable after four hours.
$threeHoursLater = $now->addHours(4);= Carbon\CarbonImmutable @1701784410 {#8586 date: 2023-12-05 13:53:30.203054 UTC (+00:00), }
Now, we get the expected result. Now, the operation is done from the original $now
variable value.
If you want to watch this lesson as a video, there is a YouTube video Carbon VS CarbonImmutable in Laravel: Avoid Potential Errors.
Or remember always use a ->copy() method.
That is impossible. Humans do make mistakes and they can be costly :) That's why immutable exists!
Damn i have to say that is annoying behaviour on laravels side. Why is the immutable class not default? i think i have to change some code in my app.. :/
because of tradeoff... Immutable will have some extra performance cost..
Actually this is not a bug or a problem with Laravel or Carbon, this is the default behaviour of PHP Object Oriented Programming.
PHP objects are passed by reference by default. https://www.php.net/manual/en/language.oop5.references.php
In order to have a copy, or new object you use 'clone'.
If you look at the source code for CarbonImmutable object which extends DateTimeImmutable, the additional method clone is used, which creates a new object.
And looking at the class Carbon extends DateTime behave the same as Immutable objects, except they modify itself when modification methods such as DateTime::modify() are called. https://www.php.net/manual/en/class.datetime.php
All by design, not a bug.
You are correct. But using Carbon instead of CarbonImmutable can definitely cause bugs in your system. So while everything you said is true - there's one crucial aspect - people treat this as a "bug" and google it as such.
In any case, understanding OOP and how things work is mandatory, but this is just something people never think about. Well, unless it's too late!