Skip to content

Feature Frozen clock

github-actions[bot] edited this page Jul 14, 2026 · 1 revision

🧊 Замороженные часы — FrozenClock

← Возможности · Главная · Сравнение

Время стоит, пока его явно не сдвинут. Главный инструмент детерминированных тестов.

Принцип работы

Часы хранят один момент и отдают его из now() без изменений. Сдвиг — только явными методами контракта AdjustableClockInterface: setTo(), modify(), advance(), rewind().

use CloudCastle\Clock\FrozenClock;

$clock = FrozenClock::from('2026-01-01 12:00:00', 'UTC');

$clock->now();               // всегда 2026-01-01 12:00:00
$clock->advance(90.0);       // +1.5 минуты (секунды с дробью — до микросекунды)
$clock->advance(new DateInterval('P1D'));
$clock->modify('next monday');
$clock->rewind(0.000001);    // -1 мкс
$clock->setTo(new DateTimeImmutable('2030-01-01'));

Фабрики: from() (строка или объект + необязательная зона), fromNow(), fromUtcNow(). Мусорные строки моментов и модификаторов отклоняются InvalidClockValueException — включая строки с null-байтами, которые встроенный парсер PHP молча усёк бы.

Плюсы

  • Полный контроль времени в тестах: никаких sleep() и флаков.
  • Микросекундная точность сдвигов в обе стороны.
  • Жёсткая валидация ввода — инъекции строк в модификаторы не проходят.

Минусы

  • Мутабельные часы: не делите один экземпляр между независимыми потребителями, если они не должны видеть сдвиги друг друга.

✅ Рекомендуется

  • Фиксировать «сегодня» в тестах тарифов, расписаний, сроков действия.
  • Проверять граничные моменты: полночь, конец месяца, переход DST.

❌ Антипаттерны

  • FrozenClock в проде — время «застынет» со всеми вытекающими.
  • Сдвигать время строкой там, где хватает advance(seconds): строка медленнее и её нужно парсить.

Схема фичи

flowchart LR
    IN["from(строка/объект, зона)"] --> FC[FrozenClock]
    FC -->|"now() — всегда один момент"| M[DateTimeImmutable]
Loading

Жизненный цикл момента

stateDiagram-v2
    [*] --> Заморожен: from()/fromNow()/fromUtcNow()
    Заморожен --> Заморожен: now() — тот же момент
    Заморожен --> Сдвинут: advance()/rewind()
    Заморожен --> Сдвинут: modify("+1 day")
    Заморожен --> Сдвинут: setTo(момент)
    Сдвинут --> Заморожен: новый фиксированный момент
    Заморожен --> Отклонено: мусор/NUL в строке
    Отклонено --> [*]: InvalidClockValueException
Loading

Сравнение с аналогами

Возможность Clock symfony/clock lcobucci/clock beste/clock nesbot/carbon ergebnis/clock 🏆 Победитель
Замороженные часы ⚠️ Clock + symfony + lcobucci + beste + ergebnis
setTo() точная установка ⚠️ ⚠️ Clock
modify() строкой ⚠️ Clock + symfony/clock
advance()/rewind() интервалом и секундами ⚠️ ⚠️ Clock
Валидация модификаторов с null-байтами Clock

Clone this wiki locally