Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Clock Laravel Package

symfony/clock

Symfony Clock decouples your code from the system clock. Inject ClockInterface to get DateTimeImmutable via now(), control timezones, and pause execution with sleep(). Ideal for testable, time-sensitive services without relying on global time.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:
    composer require symfony/clock
    
  2. Basic Usage: Inject ClockInterface into services requiring time awareness. Use NativeClock in production:
    use Symfony\Component\Clock\NativeClock;
    use Symfony\Component\Clock\ClockInterface;
    
    class OrderService {
        public function __construct(private ClockInterface $clock) {}
    
        public function checkExpiry(DateTimeInterface $orderDate): bool {
            return $this->clock->now() > $orderDate;
        }
    }
    
  3. First Use Case: Replace direct new DateTimeImmutable() or Carbon::now() calls in a critical path (e.g., subscription validation) with $clock->now(). Test by injecting a MockClock in unit tests.

Where to Look First

  • Symfony Docs: Official API reference and examples.
  • ClockInterface: Core contract with now() and sleep() methods.
  • NativeClock: Default implementation for production.
  • MockClock: Pre-built test double for PHPUnit (see release notes for PHPUnit 10/11 support).

Implementation Patterns

Core Workflows

  1. Dependency Injection:

    • Register NativeClock as a singleton in Laravel’s service container (e.g., AppServiceProvider):
      $this->app->singleton(ClockInterface::class, fn() => new NativeClock());
      
    • Use constructor injection for services needing time:
      public function __construct(private ClockInterface $clock) {}
      
  2. Timezone Isolation:

    • Enforce UTC for internal services:
      $utcClock = $clock->withTimeZone('UTC');
      
    • Store the configured timezone in a config file (e.g., config/clock.php) for consistency.
  3. Testing Patterns:

    • Unit Tests: Replace NativeClock with MockClock to freeze or advance time:
      use Symfony\Component\Clock\MockClock;
      
      $clock = new MockClock();
      $clock->setTime(new DateTimeImmutable('2023-11-15T12:00:00Z'));
      $service = new OrderService($clock);
      
    • Integration Tests: Use MockClock to simulate delays or time jumps:
      $clock->setTime($clock->now()->modify('+1 hour')); // Fast-forward
      $clock->sleep(0.5); // Simulate a 0.5s delay
      
  4. Background Jobs:

    • Replace usleep() or sleep() with $clock->sleep() for deterministic delays in queues:
      $clock->sleep(2); // 2-second delay (testable!)
      
  5. Event Sourcing:

    • Replay events in a controlled time sequence:
      $clock = new MockClock();
      $clock->setTime(new DateTimeImmutable('2023-01-01'));
      // Process events...
      $clock->setTime($clock->now()->modify('+1 day'));
      

Integration Tips

  • Laravel-Specific:
    • Use ClockSensitiveTrait (from Symfony) or build a custom trait to auto-inject ClockInterface:
      use Symfony\Component\Clock\ClockSensitiveTrait;
      
      class MyService {
          use ClockSensitiveTrait;
          // $this->clock is automatically injected
      }
      
    • Integrate with Laravel’s now() helper by binding ClockInterface to the helper:
      $this->app->bind('now', fn() => $this->app->make(ClockInterface::class)->now());
      
  • Carbon Compatibility:
    • Convert DateTimeImmutable to Carbon if needed:
      use Carbon\Carbon;
      
      $carbonTime = Carbon::instance($clock->now());
      
  • Database Timestamps:
    • Use $clock->now() in Eloquent model events or accessors:
      protected static function boot() {
          static::creating(fn($model) => $model->created_at = app(ClockInterface::class)->now());
      }
      

Gotchas and Tips

