-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Часы для PHP 8.1+ поверх PSR-20: пять реализаций, три декоратора и три инструмента интеграции — самый полный набор среди PSR-20-пакетов, с жёсткой валидацией ввода и производительностью в группе лидеров.
- 📥 Установка
- ⚡ Быстрый старт
- 🧩 Возможности — страница на каждую фичу
- ⚙️ Конфигурация
- 💡 Примеры
- 🏗️ Архитектура
- 📊 Сравнение
- 🧪 Тестирование
- ❓ FAQ
- 🤝 Contributing
composer require cloudcastle/clockuse CloudCastle\Clock\SystemClock;
$clock = SystemClock::utc(); // продакшн: PSR-20 часы (кэшированный экземпляр)
$clock->now(); // DateTimeImmutable
$clock->timestamp(); // int — без создания объектов
$clock->microtime(); // float — секунды.микросекундыuse CloudCastle\Clock\MockClock;
$mock = MockClock::from('2026-01-01 00:00:00', 'UTC');
$mock->sleep(3600.0); // тест: мгновенно, +1 час виртуального времени
$mock->now()->format('H:i'); // 01:00use CloudCastle\Clock\Clock;
use CloudCastle\Clock\ClockHolder;
ClockHolder::set($mock); // легаси без DI: одна точка подмены времени
Clock::now(); // читается из любого места приложения
ClockHolder::reset();Полные сценарии — на страницах возможностей, каждая с примерами, схемой и жизненным циклом.
flowchart TB
PSR["Psr\\Clock\\ClockInterface (PSR-20)"] --> CI["ClockInterface<br/>+timestamp() +microtime()<br/>+timezone() +withTimezone()"]
CI --> SLP["SleepableClockInterface<br/>+sleep() +sleepUntil()"]
CI --> ADJ["AdjustableClockInterface<br/>+setTo() +modify()<br/>+advance() +rewind()"]
SLP -.-> SC[SystemClock]
ADJ -.-> FC[FrozenClock]
ADJ -.-> MC[MockClock]
SLP -.-> MC
CI -.-> MO[MonotonicClock]
CI -.-> CB[CallbackClock]
subgraph Декораторы
OC[OffsetClock]
SCL[ScaledClock]
TC[TruncatingClock]
end
CI -.-> OC & SCL & TC
OC & SCL & TC -->|оборачивают| CI
subgraph Интеграция
H[ClockHolder + фасад Clock]
T[ClockAwareTrait]
W[Stopwatch]
end
H --> CI
T --> H
sequenceDiagram
participant S as Сервис
participant T as ClockAwareTrait
participant H as ClockHolder
participant P as SystemClock (прод)
participant M as MockClock (тест)
rect rgb(235, 245, 235)
note over S,P: Продакшн
S->>T: now()
T->>H: get()
H->>P: now()
P-->>S: реальный момент
end
rect rgb(235, 240, 250)
note over S,M: Тест
S->>T: setClock(MockClock)
S->>T: now()
T->>M: now()
M-->>S: виртуальный момент
S->>M: sleep(3600) — мгновенно
end
Уровни статических анализаторов и порогов пакета против аналогов — значения подставляются из конфигов автоматически:
Сгенерировано из конфигов пакета (composer docs:sync). Уровни Clock — из phpstan.neon / psalm.xml / infection.json.dist / phpmd.xml / deptrac.yaml; по аналогам — заявленный инструментарий из их composer.json (Composer-дистрибутивы аналогов конфигов качества не содержат, поэтому их уровни — «н/д»).
| Контроль качества | Clock | symfony/clock | lcobucci/clock | beste/clock | nesbot/carbon | ergebnis/clock | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| PHPStan | ✅ level max + strict-rules | ❌ | ✅ (уровень: н/д) | ✅ (уровень: н/д) | ✅ (уровень: н/д) | ✅ (уровень: н/д) | Clock (уровень задокументирован) |
| Psalm | ✅ errorLevel 1 | ❌ | ❌ | ✅ (уровень: н/д) | ❌ | ❌ | Clock (уровень задокументирован) |
| Мутационное тестирование (Infection) | ✅ minMSI 98% (факт: 0 выживших) | ❌ | ✅ (уровень: н/д) | ❌ | ❌ | ✅ (уровень: н/д) | Clock (уровень задокументирован) |
| PHPMD | ✅ 10 правил | ❌ | ❌ | ❌ | ✅ (уровень: н/д) | ❌ | Clock (уровень задокументирован) |
| Deptrac (архитектурные слои) | ✅ 5 слоёв | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
| Rector (dry-run в CI) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ (уровень: н/д) | Clock (уровень задокументирован) |
| Код-стайл (PSR-12: phpcs + cs-fixer) | ✅ | ❌ | ❌ | ❌ | ✅ (уровень: н/д) | ✅ (уровень: н/д) | Clock (уровень задокументирован) |
| Покрытие строк (порог на файл) | ✅ 98% (факт: 100%) | н/д | н/д | н/д | н/д | н/д | Clock |
| Конфиги качества в дистрибутиве | ✅ поставляются | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
Таблицы генерируются автоматически (composer docs:sync): факты поддержки —
из tools/features-data.php, победитель вычисляется по этим фактам.
Сгенерировано из tools/features-data.php (факты о поддержке). Легенда: ✅ полностью ·
| Функция | Clock | symfony/clock | lcobucci/clock | beste/clock | nesbot/carbon | ergebnis/clock | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| Системные часы (PSR-20) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Паритет |
| Замороженные часы | ✅ | ✅ | ✅ | ✅ | ✅ | Clock + symfony/clock + lcobucci/clock + beste/clock + ergebnis/clock | |
| Тестовые часы с виртуальным sleep() | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | Clock + symfony/clock |
| Монотонные часы (hrtime) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | Clock + symfony/clock |
| Часы на callback | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
| Масштабируемое время (быстрее/медленнее) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
| Часы со смещением (прошлое/будущее) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
| Усечение моментов (сек/мин/час/сутки) | ✅ | ❌ | ❌ | ❌ | ❌ | Clock |
| Функция | Clock | symfony/clock | lcobucci/clock | beste/clock | nesbot/carbon | ergebnis/clock | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| Точная установка момента (setTo) | ✅ | ❌ | ❌ | ❌ | Clock | ||
| Сдвиг строкой-модификатором (modify) | ✅ | ✅ | ❌ | ❌ | ❌ | Clock + symfony/clock | |
| Сдвиг вперёд/назад интервалом или секундами | ✅ | ❌ | ❌ | ❌ | Clock | ||
| Виртуальный sleepUntil(момент) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
| Функция | Clock | symfony/clock | lcobucci/clock | beste/clock | nesbot/carbon | ergebnis/clock | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| Расширенный контракт: timestamp()/microtime() без объектов | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
| Смена таймзоны withTimezone() | ✅ | ✅ | ❌ | ❌ | ❌ | Clock + symfony/clock | |
| Глобальный слот часов приложения | ✅ | ✅ | ❌ | ❌ | ❌ | Clock + symfony/clock | |
| Трейт внедрения часов в сервисы | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | Clock + symfony/clock |
| Секундомер на монотонном таймере | ✅ | 🔌 | ❌ | ❌ | ❌ | ❌ | Clock |
| DatePoint: момент от часов приложения с исключениями | ✅ | ✅ | ❌ | ❌ | ❌ | Clock + symfony/clock | |
| Адаптер стороннего PSR-20 к расширенному контракту | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | Clock + beste/clock |
| freeze(): снимок любых часов в управляемые | ✅ | ❌ | ❌ | ❌ | ❌ | Clock | |
| Хелпер now(«+1 hour») с модификатором | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | Clock + symfony/clock + nesbot/carbon |
| Функция | Clock | symfony/clock | lcobucci/clock | beste/clock | nesbot/carbon | ergebnis/clock | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| Типизированные исключения валидации | ✅ | ❌ | ❌ | ❌ | Clock | ||
| Отклонение null-байтов во вводе | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
| Экранирование управляющих символов в ошибках | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | Clock |
| Реальный sleep() переживает прерывание сигналом | ✅ | ❌ | — | — | — | — | Clock |
Изолированные замеры (composer compare): отдельный процесс на каждую пару
«библиотека × сценарий», одинаковые циклы, медиана из 5 прогонов, без Xdebug.
Отрыв меньше 2 % — «Паритет лидеров».
Сгенерировано из composer compare (PHP 8.1.34). Правки в блоке будут перезаписаны.
| Сценарий | Clock | symfony/clock | lcobucci/clock | beste/clock | nesbot/carbon | ergebnis/clock | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| now() в системной таймзоне | 648 611 ops/s | 592 125 ops/s | 650 011 ops/s | 99 591 ops/s | 81 644 ops/s | 649 211 ops/s | Паритет лидеров |
| now() в UTC | 643 127 ops/s | 586 970 ops/s | 642 473 ops/s | 640 540 ops/s | 173 049 ops/s | 639 452 ops/s | Паритет лидеров |
| чтение зафиксированного момента | 22 262 011 ops/s | 6 678 993 ops/s | 22 706 907 ops/s | 9 074 581 ops/s | 29 451 ops/s | 22 472 910 ops/s | Паритет лидеров |
| задача: unix-время текущего момента | 813 138 ops/s | 568 078 ops/s | 609 675 ops/s | 608 764 ops/s | 170 630 ops/s | 610 357 ops/s | Clock |
| задача: метка времени с микросекундами | 800 739 ops/s | 447 005 ops/s | 487 861 ops/s | 480 447 ops/s | 146 079 ops/s | 479 579 ops/s | Clock |
| задача: сдвиг виртуального времени на 1 с (тесты) | 941 497 ops/s | 275 647 ops/s | — | — | 71 448 ops/s | — | Clock |
| создание часов + первый now() | 97 241 ops/s | 92 239 ops/s | 96 489 ops/s | 98 468 ops/s | 77 991 ops/s | 97 054 ops/s | Паритет лидеров |
Память — добавленная пиковая память процесса за сценарий (классы библиотеки + объекты + операции), меньше — лучше:
| Сценарий | Clock | symfony/clock | lcobucci/clock | beste/clock | nesbot/carbon | ergebnis/clock | 🏆 Победитель |
|---|---|---|---|---|---|---|---|
| now() в системной таймзоне | 0.0 КБ | 0.0 КБ | 0.0 КБ | 0.0 КБ | 3 521.9 КБ | 0.0 КБ | Паритет лидеров |
| now() в UTC | 0.0 КБ | 0.0 КБ | 0.0 КБ | 0.0 КБ | 3 521.9 КБ | 0.0 КБ | Паритет лидеров |
| чтение зафиксированного момента | 0.0 КБ | 0.0 КБ | 0.0 КБ | 0.0 КБ | 3 521.9 КБ | 0.0 КБ | Паритет лидеров |
| задача: unix-время текущего момента | 0.0 КБ | 0.0 КБ | 0.0 КБ | 0.0 КБ | 3 521.9 КБ | 0.0 КБ | Паритет лидеров |
| задача: метка времени с микросекундами | 0.0 КБ | 0.0 КБ | 0.0 КБ | 0.0 КБ | 3 521.9 КБ | 0.0 КБ | Паритет лидеров |
| задача: сдвиг виртуального времени на 1 с (тесты) | 0.0 КБ | 0.0 КБ | — | — | 3 521.9 КБ | — | Паритет лидеров |
| создание часов + первый now() | 0.0 КБ | 0.0 КБ | 0.0 КБ | 0.0 КБ | 3 521.9 КБ | 0.0 КБ | Паритет лидеров |
Подробное заключение с плюсами, минусами и рекомендациями по выбору — на странице Сравнение.
CloudCastle Clock · · PHP 8.1+
GitHub · Packagist · Releases · Discussions · Issues
Email · Telegram · VK · Сетка · 💜 Поддержать проект
Исходники Wiki — каталог wiki/ в репозитории (синхронизация через GitHub Actions)