symfony/clock
Symfony Clock decouples your app from the system clock. Inject ClockInterface to get deterministic time via now() and control behavior like sleep() and time zones. Ideal for testable, time-sensitive code without relying on global time functions.
ClockInterface abstraction is a natural fit for Laravel’s service container and IoC principles.symfony/messenger or symfony/http-client. This reduces friction for teams already leveraging Symfony’s ecosystem.now() can still be used where needed, but ClockInterface provides a cleaner abstraction for testing and timezone control.Illuminate\Queue) or event listeners where time-based retries, delays, or deadlines are critical. Decouples logic from system clock inconsistencies (e.g., Lambda cold starts).time(), date(), or new DateTimeImmutable() need to depend on ClockInterface. Existing Laravel services (e.g., Carbon::now()) can coexist.ClockInterface, allowing easy binding of NativeClock (production) or MockClock (tests). Example:
$this->app->bind(ClockInterface::class, function () {
return new NativeClock(); // or MockClock in tests
});
ClockInterface implementation (e.g., CarbonClock) for gradual adoption:
class CarbonClock implements ClockInterface {
public function now(): DateTimeImmutable { return Carbon::now()->toImmutable(); }
public function sleep(float $seconds): void { usleep($seconds * 1000000); }
}
config/app.php) can be extended to default ClockInterface implementations, ensuring consistency across the app.replace() method in the service container to auto-wire ClockInterface without manual changes:
$this->app->when(SubscriptionService::class)
->needs(ClockInterface::class)
->give(NativeClock::class);
Carbon::now() or time() calls with ClockInterface. However, this is a one-time cost with long-term benefits (deterministic tests).NativeClock (uses time() under the hood). MockClock adds minimal abstraction cost.ClockInterface usage in new code? (e.g., static analysis, PHPDoc requirements).Carbon::now() usage in favor of ClockInterface?config/app.php)?time(), date(), or global now() calls? (e.g., wrap in adapters, deprecate).Facade pattern to hide ClockInterface behind a Time::now() facade for legacy code?sleep() in distributed environments? (e.g., MockClock::sleep(-1) to skip delays in tests).ClockInterface implementations for specific workers (e.g., AwsClock for Lambda)?clock->now()) separately from server time for debugging?Monolog) or APM tools (e.g., New Relic)?MockClock), Pest, and Laravel’s testing helpers. Supports PHPUnit 10/11 attributes for test isolation.time(), date(), Carbon::now(), or global now().SubscriptionService) to use ClockInterface.NativeClock in production and MockClock in tests.// Before
class SubscriptionService {
public function checkExpiry() {
$now = Carbon::now();
// ...
}
}
// After
class SubscriptionService {
public function __construct(private ClockInterface $clock) {}
public function checkExpiry() {
$now = $this->clock->now();
// ...
}
}
ClockInterface in AppServiceProvider:
public function register() {
$this->app->bind(ClockInterface::class, function () {
return new NativeClock();
});
}
$this->app->instance(ClockInterface::class, new MockClock('2024-01-01'));
Carbon::now() usage via static analysis (e.g., PHPStan rules).Carbon::now() in tests with MockClock.$clock = new MockClock('2024-01-01T12:00:00+00:00');
$service = new SubscriptionService($clock);
$service->checkExpiry(); // Deterministic!
ClockInterface returns DateTimeImmutable, which Carbon can work with. Use CarbonClock adapter for gradual adoption.created_at). Use ClockInterface for business logic, not database operations.ClockInterface into job classes. Example:
class SendReminderJob implements ShouldQueue {
public function __construct(private ClockInterface $clock) {}
public function handle() {
if ($this->clock->now() > $user->reminder_at) {
// ...
}
}
}
time() or date() can be wrapped in a ClockInterface adapter. Example for Guzzle:
$client = new Client(['clock' => new NativeClock()]);
MockClock before refactoring production code.How can I help you explore Laravel packages today?