Skip to content

Feature Clock determinism

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

⏰ Детерминизм с PSR-20

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

Киллер-фича пакета: все временные генераторы (UUID v1/v6/v7, ULID, Snowflake, Sonyflake) принимают PSR-20 часы, а все случайные — RandomSourceInterface. Подменили обе зависимости — генерация воспроизводима байт в байт. Ни один из пяти аналогов так не умеет.

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

use CloudCastle\Clock\MockClock;                  // cloudcastle/clock (suggest)
use CloudCastle\Uuid\{UuidFactory, UlidFactory, SnowflakeFactory};

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

$uuid = new UuidFactory(clock: $clock);
$ulid = new UlidFactory(clock: $clock);
$snow = new SnowflakeFactory(1, 2, clock: $clock);

$uuid->uuid7()->timestampMs();      // ровно 1767268800000
$ulid->generate()->timestampMs();   // ровно 1767268800000
$snow->generate()->timestampMs();   // ровно 1767268800000

$clock->advance(1.5);               // виртуальные +1.5 секунды
$uuid->uuid7()->timestampMs();      // ровно 1767268801500

Полная воспроизводимость — вместе с детерминированной случайностью:

use CloudCastle\Uuid\Contract\RandomSourceInterface;

final class SeededRandom implements RandomSourceInterface { /* ваша последовательность */ }

$factory = new UuidFactory(clock: $clock, random: new SeededRandom(…));
$factory->uuid7()->toString();      // одна и та же строка при каждом прогоне теста

Часы не передали? Работает быстрый системный путь (microtime()) — абстракция бесплатна для продакшна.

Плюсы

  • Снапшот-тесты идентификаторов: сравнение строк вместо регулярных выражений.
  • Замороженное время не ломает уникальность: монотонные счётчики продолжают работу.
  • Никаких глобальных моков time() и патчей функций.

Минусы

  • Часы задаются на фабрику, не на вызов — для «переносного» момента есть параметр $at.

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

  • Тесты репозиториев/фикстур с предсказуемыми ключами.
  • Бэкфил данных прошлым временем: MockClock + advance по расписанию.

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

  • sleep() в тестах в ожидании новой миллисекунды — продвиньте MockClock.

Схема фичи

flowchart TB
    PSR["Psr\Clock\ClockInterface"] --> UF[UuidFactory] & LF[UlidFactory] & SF[SnowflakeFactory] & YF[SonyflakeFactory]
    RS["RandomSourceInterface"] --> UF & LF & NF[NanoIdFactory]
    UF & LF & SF & YF -->|"clock = null"| FAST["microtime() — быстрый путь"]
Loading

Жизненный цикл в тесте

sequenceDiagram
    participant T as Тест
    participant M as MockClock
    participant F as Фабрики

    T->>M: from('2026-01-01', 'UTC')
    T->>F: new …Factory(clock: M)
    T->>F: generate()/uuid7()
    F->>M: now() — виртуальный момент
    F-->>T: идентификатор с точной меткой
    T->>M: advance(3600)
    T->>F: generate() — ровно +1 час
Loading

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

Возможность Uuid ramsey/uuid symfony/uid robinvdvleuten/ulid godruoyi/php-snowflake 🏆 Победитель
PSR-20 часы во всех генераторах ⚠️ ⚠️ Uuid
Подмена источника случайности ⚠️ Uuid + ramsey/uuid
Байт-в-байт воспроизводимость ⚠️ ⚠️ Uuid
Бесплатный системный путь без часов Паритет

Clone this wiki locally