Cache - uniqcle/Bitrix GitHub Wiki

API Caching

Классы API

CPHPCache - основной класс для кеширования PHP и HTML

CPageCache - класс для кеширования HTML

CBitrixComponent - класс компонента, содержащий оптимизированные методы кеширования (StartResultCache)

CCacheManager - класс управления кешем

Cache Type
                     CACHE_TYPE   CACHE_TIME
Неуправляемое кэш-е      Y            V    // По времени
Кеш-е комп-в (автокеш-е) А            V    // По времени + настройки сайта
Управляемое кеш-е        А            V    // Если были изменения в БД
Кеш-е меню
  

CPHPCache

Для избавления от лишней нагрузки, запросы записываем в кэш с помощью класса CPHPCache

Methods

InitCache - инициализация кеширования. Возвращает false если нет актуального файла кеша.

StartDataCache - старт буферизации для последующего сохранения в файл, вывод кеша

EndDataCache - завершение буферизации, сохранение в файл кеша. Принимает массив параметров для сохранения в кеше.

GetVars - возвращает php-переменные, сохраненные в кеше

Output - вывод html содержимого файла кеша, если тот существует

IsCacheExpired Проверяет истек ли период жизни кэша.

CleanDir Метод очищает кеш по параметру basedir.

AbortDataCache Отменяет создание текущего кэша.

$obCache = new CPHPCache();                    //Объект
$cacheLifetime = $arParams['CACHE_TIME'];     //Время кеширования
$cacheID = $arParams['IBLOCK_ID'] . $arParams['ELEMENT_ID']; // Уник. ID кэша
$cachePath = "/cache_test/";                  // Директория кеша

if($obCache->InitCache( $cacheLifetime, $cacheID, $cachePath )){  // Проверяем наличие актуального кеша
  
    $arVars = $obCache->GetVars();        // Выводим данные из кеша в случае его наличия
    $arResult = $arVars['arResult'];
    $templateCachedData = $arVars['templateCachedData'];

    $this->SetTemplateCachedData( $templateCachedData ); // Передадим параметры шаблона (скрипты, стили)
    
    $obCache->Output();                      // Хранимый html выведем через Output
} elseif($obCache->StartDataCache()){
    //Делаем выборку из базы и сохраняем результат выборки в кеш
    
    $this->IncludeComponentTemplate();
    
    //Получим закешированные параметры шаблона (скрипты, стили) из шаблона
    $templateData = $this->GetTemplateCachedData();

    $obCache->EndDataCache(
        array(
            "arResult" => $arResult,
            "templateCachedData" => $templateCachedData
        )
    );
}

При таком подходе, чтобы изминения приняли силу, необходимо либо сбросить кеш, либо пока не истечет время активного кеша.

Компонент с кешированием CPHPCache

description.php

if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

$arComponentDescription = array(
    'NAME' => 'CPHPCache',
    'DESCRIPTION' => 'CPHPCache',
    'ICON' => '',
    'CACHE_PATH' => 'Y',
    'SORT' => 10,
    'PATH' => array(
        'ID' => 'other',
        'NAME' => 'Решения UNIQCLE'
    )
);

.parameters.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

use Bitrix\Main\Loader;

if (!Loader::includeModule('iblock'))
    return;

$arTypes = CIBlockParameters::GetIBlockTypes(['-' => ' ']);

$arIBlocks = [];
if (
    isset($arCurrentValues['IBLOCK_TYPE'])
    && trim($arCurrentValues['IBLOCK_TYPE'])
    && trim($arCurrentValues['IBLOCK_TYPE']) != '-'
) {
    $rsIBlocks = CIBlock::GetList(
        ['SORT' => 'ASC'],
        ['SITE_ID' => $_REQUEST['site'], 'TYPE' => trim($arCurrentValues['IBLOCK_TYPE'])]
    );
    while ($arIBlock = $rsIBlocks->Fetch()) {
        $arIBlocks[$arIBlock['ID']] = '[' . $arIBlock['ID'] . '] ' . $arIBlock['NAME'];
    }
}

$arSections = [];
if (
    isset($arCurrentValues['IBLOCK_ID'])
    && intval($arCurrentValues['IBLOCK_ID']) > 0
) {
    $rsSections = CIBlockSection::GetList(
        [],
        ['IBLOCK_ID' => intval($arCurrentValues['IBLOCK_ID'])]
    );
    while ($arSection = $rsSections->Fetch()) {
        $arSections[$arSection['ID']] = '[' . $arSection['ID'] . '] ' . $arSection['NAME'];
    }
}


$arComponentParameters = [
    'GROUPS' => [
    ],
    'PARAMETERS' => [
        'CACHE_TIME' => [
            'DEFAULT' => '36001'
        ],
        'IBLOCK_TYPE' => [
            'PARENT' => 'BASE',
            'NAME' => 'Тип инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arTypes,
            'DEFAULT' => 'offers',
            'REFRESH' => 'Y'
        ],
        'IBLOCK_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'Инфоблок',
            'TYPE' => 'LIST',
            'VALUES' => $arIBlocks,
            'DEFAULT' => '',
            'ADDITIONAL_VALUES' => 'Y',
            'REFRESH' => 'Y'
        ],
        'SECTION_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'Раздел инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arSections,
            'DEFAULT' => '',
            'ADDITIONAL_VALUES' => 'Y',
            'REFRESH' => 'Y'
        ],
        'ELEMENT_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'ID Статьи',
            'TYPE' => 'STRING',
            'DEFAULT' => ''
        ],
        'COUNT' => [
            'PARENT' => 'BASE',
            'NAME' => 'Кол-во элементов',
            'TYPE' => 'STRING',
            'DEFAULT' => '1'
        ],
    ]
];

component.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

//Проверяем входящие параметры в компоненте
$arParams['IBLOCK_ID'] = intval($arParams['IBLOCK_ID']);
if($arParams['IBLOCK_ID'] == 0)
    return;

$arParams['ELEMENT_ID'] = intval($arParams['ELEMENT_ID']);
if($arParams['ELEMENT_ID'] == 0)
    return;
//Подключаем модуль инфоблоков
CModule::IncludeModule('iblock');

//Объект
$obCache = new CPHPCache();
//Время кеширования
$cacheLifetime = $arParams['CACHE_TIME'];
// Индентификатор кэша, уникальный
$cacheID = $arParams['IBLOCK_ID'] . $arParams['ELEMENT_ID'];
// Директория кеша
$cachePath = "/cache_test/";
// Проверяем наличие актуального кеша
if($obCache->InitCache( $cacheLifetime, $cacheID, $cachePath )){
    // Выводим данные из кеша в случае его наличия
    $arVars = $obCache->GetVars();
    $arResult = $arVars['arResult'];
    $templateCachedData = $arVars['templateCachedData'];

    // Передадим параметры шаблона (скрипты, стили) в кеш
    $this->SetTemplateCachedData( $templateCachedData );
    // Хранимый html выведем через Output
    $obCache->Output();


} elseif($obCache->StartDataCache()){

    // Код, кот. должен быть закеширован
    $rsDB = CIBlockElement::GetList(
        ['ID' => 'ASC'],                                               // Порядок
        ['ACTIVE' => 'Y', 'IBLOCK_ID' => $arParams['IBLOCK_ID'], 'ID' => $arParams['ELEMENT_ID'] ], // Фильтрация
        false,
        false,                       // Кол-во на странице
        ['IBLOCK_ID', 'ID', 'NAME', 'PREVIEW_TEXT', 'CODE']            // Выборка
    );


    if($arElem = $rsDB -> GetNext() ){
        $arResult = $arElem;
    }

    $this->IncludeComponentTemplate();
    //Получим закешированные параметры шаблона (скрипты, стили) из шаблона
    $templateCachedData = $this->GetTemplateCachedData();

    $obCache->EndDataCache(
        array(
            "arResult" => $arResult,
            "templateCachedData" => $templateCachedData
        )
    );
}

template.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();?>

<div class = "test">
    <b><?=$arResult['NAME'];?></b>
    <p><?=$arResult['PREVIEW_TEXT'];?></p>
</div>

style.css

.test{
    color: #0a7ddd;
}
Компонент с кешированием CPHPCache D7

.description.php

<?
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

$arComponentDescription = array(
    'NAME' => 'CPHPCache D7',
    'DESCRIPTION' => 'CPHPCache D7',
    'ICON' => '',
    'CACHE_PATH' => 'Y',
    'SORT' => 10,
    'PATH' => array(
        'ID' => 'other',
        'NAME' => 'Решения UNIQCLE'
    )
);

.parameters.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

use Bitrix\Main\Loader;

if (!Loader::includeModule('iblock'))
    return;

$arTypes = CIBlockParameters::GetIBlockTypes(['-' => ' ']);

$arIBlocks = [];
if (
    isset($arCurrentValues['IBLOCK_TYPE'])
    && trim($arCurrentValues['IBLOCK_TYPE'])
    && trim($arCurrentValues['IBLOCK_TYPE']) != '-'
) {
    $rsIBlocks = CIBlock::GetList(
        ['SORT' => 'ASC'],
        ['SITE_ID' => $_REQUEST['site'], 'TYPE' => trim($arCurrentValues['IBLOCK_TYPE'])]
    );
    while ($arIBlock = $rsIBlocks->Fetch()) {
        $arIBlocks[$arIBlock['ID']] = '[' . $arIBlock['ID'] . '] ' . $arIBlock['NAME'];
    }
}

$arSections = [];
if (
    isset($arCurrentValues['IBLOCK_ID'])
    && intval($arCurrentValues['IBLOCK_ID']) > 0
) {
    $rsSections = CIBlockSection::GetList(
        [],
        ['IBLOCK_ID' => intval($arCurrentValues['IBLOCK_ID'])]
    );
    while ($arSection = $rsSections->Fetch()) {
        $arSections[$arSection['ID']] = '[' . $arSection['ID'] . '] ' . $arSection['NAME'];
    }
}


$arComponentParameters = [
    'GROUPS' => [
    ],
    'PARAMETERS' => [
        'CACHE_TIME' => [
            'DEFAULT' => '36001'
        ],
        'IBLOCK_TYPE' => [
            'PARENT' => 'BASE',
            'NAME' => 'Тип инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arTypes,
            'DEFAULT' => 'offers',
            'REFRESH' => 'Y'
        ],
        'IBLOCK_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'Инфоблок',
            'TYPE' => 'LIST',
            'VALUES' => $arIBlocks,
            'DEFAULT' => '',
            'ADDITIONAL_VALUES' => 'Y',
            'REFRESH' => 'Y'
        ],
        'SECTION_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'Раздел инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arSections,
            'DEFAULT' => '',
            'ADDITIONAL_VALUES' => 'Y',
            'REFRESH' => 'Y'
        ],
        'ELEMENT_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'ID Статьи',
            'TYPE' => 'STRING',
            'DEFAULT' => ''
        ],
        'COUNT' => [
            'PARENT' => 'BASE',
            'NAME' => 'Кол-во элементов',
            'TYPE' => 'STRING',
            'DEFAULT' => '1'
        ],
    ]
];

class.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

use Bitrix\Main\Loader,
    Bitrix\Iblock,
    \Bitrix\Main\Data\Cache,
    \Bitrix\Iblock\Component\Tools;

class FirstComponent extends CBitrixComponent {

    // Обработка параметров
    function onPrepareComponentParams($params){

        if ($params['CACHE_TYPE'] == 'Y' || $params['CACHE_TYPE'] == 'A') {
            $params['CACHE_TIME'] = intval($params['CACHE_TIME']);
        } else {
            $params['CACHE_TIME'] = 0;
        }

        #проверка входных параметров
        $params['IBLOCK_ID'] = isset($params['IBLOCK_ID']) && intval($params['IBLOCK_ID']) > 0 ? intval($params['IBLOCK_ID']) : 0;
        return $params;
    }

    public function executeComponent(){
        try {

            $cache = Cache::createInstance();

            $cacheLifeTime = $this->arParams['CACHE_TIME'];
            $cacheID = $this->arParams['IBLOCK_ID'].$this->arParams['ELEMENT_ID'];
            $cachePath = "/cache_test1/";

            if ($cache->initCache( $cacheLifeTime , $cacheID, $cachePath )) { // проверяем кеш и задаём настройки

                $arVars = $cache->getVars(); // достаем переменные из кеша
                $this->arResult = $arVars['arResult'];

                // $this->templateCachedData = $vars['templateCachedData'];
                // $this->SetTemplateCachedData($this->templateCachedData);

                $cache->Output();

            } elseif ($cache->startDataCache()) {

                $this->checkModules();
                $this->prepareData();
                $this->doAction();

                $this->includeComponentTemplate();

                //$this->templateCachedData = $this->GetTemplateCachedData();

                $cache->endDataCache(
                    [
                        "arResult" => $this->arResult,
                        //"templateCachedData" => $this->templateCachedData
                        ]
                );
            }

        } catch (Exception $e) {         //Если произошла к-л ошибка, выводим ошибку
            $cache->abortDataCache();
            $this->arResult['ERROR'] = $e->getMessage();
        }
    }

    protected function checkModules()
    {
        #подключаем нужные модули
        if (!Loader::includeModule('iblock'))
            throw new Exception('Модуль "Инфоблоки" не установлен');
    }

    protected function prepareData()
    {
        #проверки на существования
        $this->arResult['IBLOCK'] = [];
        if ($this->arParams['IBLOCK_ID']) {
            $this->arResult['IBLOCK'] = CIBlock::GetByID($this->arParams['IBLOCK_ID'])->Fetch();
        }
        if (!$this->arResult['IBLOCK']) {
            throw new Exception('Инфоблок не найден');
        }
    }

    protected function doAction()
    {
        // Код, кот. должен быть закеширован
        $rsDB = CIBlockElement::GetList(
            ['ID' => 'ASC'],                                               // Порядок
            ['ACTIVE' => 'Y', 'IBLOCK_ID' => $this->arParams['IBLOCK_ID'], 'ID' => $this->arParams['ELEMENT_ID'] ], // Фильтрация
            false,
            false,                       // Кол-во на странице
            ['IBLOCK_ID', 'ID', 'NAME', 'PREVIEW_TEXT', 'CODE']            // Выборка
        );


        if($arElem = $rsDB -> GetNext() ){
            $this->arResult = $arElem;
        }
    }
}

template.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die(); ?>

<div class = "test">
    <h2><?=$arResult['NAME'];?></h2>
    <p><?=$arResult['PREVIEW_TEXT'];?></p>
</div>

Attantion! Стили лучше хранить в общем шаблоне, поскольку при кешировании они не подгружаются из компонента.

Documentation

CCacheManager (Tagged Cache)

CCacheManager - класс управления кешем.

global $CACHE_MANAGER;       //Глоб. объект класса для обращения к методам 

//Тегирования в рамках StartTagCache и EndTagCache
$CACHE_MANAGER->StartTagCache("/cache_tag/");
...
$CACHE_MANAGER->RegisterTag("my_tag"); // Регистрация тега
...
$CACHE_MANAGER->EndTagCache();
Вложенный выдов методов StartTagCache и EndTagCache
$CACHE_MANAGER->StartTagCache($cache_dir1);
   $CACHE_MANAGER->StartTagCache($cache_dir2);
 	$CACHE_MANAGER->StartTagCache($cache_dir2);
 
        $CACHE_MANAGER->EndTagCache();
   $CACHE_MANAGER->EndTagCache();
$CACHE_MANAGER->EndTagCache();
Итоговая структура тегирования кеша
$global $CACHE_MANAGER; 
$CACHE_MANAGER->StartTagCache($cache_dir);
  while($arElement=$rsElements->Fetch()){
     $CACHE_MANAGER->RegisterTag("iblock_id_".$arElement["ID"]);
     $arElements[]  = $arElement;
  }
  $CACHE_MANAGER->RegisterTag("iblock_id_new");
$CACHE_MANAGER->EndTagCache();
Компонент с тегированным кешем

Очистка кэша по тегу, привязанного к ID инфоблока, на событии изменения эл-та

.parameters.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

use Bitrix\Main\Loader;

if (!Loader::includeModule('iblock'))
    return;

$arTypes = CIBlockParameters::GetIBlockTypes(['-' => ' ']);

$arIBlocks = [];
if (
    isset($arCurrentValues['IBLOCK_TYPE'])
    && trim($arCurrentValues['IBLOCK_TYPE'])
    && trim($arCurrentValues['IBLOCK_TYPE']) != '-'
) {
    $rsIBlocks = CIBlock::GetList(
        ['SORT' => 'ASC'],
        ['SITE_ID' => $_REQUEST['site'], 'TYPE' => trim($arCurrentValues['IBLOCK_TYPE'])]
    );
    while ($arIBlock = $rsIBlocks->Fetch()) {
        $arIBlocks[$arIBlock['ID']] = '[' . $arIBlock['ID'] . '] ' . $arIBlock['NAME'];
    }
}

