lcobucci/clock
PSR-20 clock implementation for PHP. Provides a simple, testable way to access the current time with interchangeable clocks like system and frozen clocks, making time-dependent code predictable and easy to unit test.
Install the package via Composer:
composer require lcobucci/clock
Define a domain service that uses time — e.g., an OrderService that sets a placedAt timestamp:
use Lcobucci\Clock\Clock;
use Lcobucci\Clock\ClockInterface;
class OrderService
{
private ClockInterface $clock;
public function __construct(ClockInterface $clock)
{
$this->clock = $clock;
}
public function placeOrder(): void
{
$now = $this->clock->now();
// persist with $now
}
}
Bind the clock in Laravel’s service container — typically in AppServiceProvider:
$this->app->bind(ClockInterface::class, SystemClock::class);
Use in tests by swapping to a fixed/frozen clock:
use Lcobucci\Clock\FrozenClock;
$clock = new FrozenClock(new DateTimeImmutable('2024-01-01 12:00:00'));
$this->app->instance(ClockInterface::class, $clock);
ClockInterface into services, repositories, or Value Objects that rely on current time — never call new DateTimeImmutable() or time() directly inside business logic.FrozenClock to lock time at a known moment for precise assertions.FixedClock if you want time to advance (e.g., new FixedClock(new DateTimeImmutable(), '+1 second')).DatabaseTransaction, RefreshDatabase, or DatabaseMigrations for clean test isolation.ClockInterface to SystemClock in production service provider.app()->instance() or custom test setup in feature/integration tests.// In tests: hijack request handling to freeze time
public function withFrozenClock(DateTimeImmutable $time, callable $callback): mixed
{
$this->app->instance(ClockInterface::class, new FrozenClock($time));
return $callback();
}
SystemClock is stateless and thread-safe — but not mockable with Mockery::mock() directly. Use createMock(ClockInterface::class) instead, or prefer Faker + instance() injection in tests.time(), date(), new DateTimeImmutable(), and replace them all.config('app.timezone') isn’t automatically applied by SystemClock. Ensure all your DateTimeImmutable instances use UTC (best practice) or explicitly set timezone in your clock usage if needed (though lcobucci/clock defaults to UTC for consistency).ClockInterface yourself for custom behaviors (e.g., clock that advances time in sync with application events).ClockInterface calls add negligible overhead — prefer correctness and testability over micro-optimizing time lookups.How can I help you explore Laravel packages today?