cloud-castle/memcached

Клиент Memcached для PHP 8.1+ на чистом PHP без расширений: текстовый, бинарный и meta-протокол, ketama-шардирование с failover, пул соединений, SASL и TLS, PSR-6 и PSR-16, теги, компрессия, CAS, счётчики, метрики, встроенный тестовый сервер.

Maintainers

Package info

gitverse.ru/cloud-castle/memcached

Homepage

Issues

Documentation

pkg:composer/cloud-castle/memcached

Transparency log

Statistics

Installs: 40

Dependents: 1

Suggesters: 1

v1.0.4 2026-07-31 05:50 UTC

README

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

CloudCastle Memcached

CloudCastle Memcached

Клиент Memcached для PHP 8.1+ на чистом PHP — без ext-memcached и ext-memcache.

Packagist

Packagist Version Downloads PHP Version License

Репозиторий

GitVerse Issues Wiki

Качество кода

PHPStan Psalm PHPMD PHPCS Deptrac Coverage Infection MSI OpenSSF Scorecard

Зачем он нужен

Штатный способ работать с Memcached из PHP — расширение ext-memcached поверх libmemcached. Оно быстрое, но его нужно собирать под каждую версию PHP, и на управляемом хостинге или в чужом контейнере его может просто не быть.

Этот пакет реализует протокол Memcached на PHP: composer require — и клиент работает. Заодно становятся возможными вещи, которых расширение не даёт: подпись значений против подмены, meta-команды Memcached 1.6+ и тестовый сервер внутри процесса.

Установка

composer require cloud-castle/memcached

Требуется PHP 8.1+. Расширения не обязательны: igbinary, msgpack, zstd, lz4 подключаются автоматически, если собраны. Если собрано ext-memcached, клиент может работать через него — см. «Выбор бэкенда».

Быстрый старт

use CloudCastle\Memcached\Client;
use CloudCastle\Memcached\Configuration\{Config, Server};

$client = new Client(new Config([new Server('127.0.0.1', 11211)]));

$client->set('user:1', ['name' => 'Иван'], 300);
$user = $client->get('user:1');

// Значение возвращается того же типа, каким было записано:
// false не превратится в пустую строку, а записанный null отличим от промаха.
$client->set('flag', false);
var_dump($client->get('flag'));   // bool(false)
var_dump($client->has('flag'));   // bool(true)

Вычислить значение при промахе:

$report = $client->remember('report:2026-07', fn () => buildHeavyReport(), 3600);

Кластер с весами и переключением при отказе:

$client = new Client(new Config([
    new Server('10.0.0.1', 11211, weight: 2, alias: 'node-1'),
    new Server('10.0.0.2', 11211, weight: 1, alias: 'node-2'),
]));

Интеграция по стандартам PSR

Код, написанный под Psr\SimpleCache\CacheInterface или Psr\Cache\CacheItemPoolInterface, работает с этим клиентом без правок:

use CloudCastle\Memcached\Psr\{CacheItemPool, SimpleCache};

$psr16 = new SimpleCache($client);
$psr16->set('ключ', $value, 300);

$psr6 = new CacheItemPool($client);
$item = $psr6->getItem('ключ');

if (!$item->isHit()) {
    $item->set(buildValue())->expiresAfter(300);
    $psr6->save($item);
}

Разница между стандартами стоит того, чтобы её знать: PSR-16 не отличает записанный null от промаха, а PSR-6 отличает — через isHit(). Родной API клиента различает всегда, поэтому там, где кэшируются отрицательные ответы, лучше использовать его напрямую.

Выбор бэкенда

Операции выполняет один из двух исполнителей: собственная реализация протокола на PHP либо расширение ext-memcached поверх libmemcached.

use CloudCastle\Memcached\Configuration\{Backend, Config, Server, Topology};

// Расширение, если оно собрано; иначе — чистый PHP. Одна сборка
// приложения работает и на боевом сервере, и на управляемом хостинге.
$client = new Client(new Config(
    [new Server('127.0.0.1', 11211)],
    topology: new Topology(backend: Backend::Auto),
));

Что выбрать — зависит от нагрузки, и цифры честные:

НагрузкаЧистый PHPlibmemcached
5000 циклов set + get1239 мс1411 мс
500 пакетов по 100 ключей453 мс176 мс

На одиночных операциях быстрее собственный протокол: getMulti с CAS-токенами обходится дороже, чем один кадр meta. На пакетном чтении втрое выигрывает расширение — сотню ответов оно разбирает в C, а не в PHP.

Записи бэкенда libmemcached побайтово совместимы с symfony/cache и другими обёртками над расширением: сериализация оставлена самому расширению. Обратная сторона — формат отличается от собственного кодека пакета, поэтому смена бэкенда требует прогрева кэша. Подпись значений и meta-команды на этом бэкенде недоступны: расширение их не поддерживает.

Возможности

Каждая возможность описана отдельной страницей wiki с примерами и сравнением с аналогами.

ВозможностьЧто даёт
Два бэкендачистый PHP или libmemcached, с автовыбором по окружению
Три диалекта протоколатекстовый, бинарный и meta-команды Memcached 1.6+
Meta-командызначение, TTL и версия CAS за одно обращение
Кластер и ketamaконсистентное хеширование, совместимое с libmemcached
Отказоустойчивостьпереключение на живой узел и карантин упавших
СериализацияPHP, JSON, igbinary, MessagePack + белый список классов
Сжатиеdeflate, gzip, zstd, lz4 с порогом и проверкой выгоды
Подпись значенийHMAC против подмены записи в кэше
Безопасностьзащита от инъекции команд, SASL, TLS
Счётчики и CASатомарные операции на стороне сервера
Тестовый сервертесты без сети и без запуска memcached
Метрикипопадания, промахи и здоровье узлов
PSR-16 и PSR-6интеграция с фреймворками без правок кода

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

Все таблицы ниже и бейджи качества выше генерируются автоматически из замеров и отчётов инструментов: composer docs:build. Ни одна цифра не написана руками — зашитое в разметку число устаревает после первого же добавленного теста и начинает врать читателю.

Функциональность

ВозможностьCloudCastlesymfonyilluminatescrapbookphpfastcachestashlaminasext-memcached¹
Работает без расширений PHP (чистый PHP)
PSR-16 (SimpleCache) из коробки
PSR-6 (CacheItemPool) из коробки
Выбор бэкенда: чистый PHP или libmemcached
Автовыбор бэкенда по наличию расширения
Текстовый протокол
Бинарный протокол
Meta-команды Memcached 1.6+
Чтение TTL и CAS одним запросом
Консистентное хеширование ketama
Автоматическое переключение при отказе узла
Карантин упавших узлов
SASL-аутентификация
TLS-соединение
UNIX-сокеты
Оптимистическая блокировка (CAS)
Атомарные счётчики с созданием при промахе
append / prepend без чтения значения
Подпись значений (HMAC) против подмены
Белый список классов при восстановлении
Выбор формата сериализации (4 формата)
Выбор алгоритма сжатия (4 алгоритма)
Встроенный тестовый сервер без сети
Инъектируемые часы (детерминированный TTL)
Счётчики попаданий и промахов клиента
Итого возможностей2512791041112
🏆 Победитель🏆

Безопасность

Механизм защитыCloudCastlesymfonyilluminatescrapbookphpfastcachestashlaminasext-memcached¹
Отказ на управляющие символы в ключе (инъекция команды)
Ключ не обрезается молча при превышении длины
Белый список классов при восстановлении значения
Лимит длины нагрузки при разборе (защита от исчерпания памяти)
Подпись значений HMAC с обнаружением подмены
Строгий режим: неподписанная запись отвергается
TLS с полной проверкой сертификата по умолчанию
Пароль SASL не попадает в дампы и трассировки
Учётные данные не сериализуются
Fail-safe: отказ узла не выдаётся за промах кэша
Итого возможностей103122022
🏆 Победитель🏆

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

set + get на общем сервере Memcached, 20 000 раз (минимум из 3).

ПакетВремя🏆 Победитель
ext-memcached¹ (базовый уровень)3 972.4 мс
illuminate4 649.4 мс🏆
phpfastcache5 213.5 мс
scrapbook5 286.4 мс
symfony5 822.3 мс
stash9 082.3 мс
laminas9 388.1 мс
CloudCastle10 271.6 мс

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

Память самой библиотеки (классы + структуры данных): пик рабочей фазы минус baseline, снятый до создания клиента в изолированном процессе, — стоимость PHP и автолоадера вычтена.

ПакетПамять🏆 Победитель
ext-memcached¹ (базовый уровень)1 KB
scrapbook96 KB🏆
illuminate188 KB
stash396 KB
symfony431 KB
CloudCastle695 KB
laminas1 396 KB
phpfastcache2 772 KB

Утечки памяти

_Рост памяти за 20 000 операций после прогрева и gc_collectcycles (изолированный процесс, только целевая библиотека; 0 — утечек нет).

ПакетРост🏆 Победитель
stash-17 KB🏆
CloudCastle0 KB
symfony0 KB
illuminate0 KB
scrapbook0 KB
phpfastcache0 KB
laminas0 KB
ext-memcached¹ (базовый уровень)0 KB

Качество кода

МетрикаCloudCastlesymfonyilluminatescrapbookphpfastcachestashlaminas🏆 Победитель
Синтаксические ошибки (phplint)0000000CloudCastle, symfony, illuminate, scrapbook, phpfastcache, stash, laminas 🏆
Файлы со strict_types, %1000010099019CloudCastle, scrapbook 🏆
final-классы, %1009001054CloudCastle 🏆
Runtime-зависимостей4542217stash 🏆
Файлов исходников938944401423374stash 🏆
Минимальная версия PHP>=8.1>=8.1^8.1>=8.0.0>=8.0^8.0~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0

¹ Базовый уровень: расширение на C, не composer-библиотека. Показано для контекста и не претендует на победу среди PHP-пакетов.

Возможности проверены по исходникам и документации пакетов. Отметка «есть» ставится и тогда, когда возможность приходит из расширения под пакетом: пользователю она доступна.

Замер выполнен 2026-07-30 на PHP 8.1.34.

Честно о плюсах и минусах

Плюсы

  • Работает без расширений: composer require — и всё, никакой пересборки PHP.
  • Функционально шире любого аналога: meta-команды, подпись значений, карантин узлов, встроенный тестовый сервер — этого нет ни у одного из сравниваемых.
  • Два бэкенда под одним API: без расширения работает чистый PHP, с расширением — libmemcached, и пакетное чтение ускоряется втрое.
  • Стандарты PSR-16 и PSR-6 из коробки — пакет встаёт в любой фреймворк без переписывания прикладного кода.
  • Безопасность заложена в поведение: инъекция команды через ключ невозможна, восстановление объектов ограничено белым списком, отказ узла не выдаётся за промах кэша.
  • Тестируемость: клиент проверяется целиком, включая обрывы связи и повреждённые ответы, без поднятия сервера.

Минусы

  • На пакетных операциях чистый PHP уступает расширению втрое. Разбор сотни ответов в PHP объективно медленнее, чем в C. Лечится переключением на бэкенд libmemcached там, где расширение доступно, — но тогда теряются подпись значений и meta-команды.
  • Памяти на процесс тратится больше, чем у обёрток над расширением: структуры протокола живут в PHP, а не в C.
  • Подпись значений несовместима с серверными счётчиками: подписанное число сервер не умеет инкрементировать.

Когда его стоит брать

Берите, если:

  • расширение ext-memcached недоступно или его установка — отдельная боль (управляемый хостинг, чужой контейнер, быстрый прототип);
  • в кэше лежат данные, подмена которых опасна: права доступа, результаты проверок, флаги доступности — тогда подпись значений окупает всё;
  • нужны meta-команды Memcached 1.6+: чтение значения вместе с остатком TTL экономит целый цикл запросов при досрочном продлении;
  • важна тестируемость кэширующего слоя без инфраструктуры в CI;
  • кластер меняет состав, и нужен предсказуемый перенос ключей плюс карантин отказавших узлов.

Не берите, если:

  • узкое место — именно скорость обращений к кэшу, расширение уже установлено, и никакой дополнительный функционал не нужен: тогда ext-memcached напрямую или тонкая обёртка над ним будут быстрее;
  • нужен только PSR-16 поверх нескольких бэкендов сразу — для этого лучше подходит cloud-castle/cache.

Разработка

composer install
composer check       # линтеры + статический анализ + тесты
composer test:full   # полный порядок проверок качества
composer docs:build  # перегенерировать сравнительные таблицы и бейджи

Интеграционные тесты требуют сервер; адрес задаётся через MEMCACHED_HOST и MEMCACHED_PORT. Без сервера они помечаются пропущенными, а не «зелёными».

docker run -d --name memcached -p 11211:11211 memcached:1.6-alpine

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

Лицензия

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

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