$arSections = [];
if (
    isset($arCurrentValues['IBLOCK_ID'])
    && intval($arCurrentValues['IBLOCK_ID']) > 0
) {
    $rsSections = CIBlockSection::GetList(
        [],
        ['IBLOCK_ID' => intval($arCurrentValues['IBLOCK_ID'])]
    );
    while ($arSection = $rsSections->Fetch()) {
        $arSections[$arSection['ID']] = '[' . $arSection['ID'] . '] ' . $arSection['NAME'];
    }
}


$arComponentParameters = [
    'GROUPS' => [
    ],
    'PARAMETERS' => [
        'CACHE_TIME' => [
            'DEFAULT' => '36001'
        ],
        'IBLOCK_TYPE' => [
            'PARENT' => 'BASE',
            'NAME' => 'Тип инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arTypes,
            'DEFAULT' => 'offers',
            'REFRESH' => 'Y'
        ],
        'IBLOCK_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'Инфоблок',
            'TYPE' => 'LIST',
            'VALUES' => $arIBlocks,
            'DEFAULT' => '',
            'ADDITIONAL_VALUES' => 'Y',
            'REFRESH' => 'Y'
        ],
        'SECTION_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'Раздел инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arSections,
            'DEFAULT' => '',
            'ADDITIONAL_VALUES' => 'Y',
            'REFRESH' => 'Y'
        ],
        'ELEMENT_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'ID Статьи',
            'TYPE' => 'STRING',
            'DEFAULT' => ''
        ],
        'COUNT' => [
            'PARENT' => 'BASE',
            'NAME' => 'Кол-во элементов',
            'TYPE' => 'STRING',
            'DEFAULT' => '1'
        ],
    ]
];

component.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

//Проверяем входящие параметры в компоненте
$arParams['IBLOCK_ID'] = intval($arParams['IBLOCK_ID']);
if($arParams['IBLOCK_ID'] == 0)
    return;

$arParams['ELEMENT_ID'] = intval($arParams['ELEMENT_ID']);
if($arParams['ELEMENT_ID'] == 0)
    return;

CModule::IncludeModule('iblock');

$obCache = new CPHPCache();

$cacheLifetime = $arParams['CACHE_TIME'];

$cacheID = $arParams['IBLOCK_ID'] . $arParams['ELEMENT_ID'] . $USER->GetUserGroupString(); // Генерируем уник ID кэша, + ID группы пользователя

$cachePath = "/cache_test/";

if($obCache->InitCache( $cacheLifetime, $cacheID, $cachePath )){ // Проверяем наличие актуального кеша

    $arVars = $obCache->GetVars();                         // Выводим данные из кеша в случае его наличия
    $arResult = $arVars['arResult'];

    // $templateCachedData = $arVars['templateCachedData'];
    // $this->SetTemplateCachedData( $templateCachedData );   // Передадим параметры шаблона (скрипты, стили) в кеш

    $obCache->Output();                                   // Хранимый html выведем через Output

} elseif($obCache->StartDataCache()){

    $rsDB = CIBlockElement::GetList(                      // Код, кот. должен быть закеширован
        ['ID' => 'ASC'],
        ['ACTIVE' => 'Y', 'IBLOCK_ID' => $arParams['IBLOCK_ID'], 'ID' => $arParams['ELEMENT_ID'] ],
        false,
        false,
        ['IBLOCK_ID', 'ID', 'NAME', 'PREVIEW_TEXT', 'CODE']
    );

    global $CACHE_MANAGER;             // Установка Тегированного Кэша
    $CACHE_MANAGER->StartTagCache($cachePath);

    if($arElem = $rsDB -> GetNext() ){
        $CACHE_MANAGER->RegisterTag('cache_test_iblock_id_'.$arElem['IBLOCK_ID']); // Регистрация тега
        $arResult = $arElem;
    }

    $CACHE_MANAGER->RegisterTag('cache_test_iblock_id'); // Рег-ия общего кэша

    $CACHE_MANAGER->EndTagCache();     ///////////////

    $this->IncludeComponentTemplate();

    //$templateCachedData = $this->GetTemplateCachedData();     //Получим закешированные параметры шаблона (скрипты, стили) из шаблона

    $obCache->EndDataCache(
        array(
            "arResult" => $arResult,
           // "templateCachedData" => $templateCachedData
        )
    );
}

template.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();?>

<div class = "test">
    <b><?=$arResult['NAME'];?></b>
    <p><?=$arResult['PREVIEW_TEXT'];?></p>
</div>

init.php

// Обработчик при обновлении элемента
AddEventHandler("iblock", "OnAfterIBlockElementUpdate", array("CIBlockHandler", "OnAfterIBlockElementUpdateHandler"));

class CIBlockHandler{
    function OnAfterIBlockElementUpdateHandler(&$arFields){
        // Проверка на наличие глобально. объекта CACHE_MANAGER
        if(is_object($GLOBALS['CACHE_MANAGER']) && $arFields['IBLOCK_ID'] == CONST_IBLOCK_NEWS){
            // Очищаем все кэши, обладающие тегом cache_test_iblock_id_*
            $GLOBALS['CACHE_MANAGER']->ClearByTag('cache_test_iblock_id_'.$arFields['IBLOCK_ID']);
        }
    }
}

CPageCache

Кеширование HTML результата выполнения скрипта.

Компонент с HTML-кешем

.description.php

if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

$arComponentDescription = array(
    'NAME' => 'CPageCache',
    'DESCRIPTION' => 'CPageCache',
    'ICON' => '',
    'CACHE_PATH' => 'Y',
    'SORT' => 10,
    'PATH' => array(
        'ID' => 'other',
        'NAME' => 'Решения UNIQCLE'
    )
);

.parameters.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

use Bitrix\Main\Loader;

if (!Loader::includeModule('iblock'))
    return;

$arTypes = CIBlockParameters::GetIBlockTypes(['-' => ' ']);

$arIBlocks = [];
if (
    isset($arCurrentValues['IBLOCK_TYPE'])
    && trim($arCurrentValues['IBLOCK_TYPE'])
    && trim($arCurrentValues['IBLOCK_TYPE']) != '-'
) {
    $rsIBlocks = CIBlock::GetList(
        ['SORT' => 'ASC'],
        ['SITE_ID' => $_REQUEST['site'], 'TYPE' => trim($arCurrentValues['IBLOCK_TYPE'])]
    );
    while ($arIBlock = $rsIBlocks->Fetch()) {
        $arIBlocks[$arIBlock['ID']] = '[' . $arIBlock['ID'] . '] ' . $arIBlock['NAME'];
    }
}

$arSections = [];
if (
    isset($arCurrentValues['IBLOCK_ID'])
    && intval($arCurrentValues['IBLOCK_ID']) > 0
) {
    $rsSections = CIBlockSection::GetList(
        [],
        ['IBLOCK_ID' => intval($arCurrentValues['IBLOCK_ID'])]
    );
    while ($arSection = $rsSections->Fetch()) {
        $arSections[$arSection['ID']] = '[' . $arSection['ID'] . '] ' . $arSection['NAME'];
    }
}


$arComponentParameters = [
    'GROUPS' => [
    ],
    'PARAMETERS' => [
        'CACHE_TIME' => [
            'DEFAULT' => '36001'
        ],
        'IBLOCK_TYPE' => [
            'PARENT' => 'BASE',
            'NAME' => 'Тип инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arTypes,
            'DEFAULT' => 'offers',
            'REFRESH' => 'Y'
        ],
        'IBLOCK_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'Инфоблок',
            'TYPE' => 'LIST',
            'VALUES' => $arIBlocks,
            'DEFAULT' => '',
            'ADDITIONAL_VALUES' => 'Y',
            'REFRESH' => 'Y'
        ],
        'SECTION_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'Раздел инфоблока',
            'TYPE' => 'LIST',
            'VALUES' => $arSections,
            'DEFAULT' => '',
            'ADDITIONAL_VALUES' => 'Y',
            'REFRESH' => 'Y'
        ],
        'ELEMENT_ID' => [
            'PARENT' => 'BASE',
            'NAME' => 'ID Статьи',
            'TYPE' => 'STRING',
            'DEFAULT' => ''
        ],
        'COUNT' => [
            'PARENT' => 'BASE',
            'NAME' => 'Кол-во элементов',
            'TYPE' => 'STRING',
            'DEFAULT' => '1'
        ],
    ]
];

component.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

$obCache = new CPageCache();
$cacheTime = $arParams['CACHE_TIME'];
$cacheID = 'weather_parse';
$cacheDir = '/weather_cache/';

if($obCache->StartDataCache($cacheTime, $cacheID, $cacheDir)){

    $cityID = "23642"; //Москва
    $urlRoot = "https://export.yandex.ru/bar/reginfo.xml?region={$cityID}.xml";

    $data = simplexml_load_file($urlRoot);

    $city= $data->region->title;
    $temp = $data->weather->day->tomorrow->temperature;
    $url = $data->weather->url;
    ?>

    Город: <a href = "<?=$url;?>"><?=$city;?></a> </br>
    Температура: <b><?=$temp;?></b>

<?php
    $obCache->EndDataCache();
}
?>
Гаджет с кешированием
<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
?>
<?
// pre($arGadgetParams);
#
# INPUT PARAMS
#
$arGadgetParams["IBLOCK_ID"] = intval($arGadgetParams["IBLOCK_ID"]);
if ($arGadgetParams["IBLOCK_ID"] <= 0)
    return false;

$arGadgetParams["ELEMENT_COUNT"] = intval($arGadgetParams["ELEMENT_COUNT"]);
if ($arGadgetParams["ELEMENT_COUNT"] <= 0)
    $arGadgetParams["ELEMENT_COUNT"] = 5;

$arGadgetParams["SHOW_UNACTIVE_ELEMENTS"] = $arGadgetParams["SHOW_UNACTIVE_ELEMENTS"]!="N";

$arNavParams = array(
    "nPageSize" => $arGadgetParams["ELEMENT_COUNT"],
);

#
# CACHE
#
$obCache = new CPageCache;
$cacheTime = 5*60;
$cacheId = $arGadgetParams["IBLOCK_ID"].$arGadgetParams["ELEMENT_COUNT"].$arGadgetParams["ELEMENT_COUNT"];

if($obCache->StartDataCache($cacheTime, $cacheId, "/")):
    if(!CModule::IncludeModule("iblock"))
    {
        ShowError(GetMessage("IBLOCK_MODULE_NOT_INSTALLED"));
        return;
    }

    $arSelect = array(
        "ID",
        "ACTIVE",
        "DATE_CREATE",
        "IBLOCK_ID",
        "DETAIL_PAGE_URL",
        "NAME",
        "PREVIEW_PICTURE",
    );
    $arFilter = array (
        "IBLOCK_ID" => $arGadgetParams["IBLOCK_ID"],
        "CHECK_PERMISSIONS" => "Y",
    );
    if(!$arGadgetParams["SHOW_UNACTIVE_ELEMENTS"])
        $arFilter["ACTIVE"] = "Y";

    $arSort = array("DATE_CREATE"=>"DESC");

    $rsElement = CIBlockElement::GetList($arSort, $arFilter, false, $arNavParams, $arSelect);
    while($arElement = $rsElement->GetNext()):
        ?>
        <div style="margin-bottom: 10px;">
            <div style="float: left; width: 50px; margin-right: 10px">
                <a href="<?=$arElement['DETAIL_PAGE_URL']?>"><?=CFile::ShowImage($arElement['PREVIEW_PICTURE'],50,50)?></a>
            </div>
            <a href="<?=$arElement['DETAIL_PAGE_URL']?>"><?=$arElement['NAME']?></a><br/>
            <small><?=$arElement['DATE_CREATE']?></small>
            <div style="clear: both;"></div>
        </div>
    <?
    endwhile;
    $obCache->EndDataCache();
endif;
?>

CBitrixComponent::StartResultCache

Оптимизированный механизм кеширования. Содержит механизм тегирования и встроенный метод сохранения параметров шаблона GetTemplateCachedData. **При написании своих компонентов лучше использовать его. **

Methods

StartResultCache вывод содержимого кеша или инициализация кеширования. Параметр false - передача по-умолч. $arParams['CACHE_TIME'].

Если есть актуальный кеш - возвращает false. Выводит его на экран и заполняет arResult данными из кеша.

Если нет актуального кеша - возвращает true. Инициализирует процесс кеширования, который завершается подключением шаблона.

AbortResultCache отмена кеширования. ClearResultCache очистка кеша.

По-умолч. кэш находится bitrix/cache/site_id/namespace/component/

//Код, который должен быть закеширован, обернуть в блок
if ($this->StartResultCache()){
	//Кешируемый код
}

По умолчанию кеш зависит от набора входящих параметров. Т.е. если у компонента будет один параметр с каким-то значением, то именно для этого набора параметров и будет сгенерирован кеш. При изменении этого параметра будет сформирован другой кеш для компонента. В качестве времени для кеша будет браться по умолчанию значение параметра CACHE_TIME компонента. При желании его можно изменить, передав первый параметр методу StartResultCache( $cacheTime ).

Помимо параметров, можно еще и добавить зависимостей к идентификатору кеша с помощью второго параметра функции.

// сделать кеш зависимым от группы пользователя
global $USER; 
if ($this->StartResultCache( false, $USER->GetUserGroupString() )){
	//Кешируемый код
}

Cache & Template

Автоматическое кеширование по умолчанию сохраняет только html вывод, который обрамлен блоком с вызовом метода StartResultCache(). Соответственно, подключение шаблона также должно находиться внутри этого блока.

// Для того, чтобы включить шаблон в кеш, нужно подключение шаблона загнать  внутрь нашего блока
global $USER; 
if ($this->StartResultCache( false, $USER->GetUserGroupString() ))
{
	//Кешируемый код
	$this->IncludeComponentTemplate();
}
Подключение других компонентов в шаблоне
$cache_id = serialize(array($arParams, ($arParams['CACHE_GROUPS']==='N'? false: $USER->GetGroups())));
$obCache = new CPHPCache;
  if ($obCache->InitCache($arParams['CACHE_TIME'], $cache_id, '/'))
     {
      $vars = $obCache->GetVars();
      $arResult = $vars['arResult'];
     }
  elseif ($obCache->StartDataCache())
     {
      // делаем то, что надо
      $obCache->EndDataCache(array(
                            'arResult' => $arResult,
                            ));
}
// Подключение шаблона компонента
 $this->IncludeComponentTemplate();

Если в компоненте используется стандартное кэширование, но при этом не подключается шаблон (по причине ненадобности), нужно использовать:

if ($this->startResultCache())
{
    \\Код который модифицирует $arResult
    $this->endResultCache();
}

Tagged Cache in StartResultCache

В типовом механизме кеш-ия используется тегирование кеша с помощью методов StartTagCache и EndTagCache, а также Registertag.

Есть возможность тегировать компонент по своему при использовании типового механизма StartResultCache

Можно тегировать кеш типовых компонентов в result_modifier.php не меняя логики и не копируя в собственное пространство имен.

if (defined('BX_COMP_MANAGED_CACHE') && is_object($GLOBALS['CACHE_MANAGER'])){
  $GLOBALS['CACHE_MANAGER']->RegisterTag('my_custom_tag');
}

Тег в типовом механизме кеширования имеет вид iblock_id_#IBLOCK_ID#. Сбрасывается в методах Add/Update/Delete элементов, разделов и инфоблоков.

Изменение св-в элемента через метод SetPropertyValueCode

При изминении только св-в элемента кеш не очищается.

Задача: товары привязаны к новостям. В списке новостей вывести значения к-л сво-ва всех товаров, привязанных к новости.

  • Для товаров добавляем св-во NEWS (привязанные новости), MATERIALS.
  • Для эл-в новостей добавляем txt св-во PRODUCT_MATERIALS (в него будут записываться измененные св-ва привязанных товаров).
  • Далее создаются обработчики события на изменения товара OnAfterIBlockElementAdd, OnAfterIBlockElementUpdate, OnAfterIBlockElementDelete.
  • Получаем данные эл-в товаров, в частности привязки к новости NEWS, MATERIALS.
  • Далее все сво-ва PRODUCT_MATERIALS данной новости. И если значения материала данного товара нет, то добавить материал к значениям данного св-ва.

init.php

define('NEWS_IBLOCK_ID', 1);
define('PRODUCT_IBLOCK_ID', 4);
define('PRODUCT_NEWS_PROP_ID', 977 ); //ID св-ва привязки к новостям из ИБ товаров
define('PRODUCT_MATERIAL_PROP_ID', 979 ); // ID сво-ва MATERIAL из ИБ товаров

// Обработчик при обновлении элемента
AddEventHandler("iblock", "OnAfterIBlockElementAdd", array("CIBlockHandler", "OnAfterIBlockElementUpdateHandler"));
AddEventHandler("iblock", "OnAfterIBlockElementUpdate", array("CIBlockHandler", "OnAfterIBlockElementUpdateHandler"));
AddEventHandler("iblock", "OnAfterIBlockElementDelete", array("CIBlockHandler", "OnAfterIBlockElementUpdateHandler"));


class CIBlockHandler{
    function OnAfterIBlockElementUpdateHandler(&$arFields){

        //Если изменяемый эл-т принадлежит ИБ продукции
        if($arFields['IBLOCK_ID'] == PRODUCT_IBLOCK_ID ){
            $newsIblockID = NEWS_IBLOCK_ID; //ID инфоблока новостей

            //Получаем значение св-в NEWS и MATERIAL из ИБ товара
            //current(), если txt поле
            $arProductNews = current($arFields['PROPERTY_VALUES'][PRODUCT_NEWS_PROP_ID]);
            $arProductMaterial = current($arFields['PROPERTY_VALUES'][PRODUCT_MATERIAL_PROP_ID]);

            // Запрашиваем все текущие св-ва PRODUCT_MATERIAL из ИБ новостей.
            $rsNewsProperty = CIBlockElement::GetProperty($newsIblockID, $arProductNews['VALUE'], array("SORT" => "ASC"), array("CODE" => "PRODUCT_MATERIALS"));
            while( $arProperty = $rsNewsProperty->GetNext() ){
                $arRsProperty[] = $arProperty['VALUE'];
            }

            if(!in_array($arProductMaterial['VALUE'], $arRsProperty)){
                $arRsProperty[] = $arProductMaterial['VALUE'];
                CIBlockElement::SetPropertyValueCode($arProductNews['VALUE'], "PRODUCT_MATERIALS", $arRsProperty );

                if(defined("BX_COMP_MANAGED_CACHE") && is_object($GLOBALS['CACHE_MANAGER'])){
                    $GLOBALS['CACHE_MANAGER']->ClearByTag('news_list_tag');
                }
            }
        }
    }
}

result_modifier.php в компоненте news.list

...
//Добавляем уник. тег к кешу списка новостей
if (defined('BX_COMP_MANAGED_CACHE') && is_object($GLOBALS['CACHE_MANAGER'])){
    $GLOBALS['CACHE_MANAGER']->RegisterTag('news_list_tag');
}
Тегированный кеш

component.php

    if ($this->StartResultCache(......)) {
          if (defined('BX_COMP_MANAGED_CACHE') && is_object($GLOBALS['CACHE_MANAGER']))  {
              $GLOBALS['CACHE_MANAGER']->RegisterTag('my_custom_tag');
          }
    // do something
    $this->IncludeComponentTemplate();
    } else {
      $this->AbortResultCache();
    }

result_modifier.php

<?
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
if (defined('BX_COMP_MANAGED_CACHE') && is_object($GLOBALS['CACHE_MANAGER'])) {
   $cp =& $this->__component;
      if (strlen($cp->__cachePath)) {
          $GLOBALS['CACHE_MANAGER']->RegisterTag('my_custom_tag');
        }
 }
?>
//Сбросить кэш по тегу, например в обработчике
if (defined('BX_COMP_MANAGED_CACHE') && is_object($GLOBALS['CACHE_MANAGER']))
 $GLOBALS['CACHE_MANAGER']->ClearByTag('my_custom_tag');

Reset Cache AbortResultCache()

Важно избегать кеширования ошибочных данных. Если в процессе работы есть шанс получить некорректные данные, то вы должны обрабатывать данную ситуацию и сбрасывать автокеширование. Иначе в кеш запишется некорректный набор данных + шаблон и в течение всего времени жизни кеша пользователь будет видеть неверные данные.

// Для того чтобы сбросить кеш
global $USER; 
if ($this->StartResultCache( false, $USER->GetUserGroupString() )){
	//Кешируемый код
	if ($productNotFound)	{
		$this->AbortResultCache();
		return;
	}
	$this->SetResultCacheKeys(array('NAME'));
	$this->IncludeComponentTemplate();
}

Важно понимать, что при закешированном компоненте его результат отдается из кеша, и весь код, который написан в коде компонента внутри блока кеширования, а также весь код шаблона исполняться повторно не будет, поэтому если вы вдруг используете отложенные функции внутри шаблонов компонентов (или в result_modifier.php), то при кешировании они работать не будут. Поэтому никогда не используйте отложенные функции в шаблоне компонента (а точнее в файлах result_modifier.php и template.php). Если все же нужно использовать отложенные функции, вы можете произвести их вызов в файле component_epilog.php шаблона, т.к. его вызов не кешируется.

Особенности

  • $arResult в component_epilog содержит меньше данных, чем в шаблоне и result_modifier.php.
  • Для дополнения $arResult, доступного в component_epilog используется API ф-ия SetResultCacheKeys() в result_modifier.php. SetResultCacheKeys() ограничивает ключи массива, которые передаются в кэш.
  • component_epilog подключается после исполнения шаблона, после него могут следовать вызовы API.
  • component_epilog выполняется на каждом хите, не стоит размещать в него "тяжелый код".

Adding Variables in Cache SetResultCacheKeys()

В автоматический кеш компонента битрикс включить также и данные. Для этого нужно использовать метод SetResultCacheKeys(). Данный метод принимает на вход массив ключей, которые содержатся в $arResult. Если вы хотите сохранить в кеш компонента какую-то строку, например название товара (чтобы использовать его после выполнения компонента, например в component_epilog.php), который выводит компонент, то вам нужно использовать следующую конструкцию:

global $USER; 
if ($this->StartResultCache( false, $USER->GetUserGroupString() )){
	//Кешируемый код
	$this->SetResultCacheKeys(array('NAME'));
	$this->IncludeComponentTemplate();
}

Когда проектируем свой компонент - сохраняем в кэш только те данные, которые будем использовать в некешируемой части. Сохранять ITEMS с данными всех элементов явно нет смысла.

Adding Keys in component

component.php

<? if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();

//Проверяем входящие параметры в компоненте
$arParams['IBLOCK_ID'] = intval($arParams['IBLOCK_ID']);
if($arParams['IBLOCK_ID'] == 0)
    return;

$arParams['ELEMENT_ID'] = intval($arParams['ELEMENT_ID']);
if($arParams['ELEMENT_ID'] == 0)
    return;
//Подключаем модуль инфоблоков
CModule::IncludeModule('iblock');

$cacheID = $USER->GetUserGroupString();

if($this->StartResultCache(false, $cacheID)){
    //Код, кот. должен быть закеширован
    $rsDB = CIBlockElement::GetList(
        ['ID' => 'ASC'],                                                
        ['ACTIVE' => 'Y', 'IBLOCK_ID' => $arParams['IBLOCK_ID'], 'ID' => $arParams['ELEMENT_ID'] ], // Фильтрация
        false,
        false,                       // Кол-во на странице
        ['IBLOCK_ID', 'ID', 'NAME', 'PREVIEW_TEXT', 'CODE']
    );

    if($arElem = $rsDB -> GetNext() ){
        $arResult = $arElem;
    } else {
        $this->AbortResultCache();
    }

    //Добавление определенных полей в кеш.
    $this->SetResultCacheKeys(array(
        'ID',
        'NAME',
        'PREVIEW_TEXT'
    ));

    $this->IncludeComponentTemplate();
}
Adding Keys in result_modifier

Различие arResult между template и component_epilog в том, что в template получаем из кэша. В логике компонента есть метод $this->setResultCacheKeys, куда попадают все свойства которые кэшируются. Если нам необходимо получить в component_epilog получить данные из кэша, то в result_modifier.php эти свойства необходимо закэшировать с помощью метода $cp->setResultCacheKeys

result_modifier.php

<?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();

$cp = $this->__component; // Объект компонента
// или так $cp = $this->getComponent();

// Сделаем проверку на наличие привязки к пользователю
if( $arResult['PROPERTIES']['AUTHOR']['VALUE'] ) {
    $rsUser = CUser::GetByID($arResult['PROPERTIES']['AUTHOR']['VALUE']);
    $arUser = $rsUser->Fetch();

    $arResult['AUTHOR']['NAME'] = $arUser['NAME'];
    // Кэшируем AUTHOR
    $cp->setResultCacheKeys( array("AUTHOR") );
}
// Создаем новое обрезанное название
$arResult["NAME_10"] = substr( $arResult["NAME"], 0, 10).">>>";

$cp->setResultCacheKeys( array("NAME_10") ); // Закешируем новую переменную

component_epilog

<?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
// pre($arResult);
$str = $arResult["NAME"] . ' , '. $arResult["NAME_10"];

// Создаем закешированное мета-описание
$APPLICATION->SetPageProperty("description", $str);

Если кэшируем в классе компонента, то $this->setResultCacheKeys();, согласно доступным переменным

  • CBitrixComponentTemplate::getComponent(или обращение к $this->__component), позволяет получить объект компонента в template.php, result_modifier.php и с помощью SetResultCacheKeys доложить данные в кэш.
  • templateData, специальная переменная для передачи даных из template.php в component_epilog.php с поддержкой кэширования.

Добирать данные в component_epilog.php - не верно, он не кешируется, вы будете нагружать сервер на каждом хите.

Memcached

Подключение memcached осуществляется в файле dbconn.php.

Для эффективной работы системы настройки по умолчанию нужно изменить. Примеры:

// Мемкеш с объемом памяти 2ГБ 8 потоками и работой через tcp
PORT="11211"
USER="memcached"
MAXCONN="10240"
CACHESIZE="2048"
OPTIONS="-t 8"

// настройки в dbconn.php
define("BX_CACHE_TYPE", "memcache");
define("BX_CACHE_SID", $_SERVER["DOCUMENT_ROOT"]."#01");
define("BX_MEMCACHE_HOST", "127.0.0.1");
define("BX_MEMCACHE_PORT", "11211");
// Мемкеш с объемом памяти 2ГБ 8 потоками и работой через сокет
PORT="11211"
USER="bitrix"
MAXCONN="10240"
CACHESIZE="2048"
OPTIONS="-t 8 -s /tmp/memcached.sock"

// настройки в dbconn.php
define("BX_CACHE_TYPE", "memcache");
define("BX_CACHE_SID", $_SERVER["DOCUMENT_ROOT"]."#01");
define("BX_MEMCACHE_HOST", "unix:///tmp/memcached.sock");
define("BX_MEMCACHE_PORT", "0");

Youtube link https://hmarketing.ru/blog/bitrix/vsye-o-keshirovanii/

⚠️ **GitHub.com Fallback** ⚠️