The Maatify\SharedCommon module introduces the ClockInterface to manage time predictably and reliably across the application ecosystem.
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:
- 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 === expectedTimewithout complex mocking or fragile assertions. - Inconsistent Timezones: Different parts of the application might accidentally generate timestamps in different timezones depending on where and how the
\DateTimeobject was constructed. - Mutable State: If an application passes a
\DateTimeobject around, any service can call->modify()and unintentionally alter the timestamp for the rest of the request.
The ClockInterface completely abstracts the concept of "now":
interface ClockInterface
{
public function now(): DateTimeImmutable;
public function getTimezone(): DateTimeZone;
}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();
}
}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.
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.