From b4b2bc66c7478e39f427114223ef7394f7b11250 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Sun, 31 Jul 2022 18:02:30 +0200 Subject: [PATCH 01/22] Added better stubs for `DateTimeImmutable`, highlighting how the constructor is **NOT** immutable `DateTimeImmutable` is **almost** immutable: `DateTimeImmutable::__construct()` is in fact not a pure method, since `new DateTimeImmutable('now')` produces a different value at each instantiation (by design). This change makes sure that `DateTimeImmutable` loses its `@psalm-immutable` class-level marker, preventing downstream misuse of the constructor inside otherwise referentially transparent code. Note: only pure methods are stubbed here: all other methods declared by `DateTimeImmutable` (parent interface) are NOT present here, and are either inferred from runtime reflection, or `CallMap*.php` definitions. Methods are sorted in the order defined by reflection on PHP 8.1.8, at the time of writing this ( https://3v4l.org/3TGg8 ). Following simplistic snippet was used to infer the current signature: ```php getName() . '(' . implode(',', array_map(function ($p) { return $p->getType() . ' $' . $p->getName() . ($p->isOptional() ? ' = ' . var_export($p->getDefaultValue(), true) : ''); }, $m->getParameters())) . ')' . ($m->getReturnType() ? (': ' . $m->getReturnType()) : ''); }, $c->getMethods()); $properties = array_map(function ($m) { return $m->getName(); }, $c->getProperties()); var_dump($methods, $properties); ``` --- stubs/CoreImmutableClasses.phpstub | 101 ++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 405f89b43cd..325a1c2a2ce 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -1,11 +1,106 @@ Date: Sun, 31 Jul 2022 18:08:01 +0200 Subject: [PATCH 02/22] Removed `@psalm-immutable` marked from `MyDate` extending `DateTimeImmutable` `DateTimeImmutable` is not really immutable, therefore this marker was wrong upfront --- tests/MethodCallTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 843cce11941..956536988a1 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -264,7 +264,6 @@ public static function main(): void { ], 'dateTimeImmutableStatic' => [ ' Date: Sun, 31 Jul 2022 18:12:07 +0200 Subject: [PATCH 03/22] `DateTimeImmutable#sub()` always returns another `static` instance, never `false` --- tests/MethodCallTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 956536988a1..76c033f56f5 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -271,7 +271,7 @@ final class MyDate extends DateTimeImmutable {} $b = (new DateTimeImmutable())->modify("+3 hours");', 'assertions' => [ - '$yesterday' => 'MyDate|false', + '$yesterday' => 'MyDate', '$b' => 'DateTimeImmutable', ], ], From 68978b9e19c70036e6e5077a6234af513afdc77f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Mon, 1 Aug 2022 10:08:35 +0200 Subject: [PATCH 04/22] s/psalm-pure/psalm-mutation-free, since psalm-mutation-free is safer to use Ref: https://github.com/vimeo/psalm/pull/8350/files/c205d652d1e9afd9510db59e72c3fd0a4a093b3d#r934032422 The idea is that `@psalm-pure` disallows `$this` usage in child classes, which is not wanted, while `@psalm-mutation-free` allows it. By using `@psalm-mutation-free`, we don't completely destroy inheritance use-cases based on internal (immutable) state. --- stubs/CoreImmutableClasses.phpstub | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 325a1c2a2ce..2d6555c3df2 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -5,99 +5,99 @@ class DateTimeImmutable implements DateTimeInterface public function __construct(string $datetime = "now", DateTimeZone $timezone = null) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static|false */ public static function createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) {} /** - * @psalm-pure + * @psalm-mutation-free * @param string $format * @return string */ public function format($format) {} /** - * @psalm-pure + * @psalm-mutation-free * @return DateTimeZone */ public function getTimezone() {} /** - * @psalm-pure + * @psalm-mutation-free * @return int */ public function getOffset() {} /** - * @psalm-pure + * @psalm-mutation-free * @return int */ public function getTimestamp() {} /** - * @psalm-pure + * @psalm-mutation-free * @param bool $absolute * @return DateInterval */ public function diff(DateTimeInterface $targetObject, $absolute = false) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static */ public function modify(string $modifier) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static */ public function add(DateInterval $interval) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static */ public function sub(DateInterval $interval) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static|false */ public function setTimezone(DateTimeZone $timezone) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static|false */ public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static|false */ public function setDate(int $year, int $month, int $day) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static|false */ public function setISODate(int $year, int $week, int $dayOfWeek = 1) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static|false */ public function setTimestamp(int $unixtimestamp) {} /** - * @psalm-pure + * @psalm-mutation-free * @return static */ public static function createFromMutable(DateTime $object) {} /** - * @psalm-pure + * @psalm-mutation-free * @return self */ public static function createFromInterface(DateTimeInterface $object) {} From dc7d26abb1a8312d75206a478c690e61eeb96b9a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:22:27 +0200 Subject: [PATCH 05/22] Removed `DateTimeImmutable::__construct` from the CallMap: fully covered by stub --- dictionaries/CallMap.php | 2 -- dictionaries/CallMap_historical.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 219e6372641..b34c6c59cb1 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1793,8 +1793,6 @@ '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'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index a5c4875aa71..ebce06c0070 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1055,8 +1055,6 @@ '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'], From 267d76088d7bbdc19c31bbc7615fc3e5bebed1bc Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:23:00 +0200 Subject: [PATCH 06/22] Removed `DateTimeImmutable::sub()` from the CallMap: fully covered by stub --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - stubs/CoreImmutableClasses.phpstub | 5 ++++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index b34c6c59cb1..7870727ba9f 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1811,7 +1811,6 @@ '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'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index ebce06c0070..d387582cb51 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1072,7 +1072,6 @@ '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'], diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 2d6555c3df2..e5db68d978f 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -56,7 +56,10 @@ class DateTimeImmutable implements DateTimeInterface /** * @psalm-mutation-free - * @return static + * @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) {} From 58ca4e0b73eb52c6b171ef5b59f776e70cc4288b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:23:43 +0200 Subject: [PATCH 07/22] Removed `DateTimeImmutable::createFromFormat()` from the CallMap: fully covered by stub --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - 2 files changed, 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 7870727ba9f..20f68f13e05 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1796,7 +1796,6 @@ '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'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index d387582cb51..d0fc4bc2b02 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1058,7 +1058,6 @@ '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'], From 7ee12c74935dabe2b481a322acc02ba87217b049 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:24:35 +0200 Subject: [PATCH 08/22] Removed `DateTimeImmutable::format()` from the CallMap: fully covered by stub Note: some conditional return type magic was required here. See: https://github.com/vimeo/psalm/pull/8350#discussion_r937089212 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_80_delta.php | 4 ---- dictionaries/CallMap_historical.php | 1 - stubs/CoreImmutableClasses.phpstub | 4 +++- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 20f68f13e05..40633b8e18c 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1799,7 +1799,6 @@ '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,error_count:int,errors:array}'], 'DateTimeImmutable::getOffset' => ['int'], 'DateTimeImmutable::getTimestamp' => ['int|false'], diff --git a/dictionaries/CallMap_80_delta.php b/dictionaries/CallMap_80_delta.php index afeb1bc4133..4200608e06a 100644 --- a/dictionaries/CallMap_80_delta.php +++ b/dictionaries/CallMap_80_delta.php @@ -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|false', 'timezoneGroup='=>'int', 'countryCode='=>'string|null'], 'new' => ['list', 'timezoneGroup='=>'int', 'countryCode='=>'string|null'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index d0fc4bc2b02..013d88e0071 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1060,7 +1060,6 @@ 'DateTimeImmutable::add' => ['static', 'interval'=>'DateInterval'], '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,error_count:int,errors:array}'], 'DateTimeImmutable::getOffset' => ['int'], 'DateTimeImmutable::getTimestamp' => ['int|false'], diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index e5db68d978f..c241cb4f17a 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -12,8 +12,10 @@ class DateTimeImmutable implements DateTimeInterface /** * @psalm-mutation-free + * * @param string $format - * @return string + * + * @return (\PHP_MAJOR_VERSION is int<0, 7> ? string|false : string) */ public function format($format) {} From 2b6fddf88d5408da418f808a75bb497b6886e186 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:26:59 +0200 Subject: [PATCH 09/22] Removed `DateTimeImmutable::getTimezone()` from the CallMap: fully covered by stub Note: also verified that a `DateTimeImmutable#getTimezone()` always returns a default timezone (initialized internally), and therefore restricted the type a bit. --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - 2 files changed, 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 40633b8e18c..52f26dba263 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1802,7 +1802,6 @@ 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], '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'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 013d88e0071..89193d9804e 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1063,7 +1063,6 @@ 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], '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'], From 1be04e0988286950cf34fb02ce60caad89c02be6 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:27:29 +0200 Subject: [PATCH 10/22] Removed `DateTimeImmutable::getOffset()` from the CallMap: fully covered by stub --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - 2 files changed, 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 52f26dba263..e1c4f97da8d 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1800,7 +1800,6 @@ 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], -'DateTimeImmutable::getOffset' => ['int'], 'DateTimeImmutable::getTimestamp' => ['int|false'], 'DateTimeImmutable::modify' => ['static', 'modify'=>'string'], 'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 89193d9804e..52c0d8c2dac 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1061,7 +1061,6 @@ 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], - 'DateTimeImmutable::getOffset' => ['int'], 'DateTimeImmutable::getTimestamp' => ['int|false'], 'DateTimeImmutable::modify' => ['static', 'modify'=>'string'], 'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'], From 002585b57ec014a40cf81f14cedc58936c1ddfd8 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:32:26 +0200 Subject: [PATCH 11/22] Removed `DateTimeImmutable::getTimestamp()` from the CallMap: fully covered by stub This also simplifies the return type from `int|false` to always `int`, since a timestamp can always be produced. Ref: https://github.com/php/php-src/blob/eff9aed1592f59cddb12d36a55dec0ccc3bbbfd6/ext/date/php_date.stub.php#L496-L500 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - 2 files changed, 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index e1c4f97da8d..651fdc08f85 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1800,7 +1800,6 @@ 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], -'DateTimeImmutable::getTimestamp' => ['int|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'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 52c0d8c2dac..98c7d172b0b 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1061,7 +1061,6 @@ 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], - 'DateTimeImmutable::getTimestamp' => ['int|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'], From 18557b8c7005a85ab7f6ffefbf0b6683a11ea4d2 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:34:31 +0200 Subject: [PATCH 12/22] Removed `DateTimeImmutable::diff()` from the CallMap: fully covered by stub --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - 2 files changed, 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 651fdc08f85..dc136691866 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1798,7 +1798,6 @@ 'DateTimeImmutable::add' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::createFromInterface' => ['self', 'object' => 'DateTimeInterface'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], -'DateTimeImmutable::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], 'DateTimeImmutable::modify' => ['static', 'modify'=>'string'], 'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 98c7d172b0b..9e96e514e0e 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1059,7 +1059,6 @@ 'DateTimeImmutable::__wakeup' => ['void'], 'DateTimeImmutable::add' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], - 'DateTimeImmutable::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], 'DateTimeImmutable::modify' => ['static', 'modify'=>'string'], 'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'], From 7cd3d49dc4de625b7b96a1c99c7c7f269acf6725 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:37:24 +0200 Subject: [PATCH 13/22] Removed `DateTimeImmutable::modify()` from the CallMap: fully covered by stub Also expanded the return type from `static` to `static|false`, since the operation can fail (with a warning too), such as in following example: https://3v4l.org/Xrjlc ```php modify('potato') ); ``` Produces ``` Warning: DateTimeImmutable::modify(): Failed to parse time string (potato) at position 0 (p): The timezone could not be found in the database in /in/Xrjlc on line 6 bool(false) ``` Ref: https://github.com/php/php-src/blob/534127d3b22b193ffb9511c4447584f0d2bd4e24/ext/date/php_date.stub.php#L508-L509 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - stubs/CoreImmutableClasses.phpstub | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index dc136691866..b6b9fe20581 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1799,7 +1799,6 @@ 'DateTimeImmutable::createFromInterface' => ['self', 'object' => 'DateTimeInterface'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], -'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'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 9e96e514e0e..cda731c378e 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1060,7 +1060,6 @@ 'DateTimeImmutable::add' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], - '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'], diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index c241cb4f17a..4e9cf0df3e9 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -46,7 +46,7 @@ class DateTimeImmutable implements DateTimeInterface /** * @psalm-mutation-free - * @return static + * @return static|false */ public function modify(string $modifier) {} From cb9939cbd37c9e7285d243716d666004af98023b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:39:16 +0200 Subject: [PATCH 14/22] Removed `DateTimeImmutable::add()` from the CallMap: fully covered by stub Ref: https://github.com/php/php-src/blob/534127d3b22b193ffb9511c4447584f0d2bd4e24/ext/date/php_date.stub.php#L511-L512 Ref: https://github.com/php/php-src/blob/534127d3b22b193ffb9511c4447584f0d2bd4e24/ext/date/php_date.c#L3129-L3144 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - 2 files changed, 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index b6b9fe20581..809204d57f2 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1795,7 +1795,6 @@ 'DateTime::sub' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::__set_state' => ['static', 'array'=>'array'], 'DateTimeImmutable::__wakeup' => ['void'], -'DateTimeImmutable::add' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::createFromInterface' => ['self', 'object' => 'DateTimeInterface'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index cda731c378e..801026e0d18 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1057,7 +1057,6 @@ 'DateTime::sub' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::__set_state' => ['static', 'array'=>'array'], 'DateTimeImmutable::__wakeup' => ['void'], - 'DateTimeImmutable::add' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], 'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'], From 4fe554d6d227e49fcdf41d2bbd8b64b3a058aa63 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:42:45 +0200 Subject: [PATCH 15/22] Removed `DateTimeImmutable::setTimezone()` from the CallMap: fully covered by stub Also simplified the return type from `static|false` to `static`, since the method throws at all times, on failure. On PHP 7.x, it could only fail if an invalid type was passed in, which is not really valid anyway, from a type perspective. Ref (PHP 8.2.x): https://github.com/php/php-src/blob/534127d3b22b193ffb9511c4447584f0d2bd4e24/ext/date/php_date.c#L3291-L3307 Ref (PHP 8.2.x): https://github.com/php/php-src/blob/534127d3b22b193ffb9511c4447584f0d2bd4e24/ext/date/php_date.stub.php#L517-L518 Ref (PHP 7.0.33): https://github.com/php/php-src/blob/bf574c2b67a1f786e36cf679f41b758b973a82c4/ext/date/php_date.c#L3363-L3379 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - stubs/CoreImmutableClasses.phpstub | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 809204d57f2..0da0f76ecb9 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1802,7 +1802,6 @@ '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'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], 'DateTimeInterface::getOffset' => ['int'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 801026e0d18..8af7c4a8165 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1063,7 +1063,6 @@ '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'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], 'DateTimeInterface::getOffset' => ['int'], diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 4e9cf0df3e9..21efbef48e3 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -67,7 +67,7 @@ class DateTimeImmutable implements DateTimeInterface /** * @psalm-mutation-free - * @return static|false + * @return static */ public function setTimezone(DateTimeZone $timezone) {} From e61c593a2c3cadbd656ff6768e302ee94e287da2 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:47:04 +0200 Subject: [PATCH 16/22] Removed `DateTimeImmutable::setTime()` from the CallMap: fully covered by stub Also simplified the return type from `static|false` to `static`, since the method throws at all times, on failure. On PHP 7.x, it could only fail if an invalid type was passed in, which is not really valid anyway, from a type perspective. Ref (PHP 8.1.x): https://github.com/php/php-src/blob/32d55f74229e7913db0d59ef874a401744479b6a/ext/date/php_date.c#L3212-L3228 Ref (PHP 7.0.33): https://github.com/php/php-src/blob/bf574c2b67a1f786e36cf679f41b758b973a82c4/ext/date/php_date.c#L3447-L3463 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - stubs/CoreImmutableClasses.phpstub | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 0da0f76ecb9..86da052b3b6 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1800,7 +1800,6 @@ 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], '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'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 8af7c4a8165..176ffce3868 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1061,7 +1061,6 @@ 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], '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'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 21efbef48e3..aab2e880641 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -73,7 +73,7 @@ class DateTimeImmutable implements DateTimeInterface /** * @psalm-mutation-free - * @return static|false + * @return static */ public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) {} From 0a6c9d01776a99ab759062aa1b03c445550633d4 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:48:29 +0200 Subject: [PATCH 17/22] Removed `DateTimeImmutable::setDate()` from the CallMap: fully covered by stub Also simplified the return type from `static|false` to `static`, since the method throws at all times, on failure. On PHP 7.x, it could only fail if an invalid type was passed in, which is not really valid anyway, from a type perspective. Ref (PHP 8.1.x): https://github.com/php/php-src/blob/32d55f74229e7913db0d59ef874a401744479b6a/ext/date/php_date.c#L3258-L3274 Ref (PHP 7.0.33): https://github.com/php/php-src/blob/bf574c2b67a1f786e36cf679f41b758b973a82c4/ext/date/php_date.c#L3496-L3512 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - stubs/CoreImmutableClasses.phpstub | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 86da052b3b6..630770d728d 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1798,7 +1798,6 @@ 'DateTimeImmutable::createFromInterface' => ['self', 'object' => 'DateTimeInterface'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], -'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'], 'DateTimeImmutable::setISODate' => ['static|false', 'year'=>'int', 'week'=>'int', 'day='=>'int'], 'DateTimeImmutable::setTimestamp' => ['static|false', 'unixtimestamp'=>'int'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 176ffce3868..9fd8252b8a0 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1059,7 +1059,6 @@ 'DateTimeImmutable::__wakeup' => ['void'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], - 'DateTimeImmutable::setDate' => ['static|false', 'year'=>'int', 'month'=>'int', 'day'=>'int'], 'DateTimeImmutable::setISODate' => ['static|false', 'year'=>'int', 'week'=>'int', 'day='=>'int'], 'DateTimeImmutable::setTimestamp' => ['static|false', 'unixtimestamp'=>'int'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index aab2e880641..fcdae2705d3 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -79,7 +79,7 @@ class DateTimeImmutable implements DateTimeInterface /** * @psalm-mutation-free - * @return static|false + * @return static */ public function setDate(int $year, int $month, int $day) {} From 964f64a500b987c82bd5274d07d3b67e1d0b381b Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:49:45 +0200 Subject: [PATCH 18/22] Removed `DateTimeImmutable::setISODate()` from the CallMap: fully covered by stub Also simplified the return type from `static|false` to `static`, since the method throws at all times, on failure. On PHP 7.x, it could only fail if an invalid type was passed in, which is not really valid anyway, from a type perspective. Ref (PHP 8.1.x): https://github.com/php/php-src/blob/32d55f74229e7913db0d59ef874a401744479b6a/ext/date/php_date.c#L3308-L3324 Ref (PHP 7.0.33): https://github.com/php/php-src/blob/bf574c2b67a1f786e36cf679f41b758b973a82c4/ext/date/php_date.c#L3549-L3565 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - stubs/CoreImmutableClasses.phpstub | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 630770d728d..28171d694a4 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1798,7 +1798,6 @@ 'DateTimeImmutable::createFromInterface' => ['self', 'object' => 'DateTimeInterface'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], -'DateTimeImmutable::setISODate' => ['static|false', 'year'=>'int', 'week'=>'int', 'day='=>'int'], 'DateTimeImmutable::setTimestamp' => ['static|false', 'unixtimestamp'=>'int'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 9fd8252b8a0..c15978b7e50 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1059,7 +1059,6 @@ 'DateTimeImmutable::__wakeup' => ['void'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], - 'DateTimeImmutable::setISODate' => ['static|false', 'year'=>'int', 'week'=>'int', 'day='=>'int'], 'DateTimeImmutable::setTimestamp' => ['static|false', 'unixtimestamp'=>'int'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index fcdae2705d3..1829ad4e891 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -85,7 +85,7 @@ class DateTimeImmutable implements DateTimeInterface /** * @psalm-mutation-free - * @return static|false + * @return static */ public function setISODate(int $year, int $week, int $dayOfWeek = 1) {} From aaac9ccb90f32739dd36cdea7bcc16ca9e30e71a Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:50:51 +0200 Subject: [PATCH 19/22] Removed `DateTimeImmutable::setTimestamp()` from the CallMap: fully covered by stub Also simplified the return type from `static|false` to `static`, since the method throws at all times, on failure. On PHP 7.x, it could only fail if an invalid type was passed in, which is not really valid anyway, from a type perspective. Ref (PHP 8.1.x): https://github.com/php/php-src/blob/32d55f74229e7913db0d59ef874a401744479b6a/ext/date/php_date.c#L3353-L3369 Ref (PHP 7.0.33): https://github.com/php/php-src/blob/bf574c2b67a1f786e36cf679f41b758b973a82c4/ext/date/php_date.c#L3596-L3612 --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - stubs/CoreImmutableClasses.phpstub | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index 28171d694a4..caf3cfaf5f6 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1798,7 +1798,6 @@ 'DateTimeImmutable::createFromInterface' => ['self', 'object' => 'DateTimeInterface'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], -'DateTimeImmutable::setTimestamp' => ['static|false', 'unixtimestamp'=>'int'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], 'DateTimeInterface::getOffset' => ['int'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index c15978b7e50..4fa58db7dc2 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1059,7 +1059,6 @@ 'DateTimeImmutable::__wakeup' => ['void'], 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], - 'DateTimeImmutable::setTimestamp' => ['static|false', 'unixtimestamp'=>'int'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], 'DateTimeInterface::getOffset' => ['int'], diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 1829ad4e891..acf7fc0bf39 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -91,7 +91,7 @@ class DateTimeImmutable implements DateTimeInterface /** * @psalm-mutation-free - * @return static|false + * @return static */ public function setTimestamp(int $unixtimestamp) {} From a1ed84f1ed7fb8a030fea09aa2e6b4780903b3d7 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 12:52:32 +0200 Subject: [PATCH 20/22] Removed `DateTimeImmutable::createFromMutable()` from the CallMap: fully covered by stub --- dictionaries/CallMap.php | 1 - dictionaries/CallMap_historical.php | 1 - 2 files changed, 2 deletions(-) diff --git a/dictionaries/CallMap.php b/dictionaries/CallMap.php index caf3cfaf5f6..329decd3234 100644 --- a/dictionaries/CallMap.php +++ b/dictionaries/CallMap.php @@ -1796,7 +1796,6 @@ 'DateTimeImmutable::__set_state' => ['static', 'array'=>'array'], 'DateTimeImmutable::__wakeup' => ['void'], 'DateTimeImmutable::createFromInterface' => ['self', 'object' => 'DateTimeInterface'], -'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], diff --git a/dictionaries/CallMap_historical.php b/dictionaries/CallMap_historical.php index 4fa58db7dc2..6df5d473acf 100644 --- a/dictionaries/CallMap_historical.php +++ b/dictionaries/CallMap_historical.php @@ -1057,7 +1057,6 @@ 'DateTime::sub' => ['static', 'interval'=>'DateInterval'], 'DateTimeImmutable::__set_state' => ['static', 'array'=>'array'], 'DateTimeImmutable::__wakeup' => ['void'], - 'DateTimeImmutable::createFromMutable' => ['static', 'datetime'=>'DateTime'], 'DateTimeImmutable::getLastErrors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}'], 'DateTimeInterface::diff' => ['DateInterval', 'datetime2'=>'DateTimeInterface', 'absolute='=>'bool'], 'DateTimeInterface::format' => ['string', 'format'=>'string'], From 68ffae097e32d64397179da1b7353f7e4f0cdea7 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 13:21:28 +0200 Subject: [PATCH 21/22] Simplified `DateTimeImmutable::format()`: always returns a `string` Also: * a non-empty format string will always lead to `non-empty-string` * it seems that you can throw **everything** at `DateTimeInterface#format()`, even null bytes, and it will always produce a `string` --- stubs/CoreImmutableClasses.phpstub | 2 +- tests/MethodCallTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index acf7fc0bf39..3a537ba4575 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -15,7 +15,7 @@ class DateTimeImmutable implements DateTimeInterface * * @param string $format * - * @return (\PHP_MAJOR_VERSION is int<0, 7> ? string|false : string) + * @return ($format is non-empty-string ? non-empty-string : string) */ public function format($format) {} diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php index 76c033f56f5..62860e8f209 100644 --- a/tests/MethodCallTest.php +++ b/tests/MethodCallTest.php @@ -271,8 +271,8 @@ final class MyDate extends DateTimeImmutable {} $b = (new DateTimeImmutable())->modify("+3 hours");', 'assertions' => [ - '$yesterday' => 'MyDate', - '$b' => 'DateTimeImmutable', + '$yesterday' => 'MyDate|false', + '$b' => 'DateTimeImmutable|false', ], ], 'magicCall' => [ From 13828771c7c544aa61a3db1ef49b7aae9e0996fd Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 5 Aug 2022 13:28:53 +0200 Subject: [PATCH 22/22] Removed `DateTimeImmutable::createFromInterface()` from stubs While there is value in declaring `DateTimeImmutable::createFromInterface()` as mutation-free in a stub, this method was introduced in PHP 8.0, so we cannot really declare it in a stub. For now, we drop it, as the value of its stub declaration is much lower than the problems it introduces through its conditional existence. --- stubs/CoreImmutableClasses.phpstub | 6 ------ 1 file changed, 6 deletions(-) diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 3a537ba4575..23eb77bdb77 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -100,12 +100,6 @@ class DateTimeImmutable implements DateTimeInterface * @return static */ public static function createFromMutable(DateTime $object) {} - - /** - * @psalm-mutation-free - * @return self - */ - public static function createFromInterface(DateTimeInterface $object) {} } /**