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

в 7:45, , рубрики: cache, framework, php, phpixie, PSR-6, Разработка веб-сайтов

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',

         // Тот же формат что к конструктору PredisClient
        'connection' => array(
            'scheme' => 'tcp',
            'host'   => '127.0.0.1',
            'port'   => 6379
        ),

        /* Опционально */
        'defaultExpiry' => null
    ]
];

Использование

Как и говорится в заголовке PHPixie Cache поддерживает PSR-6 и новый упрощенный PSR-16, вот как выглядит интерфейс PHPixieCachePool с которым мы будем часто встречаться:

namespace PHPixieCache;

use PHPixieCachePoolPrefixed;
use PsrCacheCacheItemPoolInterface;
use PsrSimpleCacheCacheInterface;

// Наследуем у 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 имплементирует тот же интерфейс PHPixieCachePool что и хранилище и его тоже можно префиксить создавая иерархию. Это удобно тем что в будущем, когда статей станет много, такой префиксный пул можно заменить реальным отдельным хранилищем не переписывая код. Таким образом можно навсегда избавить себя от проблемы с ключами и префиксами.

Сам PHPixieCache это тоже Pool

У большинства пользователей скорее всего будет только одно хранилище для кэша, так почему бы не упростить им жизнь?

// вместо
$cache->storage('default')->get('fairy');

// можно просто
$cache->get('fairy');

При сохранении в файлы ключи не хешируються

Большинство библиотек генерируют имя файла делая хеш ключа. Это делается для того чтобы избежать проблем с кодировкой если вам вздумается задать ключ кракозябрами, но такое делать не рекомендуется в принципе. С другой стороны это хеширование занимает время процессора, так что особой причины оставлять его нет. Впрочем в планах есть добавить опцию включения этой функции параметром, для тех кому это критично.

Оптимизация кэширования в файлы
Опять же много библиотек используют сериализацию как формат файлов кэша. При этом сериализуется само значение и срок его годности. Тут минусов два: чтобы проверить срок годности надо прочитать и десериализовать весь файл и то что сама десериализация тоже недешевая для больших значений. Что же делает PHPixieCache? Посмотрим пример созданного файла:

<?php /*1483041355*/
return array(1,2,3);

В первой строке находится срок годности файла, так что для проверки его свежести достаточно считать только ее а не весь файл. К тому же данные из файла получаются оператором include и поэтому код файла попадет в opcache, так что получение данных из него несколько раз подряд на самом деле не будет считывать его с диска пока он не изменится. Кстати старый подход с сериализацией тоже будет скоро доступен в компоненте.

Использование без фреймворка

$slice = new PHPixieSlice();
$filesystem = new PHPixieFilesystem();

$config = $slice->arrayData([
    'default' => [
         'driver' => 'memory'
    ]
]);

// /tmp/cache будет корневой папкой
// относительно которой будут прописываться пути
$root = $filesystem->root('/tmp/cache/');

$cache = new PHPixieCache($config, $root);

// А если вы не собираетесь использовать кеш в файлы, то можно просто
$cache = new PHPixieCache($config);

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

Github: https://github.com/phpixie/cache

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

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

  1. Создать класс PHPixieCacheDriversTypeYourDriver унаследовав от PHPixieCacheDriversDriver.
  2. Прописать его в PHPixieCacheBuilder::$driverMap.
  3. Добавить тест PHPixieTestsCacheDriverYourDriverTest унаследовав от PHPixieTestsCacheDriverTest и прописав в нем тестовый конфиг.
  4. Подправить .travis.yml и composer.json если есть какие-то новые зависимости.
  5. Отправить Pull Request ;)

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

Автор: jigpuzzled

Источник

Поделиться

* - обязательные к заполнению поля