Skip to content

FrozenClock

A clock that does not move except when told to: every read answers the wall-clock/monotonic values last set, however much real time elapses between two calls.

This is what a deterministic test of anything expiry-based (a session timeout, a cache TTL, a cookie’s Expires) wants — a test asserting “expired after N seconds” should not depend on how fast the test runner happens to execute.

Wall-clock time is kept as a float Unix timestamp rather than a DateTimeImmutable, and FrozenClock::now() is derived from it via the @seconds constructor form (which — since PHP 7.1 — accepts a fractional part), the same conversion DateTimeValidator already relies on elsewhere in this codebase. The result is always UTC, matching what a bare new DateTimeImmutable('@...') produces.

final class FrozenClock implements ClockInterface

ImplementsClockInterface
SourceSupport/Clock/FrozenClock.php

public function __construct(float $wallClockSeconds = 0.0, float $monotonicSeconds = 0.0): mixed

ParameterTypeDescription
$wallClockSecondsfloat
$monotonicSecondsfloat

Returns mixed

MethodDescription
advance(float $seconds): voidMove both the wall clock and the monotonic clock forward by the same amount, as real elapsed time would — the common case for “then N * seconds pass” in a test, as opposed to FrozenClock::set() simulating a clock step where only the wall clock moves.
fromDateTime(DateTimeInterface $now, float $monotonicSeconds = 0.0): FrozenClockBuild a FrozenClock frozen at $now, converted through its own timezone so a caller working in local time gets the wall-clock second it expects.
microtime(): floatWall-clock Unix timestamp with microsecond precision.
monotonic(): floatSeconds on a monotonic clock: immune to wall-clock steps, so only ever meaningful as the difference between two readings.
now(): DateTimeImmutableThe current wall-clock time.
set(float $wallClockSeconds): voidJump the wall clock to $wallClockSeconds, leaving the monotonic reading untouched — a wall-clock step (an NTP correction, a VM resync) is exactly the scenario ClockInterface::monotonic() exists to be immune to.
setMonotonic(float $monotonicSeconds): void
unixTimestamp(): intWall-clock Unix timestamp in whole seconds.

public function advance(float $seconds): void

Move both the wall clock and the monotonic clock forward by the same amount, as real elapsed time would — the common case for “then N * seconds pass” in a test, as opposed to FrozenClock::set() simulating a clock step where only the wall clock moves.

ParameterTypeDescription
$secondsfloat

public static function fromDateTime(DateTimeInterface $now, float $monotonicSeconds = 0.0): FrozenClock

Build a FrozenClock frozen at $now, converted through its own timezone so a caller working in local time gets the wall-clock second it expects.

ParameterTypeDescription
$nowDateTimeInterface
$monotonicSecondsfloat

Returns FrozenClock

public function microtime(): float

Wall-clock Unix timestamp with microsecond precision.

Replaces a direct microtime(true) call.

Returns float

public function monotonic(): float

Seconds on a monotonic clock: immune to wall-clock steps, so only ever meaningful as the difference between two readings.

Replaces a direct hrtime(true) call (or a microtime(true) one used for a duration rather than a point in time).

Returns float

public function now(): DateTimeImmutable

The current wall-clock time.

Returns DateTimeImmutable

public function set(float $wallClockSeconds): void

Jump the wall clock to $wallClockSeconds, leaving the monotonic reading untouched — a wall-clock step (an NTP correction, a VM resync) is exactly the scenario ClockInterface::monotonic() exists to be immune to.

ParameterTypeDescription
$wallClockSecondsfloat

public function setMonotonic(float $monotonicSeconds): void

ParameterTypeDescription
$monotonicSecondsfloat

public function unixTimestamp(): int

Wall-clock Unix timestamp in whole seconds.

Replaces a direct time() call.

Returns int