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);

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


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


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


После того как силами сообщества к 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 ;)

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

Поделиться с друзьями
-->

Комментарии (6)


  1. snowmage
    30.12.2016 11:56
    +1

    Что у вас с доменом?


    1. jigpuzzled
      30.12.2016 12:31

      Вчера переносил с `name.com` на `namecheap.com` и по ходу регистраторы наколдовали. Написал в суппорт, до конца дня все восстановится. Если что, чат тут: https://gitter.im/PHPixie/Hotline


  1. karrok
    01.01.2017 01:40

    А вы пробовали делать бенчмарки с другими кешами? Например с https://github.com/PHPSocialNetwork/phpfastcache


    1. jigpuzzled
      01.01.2017 13:19

      Тут в принципе и мерить нечего, все зависит от того какое хранилище вы используете. Посмотрел их код, и не увидел там ничего особенного что бы «ускоряло работу кэша в 7 раз» как пишет на гитхабе.


  1. phpishka
    01.01.2017 13:09
    +1

    Спасибо! Это как раз то чего мне очень не хватало в фреймворке, пришлось использовать Stash.
    А можно добавить возможность кэш в базу писать? А то у нас на шаред хостинге ни Редиса ни Мекешда нет.


    1. jigpuzzled
      01.01.2017 13:21

      Можно, но я бы очень рекомендовал уже перейти на VPS, 2017 уже ведь.