🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano
CalendarDate macros — extending the API without inheritance
⚠️ Traducción preliminar: la versión rusa es la autoritativa; el contenido siguiente está por ahora en inglés.
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 | 🏆 CloudCastle | symfony | carbon | lcobucci | beste | ergebnis |
|---|---|---|---|---|---|---|
| Registering methods without inheritance | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
Typed non-magic call (callMacro) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Declared methods take precedence | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Name escaping in the exception | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Registry flush for test isolation | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Total | 🏆 5 | 0 | 3 | 0 | 0 | 0 |
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano