Skip to content

Commit 6ecc91e

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

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

components/clock.rst

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

0 commit comments

Comments
 (0)