The Clock Component
Warning: You are browsing the documentation for Symfony 6.2, which is no longer maintained.
Read the updated version of this page for Symfony 8.0 (the current stable version).
The Clock Component
6.2
The Clock component was introduced in Symfony 6.2
The Clock component decouples applications from the system clock. This allows you to fix time to improve testability of time-sensitive logic.
The component provides a ClockInterface with the following implementations
for different use cases:
- NativeClock
-
Provides a way to interact with the system clock, this is the same as doing
new \DateTimeImmutable(). - MockClock
-
Commonly used in tests as a replacement for the
NativeClockto be able to freeze and change the current time using eithersleep()ormodify(). - MonotonicClock
-
Relies on
hrtime()and provides a high resolution, monotonic clock, when you need a precise stopwatch.
Installation
1
$ composer require symfony/clock
Note
If you install this component outside of a Symfony application, you must
require the vendor/autoload.php file in your code to enable the class
autoloading mechanism provided by Composer. Read
this article for more details.
NativeClock
A clock service replaces creating a new DateTime or
DateTimeImmutable object for the current time. Instead, you inject the
ClockInterface and call now(). By default, your application will likely
use a NativeClock, which always returns the current system time. In tests it is replaced with a MockClock.
The following example introduces a service utilizing the Clock component to determine the current time:
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\Clock\ClockInterface;
class ExpirationChecker
{
public function __construct(
private ClockInterface $clock
) {}
public function isExpired(DateTimeInterface $validUntil): bool
{
return $this->clock->now() > $validUntil;
}
}
MockClock
The MockClock is instantiated with a time and does not move forward on its own. The time is
fixed until sleep() or modify() are called. This gives you full control over what your code
assumes is the current time.
When writing a test for this service, you can check both cases where something is expired or not, by modifying the clock's time:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
use PHPUnit\Framework\TestCase;
use Symfony\Component\Clock\MockClock;
class ExpirationCheckerTest extends TestCase
{
public function testIsExpired(): void
{
$clock = new MockClock('2022-11-16 15:20:00');
$expirationChecker = new ExpirationChecker($clock);
$validUntil = new DateTimeImmutable('2022-11-16 15:25:00');
// $validUntil is in the future, so it is not expired
static::assertFalse($expirationChecker->isExpired($validUntil));
// Clock sleeps for 10 minutes, so now is '2022-11-16 15:30:00'
$clock->sleep(600); // Instantly changes time as if we waited for 10 minutes (600 seconds)
// modify the clock, accepts all formats supported by DateTimeImmutable::modify()
static::assertTrue($expirationChecker->isExpired($validUntil));
$clock->modify('2022-11-16 15:00:00');
// $validUntil is in the future again, so it is no longer expired
static::assertFalse($expirationChecker->isExpired($validUntil));
}
}
Monotonic Clock
The MonotonicClock allows you to implement a precise stopwatch; depending on
the system up to nanosecond precision. It can be used to measure the elapsed
time between two calls without being affected by inconsistencies sometimes introduced
by the system clock, e.g. by updating it. Instead, it consistently increases time,
making it especially useful for measuring performance.