Skip to content

Latest commit

 

History

History
56 lines (40 loc) · 2.55 KB

File metadata and controls

56 lines (40 loc) · 2.55 KB

Chapter 4: Clock Abstraction

The Maatify\SharedCommon module introduces the ClockInterface to manage time predictably and reliably across the application ecosystem.

The Problem with time() and new \DateTime()

PHP's built-in functions for current time are global and stateful:

  • time() returns a mutable UNIX timestamp.
  • date() formats that mutable timestamp.
  • new \DateTime() creates a mutable object tied to the system's timezone.

When applications rely on these global functions:

  1. Testing is Fragile: It becomes extremely difficult to test time-dependent logic (e.g., "does this token expire in exactly 15 minutes?") because the system time is constantly moving during the test execution. You cannot reliably assert expiresAt === expectedTime without complex mocking or fragile assertions.
  2. Inconsistent Timezones: Different parts of the application might accidentally generate timestamps in different timezones depending on where and how the \DateTime object was constructed.
  3. Mutable State: If an application passes a \DateTime object around, any service can call ->modify() and unintentionally alter the timestamp for the rest of the request.

The Solution: ClockInterface

The ClockInterface completely abstracts the concept of "now":

interface ClockInterface
{
    public function now(): DateTimeImmutable;
    public function getTimezone(): DateTimeZone;
}

1. Predictable Testing (Freezing Time)

By injecting ClockInterface into services, tests can easily swap the real SystemClock for a MockClock or FrozenClock. This allows you to set the system time to a specific instant, execute logic, and assert the exact output.

class FrozenClock implements ClockInterface
{
    public function __construct(private DateTimeImmutable $frozenTime) {}

    public function now(): DateTimeImmutable
    {
        return $this->frozenTime;
    }

    public function getTimezone(): DateTimeZone
    {
        return $this->frozenTime->getTimezone();
    }
}

2. Immutable By Default

The now() method strictly returns a DateTimeImmutable object. This guarantees that once a timestamp is generated (e.g., when a user logs in), no other service can accidentally modify it. Any modifications (like modify('+15 minutes')) return a new object, leaving the original intact.

3. Timezone Consistency

The ClockInterface provides getTimezone(), ensuring that any timestamps generated by the application use the same timezone configuration. This eliminates the risk of mixing UTC and local time across modules.