Pitfalls

  1. Timezone Mismatches:

    • Issue: Forgetting to set a timezone can lead to inconsistent behavior across environments.
    • Fix: Always use withTimeZone('UTC') for internal clocks and document the expected timezone in service contracts.
    • Debug Tip: Add a middleware to log the timezone of $clock->now() in development:
      $request->clockTimezone = $clock->now()->getTimezone()->getName();
      
  2. MockClock vs. NativeClock Behavior:

    • Issue: MockClock::sleep() with negative values was historically inconsistent (fixed in v7.3.0). Ensure tests use recent versions.
    • Fix: Test edge cases like $clock->sleep(-1) in your test suite.
  3. Immutable Time:

    • Issue: $clock->now() returns a DateTimeImmutable, which can cause confusion if modified directly (e.g., $now->modify()).
    • Fix: Store results in local variables or use DateTime if mutability is needed:
      $now = $clock->now(); // Immutable
      $mutableNow = clone $now; // Safe to modify
      
  4. Performance Overhead:

    • Issue: $clock->sleep() may introduce slight jitter in high-frequency loops.
    • Fix: For microsecond precision, use platform-specific APIs (e.g., usleep()) and reserve ClockInterface for business logic.
  5. Circular Dependencies:

    • Issue: Over-eager use of ClockInterface can create tight coupling between services.
    • Fix: Limit injection to services with explicit time dependencies (e.g., SubscriptionService, RateLimiter). Avoid injecting into repositories or DTOs.

Debugging Tips

  • Log Clock Time: Add a debug bar item or log entry to visualize time progression:
    Log::debug('Clock time:', ['time' => $clock->now()->format('Y-m-d H:i:sP')]);
    
  • Time Warping: Use MockClock to simulate edge cases (e.g., "What if this timeout fired 10 minutes late?"):
    $clock->setTime($clock->now()->modify('+10 minutes'));
    
  • PHPUnit Attributes: Leverage Symfony’s PHPUnit support for cleaner test setup:
    use Symfony\Component\Clock\ClockTestTrait;
    
    class MyTest extends TestCase {
        use ClockTestTrait;
    
        public function testTimeSensitiveLogic() {
            $this->setClockTime('2023-11-15T12:00:00Z');
            // Test logic...
        }
    }
    

Extension Points

  1. Custom Clock Implementations:

    • Build a RedisClock for distributed time synchronization:
      class RedisClock implements ClockInterface {
          public function now(): DateTimeImmutable {
              return DateTimeImmutable::createFromFormat('U.u', Redis::get('clock_time'));
          }
          public function sleep(float $seconds): void {
              usleep($seconds * 1_000_000);
          }
      }
      
    • Use MockClock as a base for domain-specific clocks (e.g., GameClock for turn-based games).
  2. Time Zone Providers:

    • Dynamically resolve timezones from user preferences or geoip:
      $clock = $nativeClock->withTimeZone($user->timezone ?? 'UTC');
      
  3. Clock Middleware:

    • Wrap HTTP requests with a clock-aware middleware to log request durations:
      $start = $clock->now();
      // Process request...
      $duration = $clock->now()->getTimestamp() - $start->getTimestamp();
      
  4. Clock Events:

    • Dispatch events when time thresholds are crossed (e.g., "1 hour until maintenance"):
      if ($clock->now()->diff($maintenanceTime)->s <= 3600) {
          event(new MaintenanceWarning());
      }
      

Configuration Quirks

  • Laravel Cache: If using NativeClock with Laravel’s cache, ensure the cache driver doesn’t interfere with time-sensitive operations (e.g., rate limiting).
  • Time Skew: In distributed systems, synchronize clocks using NTP or a centralized time service (e.g., RedisClock above).
  • Legacy Code: Gradually replace time(), date(), or Carbon::now() by wrapping them in a LegacyClock adapter:
    class LegacyClock implements ClockInterface {
        public function now(): DateTimeImmutable
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codraw/entity-migrator
codraw/doctrine-extra
codraw/aws-tool-kit
codraw/validator
codraw/workflow
codraw/open-api
codraw/cron-job
codraw/process
codraw/log
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony