CloudCastle Clock

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano

CalendarDate macros — extending the API without inheritance

⚠️ Traduzione preliminare: fa fede la versione russa; il contenuto sottostante è per ora in inglese.

Purpose

Registering your own date methods: CalendarDate::macro('isPayday', fn (CalendarDate $date): bool => ...). The macro receives the date as its first argument; declared class methods always take precedence over macros. Parity with Carbon::macro().

What for and how to use

Domain predicates and formats (bank business day, reporting period) without a subclass: call $date->isPayday() or the strictly typed $date->callMacro('isPayday').

Example

CalendarDate::macro(
    'isPayday',
    static fn (CalendarDate $date): bool => $date->format('j') === '25',
);

$date = CalendarDate::parse('2026-09-25', 'UTC');
$date->isPayday();                 // true
$date->callMacro('isPayday');      // true, no magic for static analysis
CalendarDate::hasMacro('isPayday'); // true

Anti-patterns

The macro registry is process-global — register macros at bootstrap and clear them in tests via flushMacros(). An unknown name throws UnknownMacroException with the name escaped.

Lifecycle

flowchart LR
    A[macro(name, closure)] --> R[(MacroRegistry)]
    B[date->name(args)] --> C{declared method?}
    C -->|yes| D[Class method]
    C -->|no| E[__call → callMacro]
    E --> R
    R -->|found| F[closure(date, ...args)]
    R -->|missing| G[/UnknownMacroException/]

Feature comparison with competitors

Aspect🏆 CloudCastlesymfonycarbonlcobuccibesteergebnis
Registering methods without inheritance✅❌✅❌❌❌
Typed non-magic call (callMacro)✅❌❌❌❌❌
Declared methods take precedence✅❌✅❌❌❌
Name escaping in the exception✅❌❌❌❌❌
Registry flush for test isolation✅❌✅❌❌❌
Total🏆 503000

← Back to the feature list


🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano