Еще даже не закончилось голосование за стандарт PSR-16 а PHPixie уже его поддерживает. Казалось бы кэширование настолько уже обработанная сфера, что тут уже нечем и удивить, но надеюсь прочитав статью вы найдете в PHPixie Cache для себя что-то новое и полезное. Как всегда в конце статьи вас ждет инструкция по использованию Cache без фреймворка и также информация о том как расширить компонент и помочь проекту.
Настройка
Сразу можно быстро пройтись по поддерживаемым драйверам. Пока их всего пять: void, memory, phpfile, memcached и redis.
// /assets/config/cache.php
return [
// Описываем доступные хранилища
'default' => [
// Ничего не сохраняет
'driver' => 'void'
],
'second' => [
// Данные сохраняются в простом массиве в памяти
'driver' => 'memory',
/* Опционально */
/**
* Время жизни по умолчанию, может быть задано в секундах
* или в значениях DateInterval (например P1D это 1 день).
* По умолчанию null, то есть хранить вечно
*/
'defaultExpiry' => 10,
/**
* Число между 1 и 1000, которое определяет
* частоту сборки мусора. Чем оно выше тем чаще.
* По умолчанию 10, то есть примерно 1% всех запросов
* приведет к запуску сборки.
*/
'cleanupProbability' => 10
],
'third' => [
// Сохраняет в .php файлы
'driver' => 'phpfile',
// Путь относительно /assets/cache
'path' => 'third',
/* Опционально */
'defaultExpiry' => null,
'cleanupProbability' => 10
],
'fourth' => [
// Memcached
'driver' => 'memcached',
/**
* Тот же формат что к вызову метода Memcached::addServers,
* но порт и вес можно не указывать, по умолчанию будут 1211 и 1
*/
'servers' => [
['127.0.0.1']
],
/* Опционально */
'defaultExpiry' => null
],
'fifth' => [
// Redis через пакет Predis
'driver' => 'redis',
// Тот же формат что к конструктору Predis\Client
'connection' => array(
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379
),
/* Опционально */
'defaultExpiry' => null
]
];
Использование
Как и говорится в заголовке PHPixie Cache поддерживает PSR-6 и новый упрощенный PSR-16, вот как выглядит интерфейс PHPixie\Cache\Pool с которым мы будем часто встречаться:
namespace PHPixie\Cache;
use PHPixie\Cache\Pool\Prefixed;
use Psr\Cache\CacheItemPoolInterface;
use Psr\SimpleCache\CacheInterface;
// Наследуем у PSR-6 и PSR-16
interface Pool extends CacheItemPoolInterface, CacheInterface
{
/**
* Создает PSR-6 Item без получения его из кеша
* @param string $key
* @param mixed $value
* @return Item
*/
public function createItem($key, $value = null);
/**
* Создает виртуальный Pool с фиксированным префиксом.
* Об этом чуть позже.
* @param string $prefix
* @return Prefixed
*/
public function prefixedPool($prefix);
}
И теперь сами примеры использования:
// Получаем одно из хранилищ.
$storage = $cache->storage('second');
// PSR-6
public function getFairies()
{
$item = $this->storage->getItem('fairies');
if (!$item->isHit()) {
$fairies = $this->generateFairies();
$item->set($fairies);
$item->expiresAfter(100);
$this->storage->save($item);
}
return $item->get();
}
// PSR-16
public function getFairies()
{
$fairies = $this->storage->get('fairies');
if($fairies === null) {
$fairies = $this->buildFairies();
$this->storage->set('fairies', $fairies, 100);
}
return $fairies;
}
Впрочем нет смысла переписывать здесь работу с этими PSR-ами так как документации к ним и так полно, а если вы используете PHPStorm то подсказки вам все покажут.
Интересные фичи
Теперь о том, чем PHPixie Cache отличается от других библиотек.
Префиксные пулы
Когда несколько частей приложения пишут в тот же кэш чтобы избежать коллизий приходится придумывать уникальные префиксы для ключей. Как часто вам приходилось писать такой код:
$key = 'article-'.$id;
$article = $cache->get($key);
Если те же сущности кэшируются в разных частях приложения надо следить за тем чтобы всегда использовать тот-же префикс или выносить эту логику в отдельный класс. В PHPixie Cache эта проблема решается префиксным пулом, который проксирует запросы в хранилище автоматически добавляя префикс.
$storage = $cache->storage('default');
$articlesPool = $storage->prefixedPool('article');
$articlesPool->set($article->id, $article->html());
// то же самое что
$storage ->set('article.'.$article->id, $article->html());
Как вы наверное уже догадались $articlesPool имплементирует тот же интерфейс PHPixie\Cache\Pool что и хранилище и его тоже можно префиксить создавая иерархию. Это удобно тем что в будущем, когда статей станет много, такой префиксный пул можно заменить реальным отдельным хранилищем не переписывая код. Таким образом можно навсегда избавить себя от проблемы с ключами и префиксами.
Сам PHPixie\Cache это тоже Pool
У большинства пользователей скорее всего будет только одно хранилище для кэша, так почему бы не упростить им жизнь?
// вместо
$cache->storage('default')->get('fairy');
// можно просто
$cache->get('fairy');
При сохранении в файлы ключи не хешируються
Большинство библиотек генерируют имя файла делая хеш ключа. Это делается для того чтобы избежать проблем с кодировкой если вам вздумается задать ключ кракозябрами, но такое делать не рекомендуется в принципе. С другой стороны это хеширование занимает время процессора, так что особой причины оставлять его нет. Впрочем в планах есть добавить опцию включения этой функции параметром, для тех кому это критично.
Оптимизация кэширования в файлы
Опять же много библиотек используют сериализацию как формат файлов кэша. При этом сериализуется само значение и срок его годности. Тут минусов два: чтобы проверить срок годности надо прочитать и десериализовать весь файл и то что сама десериализация тоже недешевая для больших значений. Что же делает PHPixie\Cache? Посмотрим пример созданного файла:
<?php /*1483041355*/
return array(1,2,3);
В первой строке находится срок годности файла, так что для проверки его свежести достаточно считать только ее а не весь файл. К тому же данные из файла получаются оператором include
и поэтому код файла попадет в opcache, так что получение данных из него несколько раз подряд на самом деле не будет считывать его с диска пока он не изменится. Кстати старый подход с сериализацией тоже будет скоро доступен в компоненте.
Использование без фреймворка
$slice = new \PHPixie\Slice();
$filesystem = new \PHPixie\Filesystem();
$config = $slice->arrayData([
'default' => [
'driver' => 'memory'
]
]);
// /tmp/cache будет корневой папкой
// относительно которой будут прописываться пути
$root = $filesystem->root('/tmp/cache/');
$cache = new \PHPixie\Cache($config, $root);
// А если вы не собираетесь использовать кеш в файлы, то можно просто
$cache = new \PHPixie\Cache($config);
Как видите обязательная зависимость только одна, так что если вы ищете простой и понятный кэш то надеюсь вам понравиться.
Github: https://github.com/phpixie/cache
Добавление драйверов
После того как силами сообщества к PHPixie Social добавилось четыре новых провайдера я решил что пора добавлять небольшой чеклист о том как добавить свой драйвер в пакет:
- Создать класс
\PHPixie\Cache\Drivers\Type\YourDriver
унаследовав от\PHPixie\Cache\Drivers\Driver
. - Прописать его в
\PHPixie\Cache\Builder::$driverMap
. - Добавить тест
\PHPixie\Tests\Cache\Driver\YourDriverTest
унаследовав отPHPixie\Tests\Cache\DriverTest
и прописав в нем тестовый конфиг. - Подправить
.travis.yml
иcomposer.json
если есть какие-то новые зависимости. - Отправить Pull Request ;)
Если что, то мы всегда рады помочь с любыми проблемами в нашем чате.
Комментарии (6)
karrok
01.01.2017 01:40А вы пробовали делать бенчмарки с другими кешами? Например с https://github.com/PHPSocialNetwork/phpfastcache
jigpuzzled
01.01.2017 13:19Тут в принципе и мерить нечего, все зависит от того какое хранилище вы используете. Посмотрел их код, и не увидел там ничего особенного что бы «ускоряло работу кэша в 7 раз» как пишет на гитхабе.
phpishka
01.01.2017 13:09+1Спасибо! Это как раз то чего мне очень не хватало в фреймворке, пришлось использовать Stash.
А можно добавить возможность кэш в базу писать? А то у нас на шаред хостинге ни Редиса ни Мекешда нет.
snowmage
Что у вас с доменом?
jigpuzzled
Вчера переносил с `name.com` на `namecheap.com` и по ходу регистраторы наколдовали. Написал в суппорт, до конца дня все восстановится. Если что, чат тут: https://gitter.im/PHPixie/Hotline