PHPixie Cache: PSR-6, PSR-16 и несколько интересных фич

от автора

image

Еще даже не закончилось голосование за стандарт 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);

Как видите обязательная зависимость только одна, так что если вы ищете простой и понятный кэш то надеюсь вам понравиться.

Добавление драйверов

После того как силами сообщества к PHPixie Social добавилось четыре новых провайдера я решил что пора добавлять небольшой чеклист о том как добавить свой драйвер в пакет:

  1. Создать класс \PHPixie\Cache\Drivers\Type\YourDriver унаследовав от \PHPixie\Cache\Drivers\Driver.
  2. Прописать его в \PHPixie\Cache\Builder::$driverMap.
  3. Добавить тест \PHPixie\Tests\Cache\Driver\YourDriverTest унаследовав от PHPixie\Tests\Cache\DriverTest и прописав в нем тестовый конфиг.
  4. Подправить .travis.yml и composer.json если есть какие-то новые зависимости.
  5. Отправить Pull Request 😉

Если что, то мы всегда рады помочь с любыми проблемами в нашем чате.

ссылка на оригинал статьи https://habrahabr.ru/post/318778/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *