cloud-castle / lock
Блокировки ресурсов для PHP 8.1+: межпроцессные (flock), распределённые (PSR-16) и внутрипроцессные — токен владельца, блокирующее ожидание, счётный семафор, TTL на инъектируемых часах (PSR-20), интроспекция TTL и synchronized(). Минимум зависимостей.
Package info
pkg:composer/cloud-castle/lock
Requires
- php: >=8.1
- cloud-castle/clock: >=1.0
- psr/clock: ^1.0
- psr/simple-cache: ^3.0
Requires (Dev)
- arvenil/ninja-mutex: ^0.6
- deptrac/deptrac: ^3.0 || ^4.0
- ergebnis/composer-normalize: ^2.45
- friendsofphp/php-cs-fixer: ^3.75
- icanhazstring/composer-unused: ^0.9
- infection/infection: ^0.29 || ^0.33
- malkusch/lock: ^2.2
- php-parallel-lint/php-parallel-lint: ^1.4
- 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/lock: ^6.4
- vimeo/psalm: ^6.0
- webmozart/assert: ^1.11
Suggests
- cloud-castle/cache: PSR-16 кэш-бэкенд для распределённых блокировок (CacheLockStore)
This package is auto-updated.
Last update: 2026-07-31 15:48:52 UTC
README
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano
CloudCastle Lock
Блокировки ресурсов для PHP 8.1+: межпроцессные (
flock), распределённые (PSR-16 кэш) и внутрипроцессные (в памяти) из одного пакета. Токен владельца (чужую блокировку не снять), TTL с инъектируемыми часами (PSR-20) для детерминированных тестов иsynchronized()с гарантированным снятием. Минимум зависимостей.
Установка
composer require cloud-castle/lock
Требуется PHP 8.1+.
Быстрый старт
<?php
use CloudCastle\Lock\Locks;
// Межпроцессная блокировка на flock: атомарна между процессами.
$lock = Locks::flock('/var/lock/app')->create('order:42');
if ($lock->acquire()) {
try {
// критическая секция — ресурс держим только мы
} finally {
$lock->release();
}
}
// Одним вызовом: захватить → выполнить → гарантированно снять (даже при исключении).
$result = Locks::flock('/var/lock/app')
->create('report:daily')
->synchronized(static fn (): string => generateReport());
// Распределённая блокировка через любой PSR-16 кэш (Redis, Memcached, ...).
$lock = Locks::cache($psr16Cache)->create('payment:777', ttlSeconds: 30);
// Внутрипроцессная блокировка с детерминированным TTL в тестах —
// подмените системные часы на mock/frozen (PSR-20).
$lock = Locks::inMemory($mockClock)->create('job', ttlSeconds: 30);
// Блокирующее ожидание: подождать освобождения ресурса не дольше 5 секунд.
$lock = Locks::flock('/var/lock/app')->create('order:42');
if ($lock->block(timeoutSeconds: 5.0)) {
try {
// ресурс освободился и захвачен нами
} finally {
$lock->release();
}
}
// Счётный семафор: не более 3 процессов одновременно (пул воркеров, квота API).
$semaphore = Locks::flock('/var/lock/app')->semaphore('api-quota', slots: 3);
if ($semaphore->acquire()) {
try {
// в этой секции одновременно максимум три держателя
} finally {
$semaphore->release();
}
}
// Интроспекция TTL: сколько секунд осталось до автоистечения — чтобы вовремя продлить.
$lock = Locks::inMemory($clock)->create('job', ttlSeconds: 30);
$lock->acquire();
$secondsLeft = $lock->remainingTtl(); // 30, 29, … (null для хранилищ без TTL)
Возможности
- Три бэкенда из коробки:
FlockStore(межпроцессныйflock),CacheLockStore(распределённый поверх PSR-16) иInMemoryStore(внутрипроцессный) — единый API через фасадLocks. - Токен владельца: снять или продлить блокировку может только её держатель — чужой токен не освободит ресурс (защита от случайного снятия).
synchronized(): критическая секция с захватом, выполнением и гарантированным снятием вfinally(в том числе при исключении).- Блокирующее ожидание
block(): подождать освобождения ресурса с таймаутом и настраиваемым интервалом повтора; пауза абстрагирована и детерминирована в тестах. - Счётный семафор:
semaphore('pool', slots: N)— до N одновременных держателей ресурса (пул воркеров, квота внешнего API) на любом бэкенде. - Интроспекция остатка TTL
remainingTtl(): сколько секунд осталось до автоистечения удерживаемой блокировки — чтобы вовремя её продлить. - TTL с инъектируемыми часами (PSR-20): истечение блокировки детерминировано
в тестах — «перематывайте» время
FrozenClock/MockClockбез реального ожидания. - Переиспользование дескрипторов во
FlockStore: повторный захват одного ресурса не платит заfopen/fclose— быстрый горячий путь для воркеров. - Хеширование имени ресурса (SHA-256): нет traversal и недопустимых символов в имени lock-файла или ключе кэша.
- Минимум зависимостей — только PSR-контракты и
cloud-castle/clock.
Сравнение с аналогами
Все таблицы ниже сгенерированы автоматически из честных сравнительных тестов (benchmarks/compare.php) на ОДИНАКОВОЙ операции для всех аналогов, PHP 8.3.32, без Xdebug.
1. Функциональность
| Возможность | 🏆 CloudCastle | symfony | malkusch | ninja-mutex | laravel | sf-semaphore |
|---|---|---|---|---|---|---|
| Несколько бэкендов из коробки (память / flock / PSR-16 кэш) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Токен владельца (снять/продлить может только держатель) | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| synchronized() — критическая секция с гарантированным снятием | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ |
| Блокирующее ожидание захвата с таймаутом (block) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Счётный семафор — N одновременных держателей | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Инъектируемые часы (PSR-20) — детерминированный TTL в тестах | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Интроспекция остатка TTL (remainingTtl) | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Минимум зависимостей (только PSR + cloud-castle) | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ |
| Всего | 🏆 8 | 4 | 4 | 3 | 4 | 2 |
2. Безопасность и корректность
| Свойство | 🏆 CloudCastle | symfony | malkusch | ninja-mutex | laravel | sf-semaphore |
|---|---|---|---|---|---|---|
| Токен владельца (защита от снятия чужой блокировки) | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| Fail-safe: истёкшая блокировка освобождается автоматически (TTL) | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| Атомарный межпроцессный захват (flock LOCK_EX/LOCK_NB) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Хеширование имени ресурса SHA-256 (нет traversal в имени файла/ключе) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Детерминированное истечение TTL (инъектируемые часы для аудита) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Всего | 🏆 5 | 4 | 2 | 1 | 3 | 2 |
3. Производительность
acquire + release flock-блокировки, 50 000 раз (минимум из 4).
| Решение | Время (мс) | |
|---|---|---|
| flock¹ | 65,8 | базовый уровень (не библиотека) |
| 🏆 malkusch | 87,7 | быстрейшее среди библиотек |
| CloudCastle | 90,7 | аналог |
| ninja-mutex | 667,2 | аналог |
| symfony | 957,4 | аналог |
4. Потребление памяти
Резидентная память библиотеки на 50 000 операций (изолированный замер с вычетом baseline рантайма).
| Решение | Пиковая память (KB) | |
|---|---|---|
| flock¹ | 2 | базовый уровень (не библиотека) |
| 🏆 malkusch | 23 | легчайшее среди библиотек |
| ninja-mutex | 31 | аналог |
| CloudCastle | 58 | аналог |
| symfony | 80 | аналог |
¹ Базовый уровень (нативные вызовы/примитивы без полноты решения) показан для контекста и не претендует на победу среди библиотек-аналогов.
Вывод: CloudCastle Lock — самый функциональный (8 возможностей против
≤ 4 у любого аналога) и самый безопасный среди рассмотренных: единственный,
кто объединяет три бэкенда (память/flock/PSR-16), токен владельца,
synchronized(), блокирующее ожидание, счётный семафор, интроспекцию TTL и
тестируемые PSR-20-часы одновременно, и при этом кратно быстрее
высокоуровневых аналогов (symfony/lock, arvenil/ninja-mutex) и чуть быстрее
низкоуровневого malkusch/lock. Единственный осознанный компромисс —
потребление памяти: malkusch и ninja-mutex легче, потому что проще; мы
платим килобайтами за переиспользование дескрипторов (это и даёт скорость) и за
богатый API. Функционалом, скоростью и безопасностью ради памяти мы не жертвуем.
Плюсы и минусы
Плюсы
- Функциональный суперсет: три бэкенда, токен владельца,
synchronized(), блокирующее ожидание, семафор и интроспекция TTL — в одном пакете. - Быстрейший среди библиотек-аналогов на горячем пути (переиспользование
дескрипторов
flock). - Тестируемость: TTL на инъектируемых PSR-20-часах и абстрагированная пауза ожидания — истечение и таймауты проверяются без реального ожидания.
- Безопасность по умолчанию: токен владельца (нельзя снять чужую блокировку), SHA-256-хеширование имени ресурса (нет traversal), fail-safe по TTL.
- Минимум зависимостей (только PSR-контракты и
cloud-castle/clock), строгая типизация, 100% покрытие и мутационная устойчивость (MSI 100%).
Минусы
- Потребление памяти выше, чем у минималистичных
malkusch/ninja-mutex(осознанный компромисс ради скорости и функционала). - Нет собственных «нативных» драйверов Redis/PDO/Zookeeper: распределённые блокировки идут через PSR-16-кэш (это покрывает Redis/Memcached/файл), а не через специализированные протоколы вроде PostgreSQL advisory locks.
- Моложе и менее распространён, чем
symfony/lockиmalkusch/lock.
Когда использовать
- Веб-воркеры и очереди задач: не дать двум воркерам обработать один заказ
или платёж —
flock-блокировка именованного ресурса с токеном владельца иsynchronized(); блокирующееblock(), когда задачу нельзя пропустить. - Ограничение конкурентности: не более N параллельных обращений к внешнему
API или пулу соединений — счётный
semaphore('pool', slots: N). - Распределённые блокировки: координация между несколькими серверами через
общий PSR-16-кэш (Redis/Memcached) —
Locks::cache()с TTL. - Тестируемая доменная логика с таймерами:
InMemoryStoreна PSR-20-часах даёт детерминированное истечение TTL без реального ожидания в тестах. - Когда достаточно нативного
flock: для разового межпроцессного захвата без токена владельца, нескольких бэкендов и TTL хватит и гологоflock()— берите пакет, когда нужны эти гарантии и единый API, а не сам по себеflock.
Разработка
composer install
composer check # линтеры + статический анализ + тесты
composer fix # автоисправления (Rector, PHP CS Fixer, PHPCBF)
composer ci # полный CI-пайплайн локально
Полный список команд с описаниями: composer run-script --list.
Документация
- Репозиторий: https://gitverse.ru/cloud-castle/lock
- История изменений: CHANGELOG.md
- Как внести вклад: CONTRIBUTING.md
- Кодекс поведения: CODE_OF_CONDUCT.md
- Политика безопасности: SECURITY.md
Лицензия
MIT © CloudCastle (alex-4-17@yandex.ru)
🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano