Skip to content

Commit

Permalink
Merge pull request #8350 from Ocramius/fix/datetime-constructor-is-no…
Browse files Browse the repository at this point in the history
…t-immutable

Added better stubs for `DateTimeImmutable`, highlighting how the constructor is **NOT** immutable
  • Loading branch information
orklah committed Aug 7, 2022
2 parents 1ef3851 + 1382877 commit 57fcc39
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 43 deletions.
17 changes: 0 additions & 17 deletions dictionaries/CallMap.php
Expand Up @@ -1793,27 +1793,10 @@
'DateTime::setTimestamp' => ['static', 'unixtimestamp'=>'int'],
'DateTime::setTimezone' => ['static', 'timezone'=>'DateTimeZone'],
'DateTime::sub' => ['static', 'interval'=>'DateInterval'],
'DateTimeImmutable::__construct' => ['void', 'time='=>'string'],
'DateTimeImmutable::__construct\'1' => ['void', 'time'=>'?string', 'timezone'=>'?DateTimeZone'],
'DateTimeImmutable::__set_state' => ['static', 'array'=>'array'],
'DateTimeImmutable::__wakeup' => ['void'],
'DateTimeImmutable::add' => ['static', 'interval'=>'DateInterval'],
'DateTimeImmutable::createFromFormat' => ['static|false', 'format'=>'string', 'time'=>'string', 'timezone='=>'?DateTimeZone'],
'DateTimeImmutable::createFromInterface' => ['self', 'object' => 'DateTimeInterface'],
'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'],
'DateTimeImmutable::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'],
'DateTimeImmutable::format' => ['string', 'format'=>'string'],
'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array<int,string>,error_count:int,errors:array<int,string>}'],
'DateTimeImmutable::getOffset' => ['int'],
'DateTimeImmutable::getTimestamp' => ['int|false'],
'DateTimeImmutable::getTimezone' => ['DateTimeZone|false'],
'DateTimeImmutable::modify' => ['static', 'modify'=>'string'],
'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'],
'DateTimeImmutable::setISODate' => ['static|false', 'year'=>'int', 'week'=>'int', 'day='=>'int'],
'DateTimeImmutable::setTime' => ['static|false', 'hour'=>'int', 'minute'=>'int', 'second='=>'int', 'microseconds='=>'int'],
'DateTimeImmutable::setTimestamp' => ['static|false', 'unixtimestamp'=>'int'],
'DateTimeImmutable::setTimezone' => ['static|false', 'timezone'=>'DateTimeZone'],
'DateTimeImmutable::sub' => ['static|false', 'interval'=>'DateInterval'],
'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'],
'DateTimeInterface::format' => ['string', 'format'=>'string'],
'DateTimeInterface::getOffset' => ['int'],
Expand Down
4 changes: 0 additions & 4 deletions dictionaries/CallMap_80_delta.php
Expand Up @@ -41,10 +41,6 @@
'old' => ['string|false', 'format'=>'string'],
'new' => ['string', 'format'=>'string'],
],
'DateTimeImmutable::format' => [
'old' => ['string|false', 'format'=>'string'],
'new' => ['string', 'format'=>'string'],
],
'DateTimeZone::listIdentifiers' => [
'old' => ['list<string>|false', 'timezoneGroup='=>'int', 'countryCode='=>'string|null'],
'new' => ['list<string>', 'timezoneGroup='=>'int', 'countryCode='=>'string|null'],
Expand Down
17 changes: 0 additions & 17 deletions dictionaries/CallMap_historical.php
Expand Up @@ -1055,26 +1055,9 @@
'DateTime::setTimestamp' => ['static', 'unixtimestamp'=>'int'],
'DateTime::setTimezone' => ['static', 'timezone'=>'DateTimeZone'],
'DateTime::sub' => ['static', 'interval'=>'DateInterval'],
'DateTimeImmutable::__construct' => ['void', 'time='=>'string'],
'DateTimeImmutable::__construct\'1' => ['void', 'time'=>'?string', 'timezone'=>'?DateTimeZone'],
'DateTimeImmutable::__set_state' => ['static', 'array'=>'array'],
'DateTimeImmutable::__wakeup' => ['void'],
'DateTimeImmutable::add' => ['static', 'interval'=>'DateInterval'],
'DateTimeImmutable::createFromFormat' => ['static|false', 'format'=>'string', 'time'=>'string', 'timezone='=>'?DateTimeZone'],
'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'],
'DateTimeImmutable::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'],
'DateTimeImmutable::format' => ['string|false', 'format'=>'string'],
'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array<int,string>,error_count:int,errors:array<int,string>}'],
'DateTimeImmutable::getOffset' => ['int'],
'DateTimeImmutable::getTimestamp' => ['int|false'],
'DateTimeImmutable::getTimezone' => ['DateTimeZone|false'],
'DateTimeImmutable::modify' => ['static', 'modify'=>'string'],
'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'],
'DateTimeImmutable::setISODate' => ['static|false', 'year'=>'int', 'week'=>'int', 'day='=>'int'],
'DateTimeImmutable::setTime' => ['static|false', 'hour'=>'int', 'minute'=>'int', 'second='=>'int', 'microseconds='=>'int'],
'DateTimeImmutable::setTimestamp' => ['static|false', 'unixtimestamp'=>'int'],
'DateTimeImmutable::setTimezone' => ['static|false', 'timezone'=>'DateTimeZone'],
'DateTimeImmutable::sub' => ['static|false', 'interval'=>'DateInterval'],
'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'],
'DateTimeInterface::format' => ['string', 'format'=>'string'],
'DateTimeInterface::getOffset' => ['int'],
Expand Down
100 changes: 97 additions & 3 deletions stubs/CoreImmutableClasses.phpstub
@@ -1,11 +1,105 @@
<?php

