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
TIL that Kingfishers can hover just like hummingbird. I'll hopefully have some reasonable shots after I've gone through my nearly thousand shots today...
I hiked 9.8km in 3h40m47s
Updated a restaurant
Updated a vacant business
I walked 7.1km in 1h16m56s
I walked 3.9km in 45m56s
I walked 1.1km in 10m47s
I think this was my favourite photo of the set that I took. It's not at maximum coverage, but it does have a sun spot group (4508) just on the cusp of the terminator.
#eclipse #eclipse2026 #SolarEclipse #London #Astronomy #AstroPhotography #Photography
No totality here in London, but still had a great evening out in the local park and taking some snaps.
I did have a nice Solar Filter that I made for my lens, and had many conversations with people from different backgrounds. Kids loves to look through my filter after I was done (ran out of battery power).
More photos later...
I walked 0.9km in 9m28s
I walked 1.2km in 19m05s
Merged pull request #1100
Fixed issue #2267: Application slow when debugger is not listening on…
I walked 4.7km in 48m18s
I walked 3.0km in 1h10m24s
Is there somebody who runs PHP on native windows (with Xdebug) and can run a simple script for me?
It must be in a file (`test.php`):
```
<?php
echo microtime(true), "\n";
xdebug_connect_to_client();
echo microtime(true), "\n";
```And run like:
php -dxdebug.log_level=11 -dxdebug.log=c:\temp\xdebug-i.log -dxdebug.client_host=localhost -dxdebug.client_port=9111 -dxdebug.mode=debug c:\temp\test.php
With the port 9111 not open.
Would love to see the output and log file (in a DM).
I walked 5.5km in 55m06s
@harry_wood This is this list of 100 Go-mistakes that I was talking about: https://100go.co/
Updated a crossing
I walked 2.1km in 20m08s
Enjoying my afternoon and evening out at Lords for some cricket!
I walked 3.2km in 38m54s
I walked 9.1km in 1h34m28s
Updated a gate
Updated a bakery shop




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