DateTimeImmutable
The first time that my improved DateTime support made its way into PHP was officially in PHP 5.1, although the more advanced features such as the DateTime class only made it appearance in PHP 5.2. Since its introduction the DateTime class implementation suffered from one design mistake — arguably not something that even an RFC would have highlighted.
In PHP, if you do the following:
<?php
function formatNextMondayFromNow( DateTime $dt )
{
return $dt->modify( 'next monday' )->format( 'Y-m-d' );
}
$d = new DateTime();
echo formatNextMondayFromNow( $d ), "\n";
echo $d->format( 'Y-m-d' ), "\n";
?>
It displays:
2014-02-17 2014-02-17
The modify() method does not only return the modified DateTime object, but also changes the DateTime object it was called on. In an API like above, this is of course totally unexpected and the only way to avoid this behaviour is to do the following instead:
echo formatNextMondayFromNow( clone $d ), "\n";
This mutability property that all modifying methods of the DateTime class have is highly annoying, and something that I would now rather remove. But of course we cannot as that would break backwards compatibility.
So in PHP 5.5, after a few stumbles, I finally managed to rectify this. I did not change the original class's behaviour, but instead, I added a new class. The new DateTimeImmutable class which does not display this "mutable" behaviour, and only returns the modified object:
<?php
function formatNextMondayFromNow( DateTimeImmutable $dt )
{
return $dt->modify( 'next monday' )->format( 'Y-m-d' );
}
$d = new DateTimeImmutable();
echo formatNextMondayFromNow( $d ), "\n";
echo $d->format( 'Y-m-d' ), "\n";
?>
Which displays:
2014-02-17 2014-02-11
Both the old DateTime and the new DateTimeImmutable classes implement the DateTimeInterface interface. This interface defines all the methods that both classes implement. Of course, they can not be methods that change the object. That is why the interface is restricted to formatting and other read-only methods, such as getTimezone.
Perhaps in the future (PHP 6), we can replace the mutable DateTime class with the new DateTimeImmutable variant. Until then, you will have to take care of this yourself!
Comments
Hi Derick,
the DateTimeImmutable class should have another static method:
create(DateTimeInterface $dt) {
return $dt instanceof DateTimeImmutable
? $dt
: new DateTimeImmutable($dt)
}
such a method is important in a constructor:
class MyClass {
private $immutableDateTime;
public function __construct(DateTimeInterface $dt) {
$this->immutableDateTime = DateTimeImmutable::create($dt);
}
}
You don't want somebody from outside to modify the internal state of MyClass.
I miss a direct way to cast from one to the other.
Too bad you are using such an example to justify DateTimeImmutable. The formatNextMondayFromNow() should clone the date:
function formatNextMondayFromNow( DateTime $dt )
{
$dt = clone $dt;
return $dt->modify( 'next monday' )->format( 'Y-m-d' );
}
Or better, be broken into two parts:
$now->next('monday')->format();
Where next() would return a new instance representing the next something, here "monday".
To me, with this example, it looks like DateTimeImmutable is a solution to bad usages.
I created an extension to DateTime and all the yesterday, monday, utc, local, … magic properties return new instances. Only modify() actually modifies the instance. I'm quite happy with it.
@Thomas, @Per: I have added to PHP-5.6 a new method DateTimeImmutable::createFromMutable() that allows you to instantiate a non-mutable DateTimeImmutable variant based off a mutable DateTime object.
DateTimeImmutable::createFromMutable() is a good addition but conversely being able to do the opposite would have been great too DateTime::createFromImmutable()
This way switching from one class to the other would be less painfull.
Is it possible to make DateTime immutable in PHP 7 to avoid this madness?
@Daniel: Possible, but that is going to break quite a lot of code, and it's going to be difficult to spot/test. So that's not happening.
While we're at it. It would of course be nice to get rid of a mutable DateTime in PHP7 but I'm afraid that this would be too much of a break and Derick already commented on how important he considers backward compatibility.
But please add a method DateTimeImmutable::from(DateTimeInterface $dt).
This method is needed all the time when a Constructor accepts a DateTimeInterface. In this case one can either take a given DateTimeImmutable as is of create a new DateTimeImmutable from a given DateTime.
@Thomas: That method does exist:
And PHP 7 will have the createFromMutable method on DateTime too.
Life Line
I just made and ate, a bowl full of bacon fried Brussels Sprouts. Not under duress, and out of my own free will.
Added new residential building
Created a hairdresser shop; Confirmed a convenience shop and a dry_cleaning shop
Created a building_materials shop, a vacant shop, and 4 other objects; Confirmed a hairdresser shop, a cafe, and 2 other objects
I walked 8.3km in 1h33m44s
I walked 1.1km in 9m53s
Updated 2 buildings, a company office, and a bakery shop; Confirmed 2 restaurants, a veterinary, and 9 other objects
I walked 10.0km in 1h50m19s
Merge branch 'xdebug_3_5'
Merged pull request #1064
Fixed issue #2404: Remove debugging line
Created 3 crossings and a waste_basket; Updated a bus_stop and a waste_basket; Deleted a waste_basket
I walked 8.5km in 1h46m06s
Created an entrance and a staircase entrance
I walked 7.5km in 1h25m21s
Success (well, 80%) with my first real soldering-required project!
I have a board with components that can power three (different) Neopixel shapes in a (prototype) Lego enclosure.
Need to make lots of nice 8x8 pixel art and fonts now though, and get a properly designed Lego box.
The only problem is a wonky connector for my third Neopixel — I'll have to resolder that.
I also have software so that I can change everything through Wi-Fi.
I walked 8.0km in 1h32m34s
Mark roads and tracks as private, as I found out yesterday and got stuck behind a locked fence with a security guard
Updated a bird_hide
Updated a bench
This is not an accessible site. Needs more updates too
This is not an accessible site. Needs more updates too
Created 2 gates
I hiked 18.6km in 4h24m32s
I walked 6.2km in 59m46s



Shortlink
This article has a short URL available: https://drck.me/dti-avg