Блокировки ресурсов для PHP 8.1+: межпроцессные (flock), распределённые (PSR-16) и внутрипроцессные — токен владельца, блокирующее ожидание, счётный семафор, TTL на инъектируемых часах (PSR-20), интроспекция TTL и synchronized(). Минимум зависимостей.

Maintainers

Package info

gitverse.ru/cloud-castle/lock

Homepage

Issues

Documentation

pkg:composer/cloud-castle/lock

Transparency log

Statistics

Installs: 8

Dependents: 1

Suggesters: 0

v1.1.0 2026-07-31 11:08 UTC

This package is auto-updated.

Last update: 2026-07-31 15:48:52 UTC


README

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano

CloudCastle Lock

CloudCastle Lock

Packagist Version PHP Version License Total Downloads Monthly Downloads Dependents

GitVerse Quality CI PHP Matrix Docs

PHPStan Psalm PHPMD PHPCS Coverage Infection MSI OpenSSF Scorecard

Блокировки ресурсов для 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. Функциональность

Возможность🏆 CloudCastlesymfonymalkuschninja-mutexlaravelsf-semaphore
Несколько бэкендов из коробки (память / flock / PSR-16 кэш)
Токен владельца (снять/продлить может только держатель)
synchronized() — критическая секция с гарантированным снятием
Блокирующее ожидание захвата с таймаутом (block)
Счётный семафор — N одновременных держателей
Инъектируемые часы (PSR-20) — детерминированный TTL в тестах
Интроспекция остатка TTL (remainingTtl)
Минимум зависимостей (только PSR + cloud-castle)
Всего🏆 844342

2. Безопасность и корректность

Свойство🏆 CloudCastlesymfonymalkuschninja-mutexlaravelsf-semaphore
Токен владельца (защита от снятия чужой блокировки)
Fail-safe: истёкшая блокировка освобождается автоматически (TTL)
Атомарный межпроцессный захват (flock LOCK_EX/LOCK_NB)
Хеширование имени ресурса SHA-256 (нет traversal в имени файла/ключе)
Детерминированное истечение TTL (инъектируемые часы для аудита)
Всего🏆 542132

3. Производительность

acquire + release flock-блокировки, 50 000 раз (минимум из 4).

РешениеВремя (мс)
flock¹65,8базовый уровень (не библиотека)
🏆 malkusch87,7быстрейшее среди библиотек
CloudCastle90,7аналог
ninja-mutex667,2аналог
symfony957,4аналог

4. Потребление памяти

Резидентная память библиотеки на 50 000 операций (изолированный замер с вычетом baseline рантайма).

РешениеПиковая память (KB)
flock¹2базовый уровень (не библиотека)
🏆 malkusch23легчайшее среди библиотек
ninja-mutex31аналог
CloudCastle58аналог
symfony80аналог

¹ Базовый уровень (нативные вызовы/примитивы без полноты решения) показан для контекста и не претендует на победу среди библиотек-аналогов.

Вывод: 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.

Документация

Лицензия

MIT © CloudCastle (alex-4-17@yandex.ru)

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano