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

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • PSR-20 Compliance: The package fully implements the PSR-20 Clock Interface, making it a first-class citizen in modern PHP ecosystems (e.g., Laravel, Symfony, or any PSR-compliant framework). This aligns with Laravel’s growing adoption of PSR standards (e.g., PSR-15 middleware, PSR-17 factories).
  • Dependency Injection (DI) Friendly: The 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).
  • Time Zone Isolation: Forces explicit timezone handling, reducing bugs from implicit system defaults—a critical improvement over Laravel’s native now() or Carbon (which can inherit system time zones).
  • Immutable Design: SystemClock is marked as @immutable and readonly, preventing accidental modifications—a best practice for thread safety and predictability in Laravel’s request lifecycle.

Integration Feasibility

  • Laravel Compatibility:
    • Carbon Integration: Since Laravel relies on 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();
          }
      };
      
    • Service Provider Binding: Can be bound in Laravel’s container as a singleton or context-bound instance (e.g., per-request FrozenClock for tests).
    • Facade/Helper Methods: Can extend Laravel’s 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')));
          });
      }
      
  • Legacy Code Impact: Minimal if used only in new code. Existing now() calls can be gradually migrated via a wrapper trait or facade.

Technical Risk

  • PHP Version Lock-In: Requires PHP 8.4+ (as of v3.6.0). If the Laravel project uses PHP 8.2/8.3, this introduces a dependency upgrade risk. Mitigation:
    • Pin to v3.5.0 (supports PHP 8.5) or v3.4.0 (supports PHP 8.3).
    • Use Laravel’s PHP version policy to align upgrades.
  • Carbon vs. DateTimeInterface: The package returns DateTimeInterface, but Laravel’s ecosystem (e.g., Eloquent, Carbon) expects Carbon or DateTime. Solution:
    • Create a custom adapter to convert between DateTime and Carbon.
    • Use CarbonImmutable as the underlying implementation.
  • Performance Overhead: Minimal, but SystemClock creates a new DateTime on every now() call. Optimization:
    • Cache the DateTime object if high-frequency calls are detected (e.g., in loops).
    • Use FrozenClock for read-only operations (e.g., batch processing).

Key Questions

  1. PHP Version Strategy:
    • Is the project locked into PHP 8.2/8.3, or can it adopt 8.4+?
    • If not, will the team pin to an older version (e.g., v3.4.0)?
  2. Testing Strategy:
    • How will FrozenClock integrate with Laravel’s Pest/PHPUnit test suites?
    • Will tests mock the Clock interface or use FrozenClock directly?
  3. Carbon vs. DateTime:
    • Should the package wrap Carbon for Laravel-specific use cases?
    • Will this require a custom implementation or a composer plugin?
  4. Time Zone Handling:
    • How will the app configure time zones (e.g., per-request, per-locale)?
    • Will SystemClock use Laravel’s config('app.timezone') by default?
  5. Migration Path:
    • Will the team rewrite now() calls incrementally or batch-migrate?
    • Are there legacy services (e.g., queues, jobs) that rely on now()?

Integration Approach

Stack Fit

  • Laravel Ecosystem:
    • Service Container: Bind Clock as a singleton or context-bound instance.
    • Testing: Replace Carbon::setTestNow() with FrozenClock for deterministic tests.
    • Queues/Jobs: Use Clock to freeze time during replay testing.
    • APIs: Standardize time handling in DTOs and serialization.
  • Third-Party Libraries:
    • Carbon: Use as the underlying DateTime implementation.
    • PSR-20 Adopters: Leverage interoperability with other PSR-compliant packages (e.g., spatie/laravel-activitylog).
  • Database:
    • Eloquent: Replace now() in model timestamps with Clock::now().
    • Migrations: Use FrozenClock to test time-sensitive logic.

Migration Path

  1. Phase 1: Dependency Injection (Low Risk)
    • Bind Clock in AppServiceProvider:
      $this->app->singleton(\Lcobucci\Clock\Clock::class, function () {
          return new SystemClock(new DateTimeZone(config('app.timezone')));
      });
      
    • Replace direct now() calls with dependency injection:
      // Before
      $expiresAt = now()->addDays(7);
      
      // After
      public function __construct(private Clock $clock) {}
      $expiresAt = $this->clock->now()->addDays(7);
      
  2. Phase 2: Testing Integration (Medium Risk)
    • Replace Carbon::setTestNow() with FrozenClock:
      $clock = new FrozenClock(new DateTimeImmutable('2023-01-01'));
      $this->app->instance(Clock::class, $clock);
      
    • Update test helpers to use FrozenClock.
  3. Phase 3: Carbon Adapter (High Risk, Optional)
    • Create a CarbonClock wrapper if Carbon is required:
      use Carbon\CarbonImmutable;
      
      class CarbonClock implements \Lcobucci\Clock\Clock {
          public function now(): \DateTimeInterface {
              return CarbonImmutable::now();
          }
      }
      
  4. Phase 4: Full Replacement (Long-Term)
    • Replace all now()/Carbon::now() with $clock->now().
    • Deprecate legacy now() helpers via Laravel’s now() macro.

Compatibility

  • Laravel Versions:
    • Works with Laravel 10+ (PHP 8.1+) and Laravel 11 (PHP 8.2+).
    • For Laravel 9, pin to v3.4.0 (PHP 8.3 support).
  • Carbon:
    • No native support, but can be wrapped (see Phase 3).
  • Time Zone:
    • Explicitly configured via DateTimeZone (avoids system defaults).
    • Can integrate with Laravel’s config('app.timezone').

Sequencing

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

Operational Impact

Maintenance

  • Pros:
    • Reduced Time Zone Bugs: Explicit time zone handling eliminates "works on my machine" issues.
    • Testability: FrozenClock simplifies time-sensitive tests (e.g., expiration logic).
    • PSR Compliance: Future-proofs the codebase for PSR-20 adoption.
  • Cons:
    • Additional Abstraction: Requires **maintaining a `Clock
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.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai