diff --git a/stubs/CoreImmutableClasses.phpstub b/stubs/CoreImmutableClasses.phpstub index 60b807abbfe..a2695e0dd90 100644 --- a/stubs/CoreImmutableClasses.phpstub +++ b/stubs/CoreImmutableClasses.phpstub @@ -19,22 +19,19 @@ class DateTimeZone /** * @psalm-immutable * - * @template-covariant From of string|DateTimeInterface - * @implements IteratorAggregate + * @template-covariant Start of string|DateTimeInterface + * @implements Traversable */ -class DatePeriod implements IteratorAggregate +class DatePeriod implements Traversable { const EXCLUDE_START_DATE = 1; /** - * @param From $from - * @param (From is string ? 0|self::EXCLUDE_START_DATE : DateInterval) $interval_or_options - * @param (From is string ? never : DateTimeInterface|positive-int) $end_or_recurrences - * @param (From is string ? never : 0|self::EXCLUDE_START_DATE) $options + * @param Start $start + * @param (Start is string ? 0|self::EXCLUDE_START_DATE : DateInterval) $interval + * @param (Start is string ? never : DateTimeInterface|positive-int) $end + * @param (Start is string ? never : 0|self::EXCLUDE_START_DATE) $options */ - public function __construct($from, $interval_or_options = 0, $end_or_recurrences = 1, $options = 0) {} - - /** @psalm-return (From is string ? (Traversable&Iterator) : (Traversable&Iterator)) */ - public function getIterator(): Iterator {} + public function __construct($start, $interval = 0, $end = 1, $options = 0) {} } /** diff --git a/stubs/Php80.phpstub b/stubs/Php80.phpstub index 877b156f3a0..97b0621178b 100644 --- a/stubs/Php80.phpstub +++ b/stubs/Php80.phpstub @@ -82,3 +82,24 @@ class ReflectionUnionType extends ReflectionType { } class UnhandledMatchError extends Error {} + +/** + * @psalm-immutable + * + * @template-covariant Start of string|DateTimeInterface + * @implements IteratorAggregate + */ +class DatePeriod implements IteratorAggregate +{ + const EXCLUDE_START_DATE = 1; + /** + * @param Start $start + * @param (Start is string ? 0|self::EXCLUDE_START_DATE : DateInterval) $interval + * @param (Start is string ? never : DateTimeInterface|positive-int) $end + * @param (Start is string ? never : 0|self::EXCLUDE_START_DATE) $options + */ + public function __construct($start, $interval = 0, $end = 1, $options = 0) {} + + /** @psalm-return (Start is string ? (Traversable&Iterator) : (Traversable&Iterator)) */ + public function getIterator(): Iterator {} +} \ No newline at end of file diff --git a/tests/CoreStubsTest.php b/tests/CoreStubsTest.php index 38a38672c40..d6f0a85a916 100644 --- a/tests/CoreStubsTest.php +++ b/tests/CoreStubsTest.php @@ -33,7 +33,26 @@ public function providerValidCodeParse(): iterable 'error_levels' => [], 'php_version' => '8.0', ]; - yield 'Iterating over \DatePeriod (#5954)' => [ + yield 'Iterating over \DatePeriod (#5954) PHP7 Traversable' => [ + 'format("Y-m-d"); + }', + 'assertions' => [ + '$period' => 'DatePeriod', + '$dt' => 'DateTimeInterface|null' + ], + 'error_levels' => [], + 'php_version' => '7.3', + ]; + yield 'Iterating over \DatePeriod (#5954) PHP8 IteratorAggregate' => [ ' 'DatePeriod', '$dt' => 'DateTimeImmutable|null' ], + 'error_levels' => [], + 'php_version' => '8.0', ]; yield 'Iterating over \DatePeriod (#5954), ISO string' => [ ' 'DatePeriod', '$dt' => 'DateTime|null' ], + 'error_levels' => [], + 'php_version' => '8.0', + ]; + yield 'DatePeriod implements only Traversable on PHP 7' => [ + ' [], + 'error_levels' => [], + 'php_version' => '7.3', + ]; + yield 'DatePeriod implements IteratorAggregate on PHP 8' => [ + ' [], + 'error_levels' => ['RedundantCondition'], + 'php_version' => '8.0', ]; } }