Skip to content

Commit 9a6daec

Browse files
author
Denis Brumann
committed
[Clock] Documentation for new Clock component
1 parent fd3f21f commit 9a6daec

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

components/clock.rst

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
.. index::
2+
single: Clock
3+
single: Components; Clock
4+
5+
.. _`clock-component`:
6+
7+
The Clock Component
8+
===================
9+
10+
.. versionadded:: 6.2
11+
12+
The Clock component was introduced in Symfony 6.2
13+
14+
The Clock component decouples applications from the system clock. This improves
15+
testability of time-sensitive logic.
16+
17+
The component provides a ``ClockInterface`` with the following implementations
18+
for different use cases:
19+
20+
* ``NativeClock`` provides a way to interact with the system clock;
21+
* ``MockClock`` is commonly used in tests as a replacement for the
22+
``NativeClock`` to be able to freeze and change the current time using either
23+
``sleep()`` or ``modify()``;
24+
* ``MonotonicClock`` relies on ``hrtime()`` and provides a high resolution,
25+
monotonic clock, when you need a precise stopwatch.
26+
27+
Installation
28+
------------
29+
30+
.. code-block:: terminal
31+
32+
$ composer require symfony/clock
33+
34+
.. include:: /components/require_autoload.rst.inc
35+
36+
Usage
37+
-----
38+
39+
A clock service replaces creating a new ``DateTime`` or
40+
``DateTimeImmutable`` object for the current time. Instead, you inject the
41+
``ClockInterface`` and call ``now()``. By default, your application will likely
42+
use a ``NativeClock``, which always returns the current system time. In tests it is replaced with a ``MockClock``. The
43+
The ``MockClock`` is instantiated with a time and does not move forward on its own. The time is
44+
fixed until ``sleep()`` or ``modify()`` are called. This gives you full control over what your code
45+
assumes is the current time.
46+
47+
The following example introduces a service utilizing the Clock component to
48+
determine the current time::
49+
50+
use Symfony\Component\Clock\ClockInterface;
51+
52+
class ExpirationChecker
53+
{
54+
public function __construct(
55+
private readonly ClockInterface $clock
56+
) {}
57+
58+
public function isExpired(DateTimeInterface $validUntil): bool
59+
{
60+
return $this->clock->now() > $validUntil;
61+
}
62+
}
63+
64+
Testing
65+
-------
66+
67+
When writing a test for this service, you can check both cases where something
68+
is expired or not, by modifying the clock's time::
69+
70+
use PHPUnit\Framework\TestCase;
71+
use Symfony\Component\Clock\MockClock;
72+
73+
class ExpirationCheckerTest extends TestCase
74+
{
75+
public function testIsExpired(): void
76+
{
77+
$clock = new MockClock('2022-11-16 15:20:00');
78+
$expirationChecker = new ExpirationChecker($clock);
79+
$validUntil = new DateTimeImmutable('2022-11-16 15:25:00');
80+
81+
// $validUntil is in the future, so it is not expired
82+
static::assertFalse($expirationChecker->isExpired($validUntil));
83+
84+
// Clock sleeps for 10 minutes, so now is '2022-11-16 15:30:00'
85+
$clock->sleep(600); // Instantly changes time as if we waited for 10 minutes (600secs)
86+
87+
// $validUntil is in the past after advancing the clock, so it is expired
88+
static::assertTrue($expirationChecker->isExpired($validUntil));
89+
90+
$clock->modify('2022-11-16 15:00:00');
91+
92+
// $validUntil is in the future again, so it is no longer expired
93+
static::assertFalse($expirationChecker->isExpired($validUntil));
94+
}
95+
}

0 commit comments

Comments
 (0)