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
Merged pull request #1029
Reflow some comments
Add comments, add end of file newlines, fix php 8.5 compilation
Benchmark Xdebug performance
Merged pull request #1051
PHP 8.6: printf() is now optimised out if it only uses %s and %d (and…
PHP 8.6: The object IDs of objects changed in tests
PHP 8.6: ZSTR_INIT_LITERAL() no longer takes non-literals, so use zen…
PHP 8.6: WRONG_PARAM_COUNT has been removed in favour of zend_wrong_p…
PHP 8.6: zval_dtor() has been deprecated in favour of zval_ptr_dtor_n…
Update test for version constraints, as well as the error messages
I walked 7.4km in 1h38m15s
I just watched an interesting and lovely documentary on BBC Four: The Magical World of Moss.
It's on the iPlayer for another month:
https://www.bbc.co.uk/programmes/m001hqthI walked 8.2km in 1h26m34s
Merge branch 'xdebug_3_5'
Document to use PIE instead of PECL in the README
I walked 9.2km in 1h45m44s
I walked 10.1km in 1h40m23s
If we must show off a little…
In more info innocent times, the hat riots caused some spankings...
100 Years Ago Men and Boys Fought on the Streets of New York Over Wearing Straw Hats Past Summer | The New York Public Library
https://www.nypl.org/blog/2022/09/23/straw-hat-riots-nyc


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