lcobucci/clock
Small PHP clock abstraction to decouple your code from direct DateTimeImmutable instantiation. Depend on the Clock interface and use SystemClock for real time or FrozenClock for deterministic tests, with explicit timezone support.
Clock interface is explicitly designed for DI, enabling testability and time manipulation (e.g., freezing time for unit tests). This fits Laravel’s service container and testing paradigms (e.g., Mockery, PHPUnit).now() or Carbon (which can inherit system time zones).SystemClock is marked as @immutable and readonly, preventing accidental modifications—a best practice for thread safety and predictability in Laravel’s request lifecycle.Carbon, the package can wrap Carbon\CarbonImmutable (via now()) for seamless adoption. Example:
use Lcobucci\Clock\SystemClock;
use Carbon\CarbonImmutable;
$clock = new class implements \Lcobucci\Clock\Clock {
public function now(): \DateTimeInterface {
return CarbonImmutable::now();
}
};
FrozenClock for tests).now() helper to use the Clock interface:
// app/Providers/AppServiceProvider.php
use Lcobucci\Clock\Clock;
public function register()
{
$this->app->singleton(Clock::class, function () {
return new SystemClock(new DateTimeZone(config('app.timezone')));
});
}
now() calls can be gradually migrated via a wrapper trait or facade.DateTimeInterface: The package returns DateTimeInterface, but Laravel’s ecosystem (e.g., Eloquent, Carbon) expects Carbon or DateTime. Solution:
DateTime and Carbon.CarbonImmutable as the underlying implementation.SystemClock creates a new DateTime on every now() call. Optimization:
DateTime object if high-frequency calls are detected (e.g., in loops).FrozenClock for read-only operations (e.g., batch processing).FrozenClock integrate with Laravel’s Pest/PHPUnit test suites?Clock interface or use FrozenClock directly?DateTime:
Carbon for Laravel-specific use cases?SystemClock use Laravel’s config('app.timezone') by default?now() calls incrementally or batch-migrate?now()?Clock as a singleton or context-bound instance.Carbon::setTestNow() with FrozenClock for deterministic tests.Clock to freeze time during replay testing.DateTime implementation.spatie/laravel-activitylog).now() in model timestamps with Clock::now().FrozenClock to test time-sensitive logic.Clock in AppServiceProvider:
$this->app->singleton(\Lcobucci\Clock\Clock::class, function () {
return new SystemClock(new DateTimeZone(config('app.timezone')));
});
now() calls with dependency injection:
// Before
$expiresAt = now()->addDays(7);
// After
public function __construct(private Clock $clock) {}
$expiresAt = $this->clock->now()->addDays(7);
Carbon::setTestNow() with FrozenClock:
$clock = new FrozenClock(new DateTimeImmutable('2023-01-01'));
$this->app->instance(Clock::class, $clock);
FrozenClock.CarbonClock wrapper if Carbon is required:
use Carbon\CarbonImmutable;
class CarbonClock implements \Lcobucci\Clock\Clock {
public function now(): \DateTimeInterface {
return CarbonImmutable::now();
}
}
now()/Carbon::now() with $clock->now().now() helpers via Laravel’s now() macro.DateTimeZone (avoids system defaults).config('app.timezone').| Step | Task | Risk | Dependencies |
|---|---|---|---|
| 1 | Bind Clock in service container |
Low | Laravel 10+ |
| 2 | Update unit tests to use FrozenClock |
Medium | PHPUnit/Pest |
| 3 | Replace now() in new services |
Low | DI Container |
| 4 | Create CarbonClock adapter (if needed) |
High | Carbon |
| 5 | Deprecate legacy now() helpers |
Medium | Laravel Facades |
| 6 | Migrate queues/jobs to use Clock |
High | Laravel Queues |
FrozenClock simplifies time-sensitive tests (e.g., expiration logic).How can I help you explore Laravel packages today?