/**
* @psalm-immutable
*/
class DateTimeImmutable implements DateTimeInterface
{
public function __construct(string $datetime = "now", DateTimeZone $timezone = null) {}

/**
* @psalm-mutation-free
* @return static|false
*/
public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) {}

/**
* @psalm-mutation-free
*
* @param string $format
*
* @return ($format is non-empty-string ? non-empty-string : string)
*/
public function format($format) {}

/**
* @psalm-mutation-free
* @return DateTimeZone
*/
public function getTimezone() {}

/**
* @psalm-mutation-free
* @return int
*/
public function getOffset() {}

/**
* @psalm-mutation-free
* @return int
*/
public function getTimestamp() {}

/**
* @psalm-mutation-free
* @param bool $absolute
* @return DateInterval
*/
public function diff(DateTimeInterface $targetObject, $absolute = false) {}

/**
* @psalm-mutation-free
* @return static|false
*/
public function modify(string $modifier) {}

/**
* @psalm-mutation-free
* @return static
*/
public function add(DateInterval $interval) {}

/**
* @psalm-mutation-free
* @return static|false this method can fail in case an {@see DateInterval} with relative
* week days is passed in.
*
* @see https://github.com/php/php-src/blob/534127d3b22b193ffb9511c4447584f0d2bd4e24/ext/date/php_date.c#L3157-L3160
*/
public function sub(DateInterval $interval) {}

/**
* @psalm-mutation-free
* @return static
*/
public function setTimezone(DateTimeZone $timezone) {}

/**
* @psalm-mutation-free
* @return static
*/
public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) {}

/**
* @psalm-mutation-free
* @return static
*/
public function setDate(int $year, int $month, int $day) {}

/**
* @psalm-mutation-free
* @return static
*/
public function setISODate(int $year, int $week, int $dayOfWeek = 1) {}

/**
* @psalm-mutation-free
* @return static
*/
public function setTimestamp(int $unixtimestamp) {}

/**
* @psalm-mutation-free
* @return static
*/
public static function createFromMutable(DateTime $object) {}
}

/**
Expand Down
3 changes: 1 addition & 2 deletions tests/MethodCallTest.php
Expand Up @@ -264,7 +264,6 @@ public static function main(): void {
],
'dateTimeImmutableStatic' => [
'<?php
/** @psalm-immutable */
final class MyDate extends DateTimeImmutable {}
$today = new MyDate();
Expand All @@ -273,7 +272,7 @@ final class MyDate extends DateTimeImmutable {}
$b = (new DateTimeImmutable())->modify("+3 hours");',
'assertions' => [
'$yesterday' => 'MyDate|false',
'$b' => 'DateTimeImmutable',
'$b' => 'DateTimeImmutable|false',
],
],
'magicCall' => [
Expand Down

0 comments on commit 57fcc39

Please sign in to comment.