cloudcastle / clock
Часы для PHP 8.1+ поверх PSR-20: системные, замороженные, тестовые с виртуальным сном, монотонные и масштабируемые, декораторы смещения и усечения, DatePoint, адаптер сторонних PSR-20-часов, глобальный слот, трейт внедрения и секундомер.
Requires
- php: >=8.1
- psr/clock: ^1.0
Requires (Dev)
- ext-pcntl: *
- beste/clock: ^3.0
- deptrac/deptrac: ^3.0 || ^4.0
- ergebnis/clock: ^2.0
- ergebnis/composer-normalize: ^2.45
- friendsofphp/php-cs-fixer: ^3.75
- icanhazstring/composer-unused: ^0.9
- infection/infection: ^0.29 || ^0.33
- lcobucci/clock: ^3.0
- nesbot/carbon: ^2.72 || ^3.0
- php-parallel-lint/php-parallel-lint: ^1.4
- phpdocumentor/shim: ^3.7
- phpmd/phpmd: ^2.15
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^1.12 || ^2.1
- phpstan/phpstan-deprecation-rules: ^1.2 || ^2.0
- phpstan/phpstan-phpunit: ^1.4 || ^2.0
- phpstan/phpstan-strict-rules: ^1.6 || ^2.0
- phpunit/phpunit: ^10.5 || ^11.5
- psalm/plugin-phpunit: ^0.19 || ^0.20
- rector/rector: ^1.2 || ^2.0
- roave/security-advisories: dev-latest
- squizlabs/php_codesniffer: ^3.12 || ^4.0
- symfony/clock: ^6.4 || ^7.0
- symfony/polyfill-php83: ^1.28
- vimeo/psalm: ^6.0
- webmozart/assert: ^1.11
This package is auto-updated.
Last update: 2026-07-14 17:58:11 UTC
README
Часы для PHP 8.1+ поверх PSR-20: системные, замороженные, тестовые с виртуальным сном, монотонные, масштабируемые — плюс декораторы смещения и усечения, глобальный слот, трейт внедрения и секундомер. Самый полный набор среди PSR-20-реализаций.
Установка
composer require cloudcastle/clock
Требуется PHP 8.1+. Единственная runtime-зависимость — psr/clock.
Быстрый старт
<?php use CloudCastle\Clock\SystemClock; use CloudCastle\Clock\MockClock; use CloudCastle\Clock\ClockHolder; // Продакшн: системные часы как PSR-20 зависимость. $clock = SystemClock::utc(); $now = $clock->now(); // DateTimeImmutable $unix = $clock->timestamp(); // int — без создания объектов $micro = $clock->microtime(); // float — секунды.микросекунды // Тест: время под полным контролем, sleep() виртуальный. $mock = MockClock::from('2026-01-01 00:00:00', 'UTC'); $mock->sleep(3600.0); // мгновенно: +1 час виртуального времени $mock->now()->format('H:i'); // 01:00 // Легаси без DI: глобальный слот. ClockHolder::set($mock); ClockHolder::now(); // читается из любого места приложения
Больше сценариев — в wiki: на каждую возможность есть отдельная страница с примерами и сравнением с аналогами.
Возможности
Пять реализаций часов (системные,
замороженные,
тестовые,
монотонные,
callback),
три декоратора (смещение,
масштаб,
усечение)
инструменты интеграции (слот + фасад,
трейт,
секундомер), а также DatePoint, адаптер сторонних PSR-20-часов и freeze() у всех реализаций: функционально пакет покрывает возможности всех пяти сравниваемых аналогов вместе взятых.
Сводное сравнение с аналогами (генерируется автоматически из 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: каждая пара
«библиотека × сценарий» выполняется в отдельном PHP-процессе (память одной
библиотеки не влияет на замеры другой), одинаковые циклы для всех, медиана
из 5 прогонов, Xdebug выключен. Отрыв лидера меньше 2 % честно помечается
как «Паритет лидеров» — на одинаковых по сути операциях решает шум.
Память меряется с байтовой точностью в тех же изолированных процессах:
добавленная пиковая память за сценарий (классы библиотеки + объекты + операции).
Дополнительно потребление памяти самого пакета контролируется нагрузочными
тестами (tests/Load — бюджеты на 200 000+ операций) и собственными
бенчмарками (composer benchmark-report, колонка Mem).
Сгенерировано из 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 КБ | Паритет лидеров |
Качество
Пороги ниже проставляются автоматически из конфигов пакета при генерации
документации (composer docs:sync):
Сгенерировано из конфигов проекта — правьте infection.json.dist / phpmd.xml / tools/coverage-check.php / composer.json, не таблицу.
| Порог | Значение |
|---|---|
Mutation MSI (minMsi) |
≥ 98% |
Mutation Covered MSI (minCoveredMsi) |
≥ 98% |
| Mutation timeout | 120 с |
| Покрытие строк — проект | ≥ 98% |
| Покрытие строк — каждый файл | ≥ 98% |
| Класс: цикломатическая сложность | ≤ 50 |
| Класс: полей | ≤ 15 |
| Класс: методов | ≤ 25 |
| Класс: public-методов | ≤ 20 |
| PHP (runtime) | ≥ 8.1 |
| PHP (CI-матрица) | 8.1–8.5 |
Строгость контроля качества против аналогов (значения — из конфигов, автоматически):
Сгенерировано из конфигов пакета (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 ci # полный пайплайн качества composer check # быстрые проверки перед коммитом composer test # тесты (unit + integration + security + load)
Заключение: когда что выбирать
Сильные стороны cloudcastle/clock — самый широкий набор инструментов времени
в одном пакете (единственный с масштабируемым временем, смещением, зонозависимым
усечением и виртуальным sleepUntil), расширенный контракт timestamp()/microtime()
без создания объектов, жёсткая валидация ввода (включая null-байты) и sleep(),
переживающий прерывание сигналом.
Производительность: на задачах, где у пакета есть специализированный API
(unix-время, микросекундные метки, сдвиг виртуального времени), превосходство
над лучшим из аналогов — от +33 % до +248 % (таблица выше). На идентичных
PSR-20-операциях (now() — один и тот же вызов конструктора даты внутри движка
PHP у всех реализаций) обгон на десятки процентов физически недостижим ни для
одной библиотеки — там пакет стабильно в группе лидеров с отрывом от аутсайдеров
до 8×. Единственный сценарий, где лидирует не Clock, — «создание часов + первый
now()» у beste/clock (+4 %): beste не резолвит таймзону при создании и расплачивается
за это 6,4-кратно медленным now(). Память: пакет не добавляет измеримой
пиковой памяти ни в одном сценарии (0.0 КБ, байтовая точность), как и другие
лидеры; nesbot/carbon добавляет 3,5 МБ на классы.
Слабые стороны — пакет моложе конкурентов и объективно больше их по API:
если нужен только now(), половина возможностей останется невостребованной.
Честные рекомендации:
- Нужен только PSR-20
now()и минимум кода — беритеlcobucci/clockилиergebnis/clock: они крошечные и быстрые. - Проект уже на Symfony —
symfony/clockдастClock::get(),ClockAwareTraitиDatePoint, органично вписанные в экосистему. - Уже используете Carbon для дат — PSR-20 у вас есть «бесплатно»
через
Carbon\FactoryImmutable; отдельный пакет не обязателен. - Нужны управляемое время в тестах, симуляции, backoff-логика, замеры,
временные окна кэшей —
cloudcastle/clock: это единственный пакет, закрывающий все эти сценарии без дополнительных зависимостей.
Документация
- Wiki — руководство пользователя
- Возможности — страница на каждую фичу
- Сравнение с аналогами
- Архитектура — схемы работы пакета
- API-документация
Контакты
- Email — alex-4-17@yandex.ru
- VK — https://vk.com/leha_zorin
- Telegram — @CloudCastle85
- Телефон — +79872763991
- Сетка — https://set.ki/LdYRz76
Поддержать проект
Реквизиты для поддержки автора — в DONATE.md.
Contributors
Спасибо всем, кто вносит вклад в проект:
Список ведётся по спецификации all-contributors.
Лицензия
MIT © CloudCastle