From c3b470f60455b7712a5b86b9cc4bf6b4c33dce9a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 3 May 2026 10:38:02 +0200 Subject: [PATCH 1/4] Narrow DateInterval->days after DateTimeInterface->diff() --- stubs/date.stub | 22 +++++++++++++ tests/PHPStan/Analyser/nsrt/bug-14428.php | 31 +++++++++++++++++++ .../Rules/Functions/ReturnTypeRuleTest.php | 7 +++++ 3 files changed, 60 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-14428.php diff --git a/stubs/date.stub b/stubs/date.stub index e58c7f0682a..cddb6af9317 100644 --- a/stubs/date.stub +++ b/stubs/date.stub @@ -34,3 +34,25 @@ class DatePeriod implements \IteratorAggregate, \Traversable } } + +interface DateTimeInterface { + /** @return \DateInterval&object{days:int} */ + function diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval + {} +} + +interface DateTimeImmutable { + /** @return \DateInterval&object{days:int} */ + function diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval + {} +} + +interface DateTime { + /** @return \DateInterval&object{days:int} */ + function diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval + {} +} + +/** @return \DateInterval&object{days:int} */ +function date_diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval +{} diff --git a/tests/PHPStan/Analyser/nsrt/bug-14428.php b/tests/PHPStan/Analyser/nsrt/bug-14428.php new file mode 100644 index 00000000000..f59ce94f3df --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-14428.php @@ -0,0 +1,31 @@ +diff($b); + assertType('int', $interval->days); + doImpure($interval); + assertType('int|false', $interval->days); +} + +function getDateTimeImmutablediff(DateTimeImmutable $a, DateTimeImmutable $b): int { + return $a->diff($b)->days; +} + +function getDatetimediff(DateTime $a, DateTime $b): int { + return $a->diff($b)->days; +} + +function getDateDiffDays(DateTime $a, DateTime $b): int { + return date_diff($a, $b)->days; +} diff --git a/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php index 0c23f0e6b0a..272fc1a39e9 100644 --- a/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php @@ -431,4 +431,11 @@ public function testBug13000(): void $this->analyse([__DIR__ . '/data/bug-13000.php'], []); } + public function testBug14428(): void + { + $this->checkNullables = true; + $this->checkExplicitMixed = false; + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-14428.php'], []); + } + } From 1efd33bdb035f9d4c26d569b604305eaff39e368 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 3 May 2026 10:39:51 +0200 Subject: [PATCH 2/4] Update date.stub --- stubs/date.stub | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/date.stub b/stubs/date.stub index cddb6af9317..aeadf6ef55e 100644 --- a/stubs/date.stub +++ b/stubs/date.stub @@ -41,13 +41,13 @@ interface DateTimeInterface { {} } -interface DateTimeImmutable { +class DateTimeImmutable { /** @return \DateInterval&object{days:int} */ function diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval {} } -interface DateTime { +class DateTime { /** @return \DateInterval&object{days:int} */ function diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval {} From a2c906eb1d526c3f7088823dd1ccd33ad631961e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 3 May 2026 10:41:01 +0200 Subject: [PATCH 3/4] Update date.stub --- stubs/date.stub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/date.stub b/stubs/date.stub index aeadf6ef55e..0a3b69e0203 100644 --- a/stubs/date.stub +++ b/stubs/date.stub @@ -54,5 +54,5 @@ class DateTime { } /** @return \DateInterval&object{days:int} */ -function date_diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval +function date_diff(\DateTimeInterface $baseObject, \DateTimeInterface $targetObject, bool $absolute = false): \DateInterval {} From 8b0b833680871af673a7b50637e58d4477cce040 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 3 May 2026 10:41:59 +0200 Subject: [PATCH 4/4] simplify --- stubs/date.stub | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/stubs/date.stub b/stubs/date.stub index 0a3b69e0203..8191057c642 100644 --- a/stubs/date.stub +++ b/stubs/date.stub @@ -41,16 +41,10 @@ interface DateTimeInterface { {} } -class DateTimeImmutable { - /** @return \DateInterval&object{days:int} */ - function diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval - {} +class DateTimeImmutable implements DateTimeInterface { } -class DateTime { - /** @return \DateInterval&object{days:int} */ - function diff(\DateTimeInterface $targetObject, bool $absolute = false): \DateInterval - {} +class DateTime implements DateTimeInterface { } /** @return \DateInterval&object{days:int} */