diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5678657c..23c4d22c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,3 +3,18 @@ parameters: paths: - src - test + + ignoreErrors: + # This is only for PHP 8.2 compatibility + - + message: "#^PHPDoc tag @throws with type DateMalformedStringException\\|InvalidArgumentException is not subtype of Throwable$#" + count: 1 + path: src/FrozenClock.php + - + message: "#^Class DateMalformedStringException not found\\.$#" + count: 1 + path: test/FrozenClockTest.php + - + message: "#^Parameter \\#1 \\$exception of method PHPUnit\\\\Framework\\\\TestCase\\:\\:expectException\\(\\) expects class\\-string\\, string given\\.$#" + count: 1 + path: test/FrozenClockTest.php diff --git a/src/FrozenClock.php b/src/FrozenClock.php index 0aad7016..5b3c604c 100644 --- a/src/FrozenClock.php +++ b/src/FrozenClock.php @@ -3,8 +3,10 @@ namespace Lcobucci\Clock; +use DateMalformedStringException; use DateTimeImmutable; use DateTimeZone; +use InvalidArgumentException; final class FrozenClock implements Clock { @@ -22,6 +24,26 @@ public function setTo(DateTimeImmutable $now): void $this->now = $now; } + /** + * Adjusts the current time by a given modifier. + * + * @param string $modifier @see https://www.php.net/manual/en/datetime.formats.php + * + * @throws InvalidArgumentException When an invalid format string is passed (PHP < 8.3). + * @throws DateMalformedStringException When an invalid date/time string is passed (PHP 8.3+). + */ + public function adjustTime(string $modifier): void + { + $modifiedTime = @$this->now->modify($modifier); + + // PHP < 8.3 won't throw exceptions on invalid modifiers + if ($modifiedTime === false) { + throw new InvalidArgumentException('The given modifier is invalid'); + } + + $this->now = $this->now->modify($modifier); + } + public function now(): DateTimeImmutable { return $this->now; diff --git a/test/FrozenClockTest.php b/test/FrozenClockTest.php index fc4ab7ad..950f9741 100644 --- a/test/FrozenClockTest.php +++ b/test/FrozenClockTest.php @@ -3,8 +3,11 @@ namespace Lcobucci\Clock; +use DateMalformedStringException; use DateTimeImmutable; +use InvalidArgumentException; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; @@ -34,6 +37,45 @@ public function nowSetChangesTheObject(): void self::assertSame($newNow, $clock->now()); } + #[Test] + public function adjustTimeChangesTheObject(): void + { + $oldNow = new DateTimeImmutable(); + $newNow = $oldNow->modify('+1 day'); + + $clock = new FrozenClock($oldNow); + + $clock->adjustTime('+1 day'); + + self::assertNotEquals($oldNow, $clock->now()); + self::assertEquals($newNow, $clock->now()); + + $clock->adjustTime('-1 day'); + + self::assertEquals($oldNow, $clock->now()); + self::assertNotEquals($newNow, $clock->now()); + } + + #[Test] + #[RequiresPhp('< 8.3.0')] + public function adjustTimeThrowsForInvalidModifierInPhp82(): void + { + $clock = FrozenClock::fromUTC(); + + $this->expectException(InvalidArgumentException::class); + $clock->adjustTime('invalid'); + } + + #[Test] + #[RequiresPhp('>= 8.3.0')] + public function adjustTimeThrowsForInvalidModifier(): void + { + $clock = FrozenClock::fromUTC(); + + $this->expectException(DateMalformedStringException::class); + $clock->adjustTime('invalid'); + } + #[Test] public function fromUTCCreatesClockFrozenAtCurrentSystemTimeInUTC(): void {