Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Подключение системы кэширования

John edited this page Feb 21, 2017 · 3 revisions

Подключение системы кэширования в Bitrix d7

Чтобы использовать тип кэширования, который не поддерживается системой или использовать свой тип кэширования, нужно задать массив настроек в ключе type:

'cache' => 
  array (
    'value' => 
    array (
      'type' => array(
        'class_name' => '', //Класс реализующий интерфейс ICacheEngine
        'required_file' => '', //Файл относительно папки local или bitrix
        'required_remote_file' => '', //Абсолютный путь к файлу
        'extension' => '' //Подключение через extension_loaded (имя расширения)
      ),
    ),
    'readonly' => false,
  ),
  • readonly - ключ означает, что нельзя изменить значение через API.
  • class_name - класс реализующий интерфейс ICacheEngine (вместе с namespace).
  • required_file - Файл относительно папки local или bitrix (который нужно подключить для реализации технологии кэша).
  • required_remote_file - Абсолютный путь к файлу (Если файл находится вне папки local или bitrix).
  • extension - Расширение php, которое должно быть включено.

Интерфейс ICacheEngine

Первое и необходимое, что нужно реализовать это класс работы с кэшем, который имплементирует интерфейс ICacheEngine

namespace Bitrix\Main\Data;

interface ICacheEngine
{
	public function isAvailable(); 
	public function clean($baseDir, $initDir = false, $filename = false);
	public function read(&$arAllVars, $baseDir, $initDir, $filename, $TTL);
	public function write($arAllVars, $baseDir, $initDir, $filename, $TTL);
	public function isCacheExpired($path);
}

/*Необязательно, но можно имплементировать, если будет необходимо. Все типы кэширования битрикс из коробки 
имплементируют этот интерфейс*/
interface ICacheEngineStat
{
	public function getReadBytes();
	public function getWrittenBytes();
	public function getCachePath();
}

Хранение кэша в сессии пользователя CacheEngineSession.

В качестве примера, реализуем класс для хранения кэша в сессии пользователя. Не рекомендуется использовать данных тип кэширования, особенно на платформе битрикс, приведено в качестве ознакомления и понимания основных принципов.

use \Bitrix\Main\Data\ICacheEngine;
use \Bitrix\Main\Data\ICacheEngineStat;
use \Bitrix\Main\Config\Configuration;

class CacheEngineSession implements ICacheEngine, ICacheEngineStat
{

}

Расположение required_file

Можно положить класс CacheEngineSession.php в папку /local/php_interface/lib/cache/.

Итоги

Файл настроек будет выглядеть примерно так:

'cache' => 
  array (
    'value' => 
    array (
      'type' => array(
        'class_name' => 'CacheEngineSession',
        'required_file' => 'php_interface/lib/cache/CacheEngineSession.php',
         //'required_remote_file' => '', //Не требуется
         //'extension' => '' //Расширение не требуется
      ),
    ),
    'readonly' => false,
  ),

Неиспользуемые ключи нужно неуказывать, так как внутри ядра идут проверки isset, которые приведут к неожиданным результатам

  • Примечание разработчика

Результатом правильного выполнения инструкции будет название CacheEngineSession в Административная часть -> Настройки -> Панель производительности -> Вкладка Битрикс.

Сессия пользователя это хорошо, но практической ценности от неё никакой (в качестве кэша). Поэтому рассмотрим более практичный подход.

Хранение кэша в shmop CacheEngineShmop (Разделяемая (shared) память).

В качестве примера, реализуем класс для хранения кэша в shared памяти unix. Метод и технология достаточно устаревшие.

use \Bitrix\Main\Data\ICacheEngine;
use \Bitrix\Main\Data\ICacheEngineStat;
use \Bitrix\Main\Config\Configuration;

/**
 * Caching based on Unix Share Memory
 *
 * # ipcs -m
 * List all segments used
 *
 * # ipcs -lm
 * ------ Shared Memory Limits --------
 * max number of segments = 4096       <--- this is SHMMNI
 * max seg size (kbytes) = 67108864    <--- this is SHMMAX
 * max total shared memory (kbytes) = 17179869184<- this is SHMALL
 * min seg size (bytes) = 1
 *
 *
 */

class CacheEngineShmop implements ICacheEngine, ICacheEngineStat
{

}

Расположение required_file

Можно положить класс CacheEngineShmop.php в папку /local/php_interface/lib/cache/.

Итоги

Файл настроек будет выглядеть примерно так:

'cache' => 
  array (
    'value' => 
    array (
      'type' => array(
        'class_name' => 'CacheEngineShmop',
        'required_file' => 'php_interface/lib/cache/CacheEngineShmop.php',
      ),
    ),
    'readonly' => false,
  ),

Результатом правильного выполнения инструкции будет название CacheEngineShmop в Административная часть -> Настройки -> Панель производительности -> Вкладка Битрикс.

Ссылки по теме

PHP

CacheEngineSession.php

<?php

use \Bitrix\Main\Data\ICacheEngine;
use \Bitrix\Main\Data\ICacheEngineStat;
use \Bitrix\Main\Config\Configuration;

class CacheEngineSession implements ICacheEngine, ICacheEngineStat
{
    /**
     * @var string
     */
    private $sid = "";

    /**
     * Переменные для реализации методов из интерфейса ICacheEngineStat
     * @var bool
     */
    private $written = false;
    /**
     * @var bool
     */
    private $read = false;

    /**
     * CacheEngineSession constructor.
     */
    public function __construct()
    {
        $v = Configuration::getValue("cache");
        if ($v != null && isset($v["sid"]) && ($v["sid"] != ""))
            $this->sid = $v["sid"];
        else
            $this->sid = "BX";
    }

    /**
     * @return integer
     */
    public function getReadBytes()
    {
        return $this->read;
    }

    /**
     * @return integer
     */
    public function getWrittenBytes()
    {
        return $this->written;
    }

    /**
     * @return string
     */
    public function getCachePath()
    {
        return '';
    }

    /**
     * @return bool
     */
    public function isAvailable()
    {
        return $this->checkSession();
    }

    /**
     * @param $baseDir
     * @param bool|false $initDir
     * @param bool|false $filename
     */
    public function clean($baseDir, $initDir = false, $filename = false)
    {
        $this->checkSession();

        $this->sessionOperation($baseDir, $initDir, $filename, 'clean');
    }

    /**
     * @param $arAllVars
     * @param $baseDir
     * @param $initDir
     * @param $filename
     * @param $TTL
     * @return bool
     */
    public function read(&$arAllVars, $baseDir, $initDir, $filename, $TTL)
    {
        $this->checkSession();

        $arAllVars = $this->sessionOperation($baseDir, $initDir, $filename, 'read');

        if ($arAllVars) {
            $this->read = strlen($arAllVars);
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param $arAllVars
     * @param $baseDir
     * @param $initDir
     * @param $filename
     * @param $TTL
     */
    public function write($arAllVars, $baseDir, $initDir, $filename, $TTL)
    {
        $this->checkSession();
        $this->sessionOperation($baseDir, $initDir, $filename, 'add', $arAllVars);
        if (isset($arAllVars)){
            $this->written = strlen($arAllVars);
        }

    }

    /**
     * @param $path
     * @return bool
     */
    public function isCacheExpired($path)
    {
        return false;
    }

    /**
     * @return bool
     */
    protected function checkSession()
    {
        if (session_status() == PHP_SESSION_NONE)
            session_start();

        return !(session_status() == PHP_SESSION_NONE);
    }

    /**
     * @return string
     */
    protected function getSID()
    {
        return (isset($this->sid) ? $this->sid : "default");
    }

    /**
     * @param $baseDir
     * @param $initDir
     * @param $filename
     * @param $operation
     * @param bool|false $data
     * @return bool
     */
    private function sessionOperation($baseDir, $initDir, $filename, $operation, $data = false){
        $baseDir = $baseDir . '/';
        $initDir = '/'. $initDir . '/';
        $filename = '/'. $filename;
        $path = explode('/', str_replace('//', '/', $baseDir . $initDir . $filename));
        trimArr($path);

        if (!is_array($path))
            $path[] = $path;

        $path = array_merge(['cache', $this->getSID() ], $path);

        $link = &$_SESSION;

        foreach($path as $index => $item){
            $lastItem = $item;
            if (!$path[$index + 1]) break;
            $link = &$link[$item];
        }

        if ($operation == 'add'){
            $link[$lastItem] = $data;
            return $link[$lastItem];
        }

        if ($operation == 'read')
            return $link[$lastItem];

        if ($operation == 'clean'){
            unset($link[$lastItem]);
            return true;
        }

        return false;

    }
}

CacheEngineShmop.php

<?php

use \Bitrix\Main\Data\ICacheEngine;
use \Bitrix\Main\Data\ICacheEngineStat;
use \Bitrix\Main\Config\Configuration;
/**
 * Caching based on Unix Share Memory
 *
 * # ipcs -m
 * List all segments used
 *
 * # ipcs -lm
 * ------ Shared Memory Limits --------
 * max number of segments = 4096       <--- this is SHMMNI
 * max seg size (kbytes) = 67108864    <--- this is SHMMAX
 * max total shared memory (kbytes) = 17179869184<- this is SHMALL
 * min seg size (bytes) = 1
 *
 *
 */

class CacheEngineShmop implements ICacheEngine, ICacheEngineStat
{
    /**
     * @var string
     */
    private $sid = "";
    /**
     * @var integer
     */
    private $maxSize;
    /**
     * @var integer
     */
    private $permissions;

    /**
     * Переменные для реализации методов из интерфейса ICacheEngineStat
     * @var bool
     */
    private $written = false;
    /**
     * @var bool
     */
    private $read = false;
    /**
     * CacheEngineShmop constructor.
     */
    public function __construct()
    {
        $v = Configuration::getValue("cache");
        if ($v != null && isset($v["sid"]) && ($v["sid"] != ""))
            $this->sid = $v["sid"];
        else
            $this->sid = "BX";

        $this->maxSize = (isset($v['max-size'])) ? $v['max-size'] : 524288;
        $this->permissions = (isset($v['default-permission'])) ? $v['default-permission'] : '0700';
    }

    /**
     * @return integer
     */
    public function getReadBytes()
    {
        return $this->read;
    }
    /**
     * @return integer
     */
    public function getWrittenBytes()
    {
        return $this->written;
    }
    /**
     * @return string
     */
    public function getCachePath()
    {
        return '';
    }

    /**
     * @return bool
     */
    public function isAvailable()
    {
        return true;
    }

    /**
     * @param $baseDir
     * @param bool|false $initDir
     * @param bool|false $filename
     * @return null
     */
    public function clean($baseDir, $initDir = false, $filename = false)
    {
        $filekey = $this->getKeyId($baseDir, $initDir, $filename);
        $shm_id = shmop_open($filekey, "w", 0, 0);
        $file = $this->getFTok($baseDir, $initDir, $filename);
        if (file_exists($file)) {
            unlink($file);
        }
        if (!$shm_id) {
            return null;
        }
        shmop_delete($shm_id);
        shmop_close($shm_id);
    }

    /**
     * @param $arAllVars
     * @param $baseDir
     * @param $initDir
     * @param $filename
     * @param $TTL
     * @return bool
     */
    public function read(&$arAllVars, $baseDir, $initDir, $filename, $TTL)
    {
        $fileKey = $this->getKeyId($baseDir, $initDir, $filename);

        $shm_id = @shmop_open($fileKey, "a", 0, 0);
        if (!$shm_id) {
            return false;
        }
        $fileAge = filemtime($this->getFTok($baseDir, $initDir, $filename));

        if (($TTL > 0) && (intval(time() - $fileAge) > $TTL)) {
            shmop_close($shm_id);
            $shm_id = shmop_open($fileKey, "w", $this->getDefaultPermission(), $this->getMaxSize());
            shmop_delete($shm_id);
            shmop_close($shm_id);
            return false;
        }

        $serialized = shmop_read($shm_id, 0, shmop_size($shm_id));
        shmop_close($shm_id);
        $this->read = strlen($serialized);
        $arAllVars = unserialize($serialized);
    }

    /**
     * @param $arAllVars
     * @param $baseDir
     * @param $initDir
     * @param $filename
     * @param $TTL
     * @throws Exception
     */
    public function write($arAllVars, $baseDir, $initDir, $filename, $TTL)
    {
        $this->release($baseDir, $initDir, $filename);
        $serialized = serialize($arAllVars);
        $this->written = strlen($serialized);
        if ($this->written > $this->getMaxSize()) {
            throw new \Exception('Object is greater than the max size allowed: ' . $this->getMaxSize());
        }
        $shmKey = $this->getKeyId($baseDir, $initDir, $filename);
        $shm_id = shmop_open($shmKey, "c", 0777, $this->written);
        if (!$shm_id) {
            $message = "Couldn't create shared memory segment";
            $lastError = error_get_last();
            if (isset($lastError['message'])) {
                $message = $lastError['message'];
            }
            throw new \Exception($message);
        }
        $shm_bytes_written = shmop_write($shm_id, $serialized, 0);

        if ($shm_bytes_written != $this->written) {
            throw new \Exception("Couldn't write the entire length of data");
        }

        shmop_close($shm_id);
    }

    /**
     * @param $path
     * @return bool
     */
    public function isCacheExpired($path)
    {
        return false;
    }

    /**
     * @param $baseDir
     * @param $initDir
     * @param $filename
     * @return null|void
     */
    protected function release($baseDir, $initDir, $filename)
    {
        if ($this->read($vars, $baseDir, $initDir, $filename, 0) === false) {
            return;
        }
        $filekey = $this->getKeyId($baseDir, $initDir, $filename);
        $shm_id = shmop_open($filekey, "w", 0, 0);
        $file = $this->getFTok($baseDir, $initDir, $filename);
        if (file_exists($file)) {
            unlink($file);
        }
        if (!$shm_id) {
            return null;
        }
        shmop_delete($shm_id);
        shmop_close($shm_id);
    }

    /**
     * @param $baseDir
     * @param $initDir
     * @param $filename
     * @return int
     */
    protected function getKeyId($baseDir, $initDir, $filename)
    {
        $file = $this->getFTok($baseDir, $initDir, $filename);
        if (!file_exists($file)) {
            touch($file);
        }
        return ftok($file, 'j');
    }

    /**
     * @param $baseDir
     * @param $initDir
     * @param $filename
     * @return string
     */
    protected function getFTok($baseDir, $initDir, $filename)
    {
        $baseDir = $baseDir . '/';
        $initDir = '/' . $initDir . '/';
        $path = str_replace('//', '/', $baseDir . $initDir . $filename);
        return sys_get_temp_dir() . '/' . md5($path);
    }

    /**
     * @return int
     */
    protected function getMaxSize()
    {
        return $this->maxSize;
    }

    /**
     * @return int|string
     */
    protected function getDefaultPermission()
    {
        return $this->permissions;
    }
}

ICacheEngine.php

<?php
namespace Bitrix\Main\Data;

interface ICacheEngine
{
	/**
	* Возвращает true, если можно произвести чтение из кэша или запись в кэш.
	*/
	public function isAvailable(); 
	
	/**
	* Метод очистки кэша
	*
	* $baseDir Базовая директория кэша (обычно /bitrix/cache).
	* $initDir Директория в базовой (подкаталог).
	* $filename Имя файла.
	*/
	public function clean($baseDir, $initDir = false, $filename = false);
	
	/**
	 * Метод чтения из кэша. Возвращает true, если чтение произошло успешно
	 *
	 * &$arAllVars Результат кэша.
	 * $baseDir Базовая директория кэша (обычно /bitrix/cache).
	 * $initDir Директория в базовой (подкаталог).
	 * $filename Имя файла.
	 * $TTL Время жизни кэша в секундах
	 *
	 */
	public function read(&$arAllVars, $baseDir, $initDir, $filename, $TTL);
	
	/**
	 * Метод записи в кэш.
	 *
	 * &$arAllVars Результат кэша.
	 * $baseDir Базовая директория кэша (обычно /bitrix/cache).
	 * $initDir Директория в базовой (подкаталог).
	 * $filename Имя файла.
	 * $TTL Время жизни кэша в секундах
	 *
	 */
	public function write($arAllVars, $baseDir, $initDir, $filename, $TTL);
	
	/**
	 * Возвращает true, если кэш валидный, иначе возвращает false
	 *
	 * $path Абсолютный путь
	 *
	 */
	public function isCacheExpired($path);
}

ICacheEngineStat.php

<?php
namespace Bitrix\Main\Data;

interface ICacheEngineStat
{
	/**
	 * Возвращает число байт прочитанных с диска или false, если не было операции чтения.
	 *
	 */
	public function getReadBytes();
	
	/**
	 * Возвращает число байт записанных на диск или false, если не было операции записи.
	 *
	 */
	public function getWrittenBytes();
	
	/**
	 * Возвращение физического пути к файлу после операции чтения/записи.
	 *
	 */
	public function getCachePath();
}
Clone this wiki locally