класс CBitrixComponent



Класс CBitrixComponent - документация битрикс
описан тут /bitrix/modules/main/classes/general/component.php
/**
 * Bitrix Framework
 * @package bitrix
 * @subpackage main
 * @copyright 2001-2013 Bitrix
 */

use Bitrix\Main\IO;

class CBitrixComponent
{
   public $__name = "";
   private $__relativePath = "";
   public $__path = "";

   private $__templateName = "";
   public $__templatePage = "";

   /** @var CBitrixComponentTemplate */
   public $__template = null;

   private $__component_epilog = false;

   public $arParams = array();
   public $arResult = array();
   /** @var array */
   public $arResultCacheKeys = false;

   /** @var CBitrixComponent */
   public $__parent = null;

   private $__bInited = false;

   private $__arIncludeAreaIcons = array();

   private $__NavNum = false;

   /** @var CPHPCache */
   private $__cache = null;
   private $__cacheID = "";
   private $__cachePath = "";

   private $__children_css = array();
   private $__children_js = array();
   private $__children_epilogs = array();

   /** @var \Bitrix\Main\Composite\StaticArea[] */
   private $__children_frames = array();

   private $__view = array();

   private static $__componentCounter = array();
   private $__currentCounter = 0;
   private $__currentCounters = array();

   private $__editButtons = array();
   private static $__classes_map = array();
   private static $classes = array();
   private $classOfComponent = "";
   private $randomSequence = null;
   private $frameMode = null;

   /** @var  \Bitrix\Main\HttpRequest */
   protected $request;

   private $siteId = false;
   private $siteTemplateId = false;
   private $languageId = false;

   /** @var string|null */
   protected $signedParameters;

   /**
    * Event called from includeComponent before component execution.
    *
    * <p>Takes component parameters as argument and should return it formatted as needed.</p>
    * @param array[string]mixed $arParams
    * @return array[string]mixed
    *
    */
   public function onPrepareComponentParams($arParams)
   {
      return $arParams;
   }
   /**
    * Event called from includeComponent before component execution.
    *
    * <p>Includes component.php from within lang directory of the component.</p>
    * @return void
    *
    */
   public function onIncludeComponentLang()
   {
      $this->includeComponentLang();
   }
   /**
    * Function calls __includeComponent in order to execute the component.
    *
    * @return mixed
    *
    */
   public function executeComponent()
   {
      return $this->__includeComponent();
   }
   /**
    * Constructor with ability to copy the component.
    *
    * @param CBitrixComponent $component
    */
   public function __construct($component = null)
   {
      if(is_object($component) && ($component instanceof cbitrixcomponent))
      {
         $this->__name = $component->__name;
         $this->__relativePath = $component->__relativePath;
         $this->__path = $component->__path;
         $this->__templateName = $component->__templateName;
         $this->__templatePage = $component->__templatePage;
         $this->__template = $component->__template;
         $this->__component_epilog = $component->__component_epilog;
         $this->arParams = $component->arParams;
         $this->arResult = $component->arResult;
         $this->arResultCacheKeys = $component->arResultCacheKeys;
         $this->__parent = $component->__parent;
         $this->__bInited = $component->__bInited;
         $this->__arIncludeAreaIcons = $component->__arIncludeAreaIcons;
         $this->__NavNum = $component->__NavNum;
         $this->__cache = $component->__cache;
         $this->__cacheID = $component->__cacheID;
         $this->__cachePath = $component->__cachePath;
         $this->__children_css = $component->__children_css;
         $this->__children_js = $component->__children_js;
         $this->__children_epilogs = $component->__children_epilogs;
         $this->__children_frames = $component->__children_frames;
         $this->__view = $component->__view;
         $this->__currentCounter = $component->__currentCounter;
         $this->__currentCounters = $component->__currentCounters;
         $this->__editButtons = $component->__editButtons;
         $this->classOfComponent = $component->classOfComponent;
         $this->setSiteId($component->getSiteId());
         $this->setLanguageId($component->getLanguageId());
         $this->setSiteTemplateId($component->getSiteTemplateId());
      }
      else
      {
         $this->setSiteId(SITE_ID);
         $this->setLanguageId(LANGUAGE_ID);
         if (defined('SITE_TEMPLATE_ID'))
            $this->setSiteTemplateId(SITE_TEMPLATE_ID);
      }

      $this->request = \Bitrix\Main\Context::getCurrent()->getRequest();
   }
   /**
    * Function returns component name in form bitrix:component.name
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return string
    *
    */
   final public function getName()
   {
      if ($this->__bInited)
         return $this->__name;
      else
         return null;
   }
   /**
    * Function returns path to component in form /bitrix/component.name
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return string
    *
    */
   final public function getRelativePath()
   {
      if ($this->__bInited)
         return $this->__relativePath;
      else
         return null;
   }
   /**
    * Function returns path to component relative to Web server DOCUMENT_ROOT in form /bitrix/components/bitrix/component.name
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return string
    *
    */
   final public function getPath()
   {
      if ($this->__bInited)
         return $this->__path;
      else
         return null;
   }
   /**
    * Function returns the name of the template
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return string
    *
    */
   final public function getTemplateName()
   {
      if ($this->__bInited)
         return $this->__templateName;
      else
         return null;
   }
   /**
    * Function sets the name of the template. Returns true on success.
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @param string $templateName
    * @return bool
    *
    */
   final public function setTemplateName($templateName)
   {
      if (!$this->__bInited)
         return null;

      $this->__templateName = $templateName;
      return true;
   }

   /**
    * @param string $siteTemplateId
    */
   public function setSiteTemplateId($siteTemplateId)
   {
      $this->siteTemplateId = $siteTemplateId;
   }

   /**
    * @return mixed
    */
   public function getSiteTemplateId()
   {
      return $this->siteTemplateId;
   }

   /**
    * @param string $siteId
    */
   public function setSiteId($siteId)
   {
      $this->siteId = $siteId;
   }

   /**
    * @return mixed
    */
   public function getSiteId()
   {
      return $this->siteId;
   }

   /**
    * @param string $languageId
    */
   public function setLanguageId($languageId)
   {
      $this->languageId = $languageId;
   }

   /**
    * @return mixed
    */
   public function getLanguageId()
   {
      return $this->languageId;
   }

   /**
    * Returns signed parameters.
    * The list contains parameters which are presented in  \CBitrixComponent::listKeysSignedParameters().
    *
    * @see \CBitrixComponent::listKeysSignedParameters()
    *
    * @return string|null
    */
   final public function getSignedParameters()
   {
      return $this->signedParameters;
   }

   /**
    * Sings and stores parameters.
    *
    * @param array $params Parameters of component.
    *
    * @return $this
    * @throws \Bitrix\Main\ArgumentTypeException
    */
   private function storeSignedParameters(array $params)
   {
      $this->signedParameters = \Bitrix\Main\Component\ParameterSigner::signParameters($this->getName(), $params);

      return $this;
   }

   /**
    * List of keys of parameters which the component have to sign,
    *
    * @return null|array
    */
   protected function listKeysSignedParameters()
   {
      return null;
   }

   /**
    * Function returns the template page witch was set with initComponentTemplate
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return string
    *
    */
   final public function getTemplatePage()
   {
      if ($this->__bInited)
         return $this->__templatePage;
      else
         return null;
   }
   /**
    * Function returns the template object
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return CBitrixComponentTemplate
    *
    */
   final public function getTemplate()
   {
      if ($this->__bInited && $this->__template)
         return $this->__template;
      else
         return null;
   }
   /**
    * Function returns the parent component (if exists)
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return CBitrixComponent
    *
    */
   final public function getParent()
   {
      if ($this->__bInited && $this->__parent)
         return $this->__parent;
      else
         return null;
   }
   /**
    * Function returns current template css files or null if there is no template.
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return array[string][int]string
    *
    */
   final public function getTemplateCachedData()
   {
      if ($this->__bInited && $this->__template)
         return $this->__template->GetCachedData();
      else
         return null;
   }
   /**
    * Function applies collection of the css files to the current template.
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @param array[string][int]string $templateCachedData
    * @return void
    *
    */
   final public function setTemplateCachedData($templateCachedData)
   {
      if ($this->__bInited && $this->__template)
         $this->__template->ApplyCachedData($templateCachedData);
   }
   /**
    * Function includes class of the component by component name bitrix:component.base
    *
    * @param string $componentName
    * @return string
    *
    */
   final public static function includeComponentClass($componentName)
   {
      $component = new CBitrixComponent;
      $component->initComponent($componentName);

      return $component->classOfComponent;
   }
   /**
    * Function returns class name of the component by it's path.
    *
    * <p>At first class.php is checked and if exists then included.
    * Then if there is subsclass of CBitrixComponent found? it's name is returned.</p>
    * @param string $componentPath
    * @return string
    *
    */
   private function __getClassForPath($componentPath)
   {
      if (!isset(self::$__classes_map[$componentPath]))
      {
         $fname = $_SERVER["DOCUMENT_ROOT"].$componentPath."/class.php";
         if (file_exists($fname) && is_file($fname))
         {
            $beforeClasses = get_declared_classes();
            $beforeClassesCount = count($beforeClasses);
            include_once($fname);
            $afterClasses = get_declared_classes();
            $afterClassesCount = count($afterClasses);

            for ($i = $beforeClassesCount; $i < $afterClassesCount; $i++)
            {
               if (!isset(self::$classes[$afterClasses[$i]]) && is_subclass_of($afterClasses[$i], "cbitrixcomponent"))
               {
                  if (!isset(self::$__classes_map[$componentPath]) || is_subclass_of($afterClasses[$i], self::$__classes_map[$componentPath]))
                  {
                     self::$__classes_map[$componentPath] = $afterClasses[$i];

                     //recursion control
                     self::$classes[$afterClasses[$i]] = true;
                  }
               }
            }
         }
         else
         {
            //no need to try for several times
            self::$__classes_map[$componentPath] = "";
         }
      }
      return self::$__classes_map[$componentPath] ?? null;
   }
   /**
    * Function initializes the component. Returns true on success.
    *
    * <p>It is absolutly necessery to call this function before any component usage.</p>
    * @param string $componentName
    * @param string|bool $componentTemplate
    * @return bool
    *
    */
   final public function initComponent($componentName, $componentTemplate = false)
   {
      $this->__bInited = false;

      $componentName = trim($componentName);
      if ($componentName == '')
      {
         $this->__ShowError("Empty component name");
         return false;
      }

      $path2Comp = CComponentEngine::MakeComponentPath($componentName);
      if ($path2Comp == '')
      {
         $this->__ShowError(sprintf("'%s' is not a valid component name", $componentName));
         return false;
      }

      $componentPath = getLocalPath("components".$path2Comp);
      $this->classOfComponent = self::__getClassForPath($componentPath);

      if($this->classOfComponent === "")
      {
         $componentFile = $_SERVER["DOCUMENT_ROOT"].$componentPath."/component.php";
         if (!file_exists($componentFile) || !is_file($componentFile))
         {
            $this->__ShowError(sprintf("'%s' is not a component", $componentName));
            return false;
         }
      }

      self::increaseComponentCounter($componentName);
      $this->__currentCounter = self::$__componentCounter[$componentName];

      $this->__name = $componentName;
      $this->__relativePath = $path2Comp;
      $this->__path = $componentPath;
      $this->arResult = array();
      $this->arParams = array();
      $this->__parent = null;
      $this->__arIncludeAreaIcons = array();
      $this->__cache = null;
      if ($componentTemplate !== false)
         $this->__templateName = $componentTemplate;

      $this->__bInited = true;

      return true;
   }
   /**
    * Helper function for component parameters safe html escaping.
    *
    * @param array[string]mixed &$arParams
    * @return void
    *
    */
   final public function __prepareComponentParams(&$arParams)
   {
      if(!is_array($arParams))
      {
         return;
      }

      $p = $arParams; //this avoids endless loop
      foreach($p as $k => $v)
      {
         if (str_starts_with($k, '~'))
         {
            // already stored raw value
            continue;
         }

         // store raw value
         $arParams["~".$k] = $v;

         if (isset($v))
         {
            if (is_string($v))
            {
               if (preg_match("/[;&<>\"]/", $v))
               {
                  $arParams[$k] = htmlspecialcharsEx($v);
               }
            }
            elseif (is_array($v))
            {
               //one more cycle, php 7 bug https://bugs.php.net/bug.php?id=71969
               foreach($v as $kk => $vv)
               {
                  if (is_string($vv))
                  {
                     $arParams[$k][$kk] = htmlspecialcharsEx($vv);
                  }
               }
            }
         }
      }
   }
   /**
    * Function includes language files from within the component directory.
    *
    * <p>For example: $this->includeComponentLang("ajax.php") will include "lang/en/ajax.php" file. </p>
    * <p>Note: component must be inited by initComponent method.</p>
    * @param string $relativePath
    * @param string|bool $lang
    * @return void
    *
    */
   final public function includeComponentLang($relativePath = "", $lang = false)
   {
      if (!$this->__bInited)
         return null;

      if ($relativePath == "")
         $relativePath = "component.php";

      $path = $_SERVER["DOCUMENT_ROOT"].$this->__path."/".$relativePath;

      if($lang === false)
      {
         \Bitrix\Main\Localization\Loc::loadMessages($path);
      }
      else
      {
         \Bitrix\Main\Localization\Loc::loadLanguageFile($path, $lang);
      }
   }
   /**
    * Function includes component.php file thus executing the component. Returns what component.php returns.
    *
    * <p>Before include there is some helper variables made available for component.php scope.</p>
    * <p>Note: component must be inited by initComponent method.</p>
    * @return mixed
    *
    */
   final protected function __includeComponent()
   {
      /** @noinspection PhpUnusedLocalVariableInspection */
      global $APPLICATION, $USER, $DB;

      if (!$this->__bInited)
         return null;

      //these vars are used in the component file
      $arParams = &$this->arParams;
      $arResult = &$this->arResult;

      $componentPath = $this->__path;
      $componentName = $this->__name;
      $componentTemplate = $this->getTemplateName();

      if ($this->__parent)
      {
         $parentComponentName = $this->__parent->__name;
         $parentComponentPath = $this->__parent->__path;
         $parentComponentTemplate = $this->__parent->getTemplateName();
      }
      else
      {
         $parentComponentName = "";
         $parentComponentPath = "";
         $parentComponentTemplate = "";
      }

      return include($_SERVER["DOCUMENT_ROOT"].$this->__path."/component.php");
   }
   /**
    * Function executes the component. Returns the result of it's execution.
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @param string $componentTemplate
    * @param array $arParams
    * @param CBitrixComponent|null $parentComponent
    * @return mixed
    *
    */
   final public function includeComponent($componentTemplate, $arParams, $parentComponent, $returnResult = false)
   {
      if (!$this->__bInited)
         return null;

      if ($componentTemplate !== false)
         $this->setTemplateName($componentTemplate);

      if ($parentComponent instanceof cbitrixcomponent)
         $this->__parent = $parentComponent;

      if (!isset($arParams["CACHE_TYPE"]) || ($arParams["CACHE_TYPE"] != "Y" && $arParams["CACHE_TYPE"] != "N"))
      {
         $arParams["CACHE_TYPE"] = "A";
      }

      if($this->classOfComponent)
      {
         /** @var CBitrixComponent $component  */
         $component = new $this->classOfComponent($this);
         $component->onIncludeComponentLang();

         $keysToExport = $component->listKeysSignedParameters();
         if($keysToExport)
         {
            $component->storeSignedParameters(array_intersect_key($arParams, array_combine($keysToExport, $keysToExport)));
         }

         $component->arParams = $component->onPrepareComponentParams($arParams);
         $component->__prepareComponentParams($component->arParams);

         $componentFrame = new \Bitrix\Main\Composite\Internals\AutomaticArea($component);
         $componentFrame->start();

         if($returnResult)
         {
            $component->executeComponent();
            $result = $component->arResult;
         }
         else
         {
            $result = $component->executeComponent();
         }

         $this->__arIncludeAreaIcons = $component->__arIncludeAreaIcons;
         $frameMode = $component->getFrameMode();

         $componentFrame->end();
      }
      else
      {
         $this->includeComponentLang();
         $this->__prepareComponentParams($arParams);
         $this->arParams = $arParams;

         $componentFrame = new \Bitrix\Main\Composite\Internals\AutomaticArea($this);
         $componentFrame->start();

         if($returnResult)
         {
            $this->__IncludeComponent();
            $result = $this->arResult;
         }
         else
         {
            $result = $this->__IncludeComponent();
         }

         $frameMode = $this->getFrameMode();

         $componentFrame->end();
      }

      if (!$frameMode)
      {
         $page = \Bitrix\Main\Composite\Page::getInstance();
         $page->giveNegativeComponentVote($this->__name);
      }

      return $result;
   }
   /**
    * Function executes the template.
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @param string $templatePage
    * @param string $customTemplatePath
    * @return void
    *
    */
   final public function includeComponentTemplate($templatePage = "", $customTemplatePath = "")
   {
      if (!$this->__bInited)
         return null;

      if ($this->initComponentTemplate($templatePage, $this->getSiteTemplateId(), $customTemplatePath))
      {
         $this->showComponentTemplate();
         if($this->__component_epilog)
            $this->includeComponentEpilog($this->__component_epilog);
      }
      else
      {
         $this->abortResultCache();
         $this->__showError(str_replace(
            array("#PAGE#", "#NAME#"),
            array($templatePage, $this->getTemplateName()),
            "Cannot find '#NAME#' template with page '#PAGE#'"
         ));
      }
      $this->__template->__component = null;
   }
   /**
    * Function initializes the template of the component. Returns true on success.
    *
    * <p>Instansiates the template object and calls it's init function.</p>
    * <p>Note: component must be inited by initComponent method.</p>
    * @param string $templatePage
    * @param string|bool $siteTemplate
    * @param string $customTemplatePath
    * @return bool
    *
    */
   final public function initComponentTemplate($templatePage = "", $siteTemplate = false, $customTemplatePath = "")
   {
      if (!$this->__bInited)
         return null;

      try
      {
         $this->__templatePage = IO\Path::normalize($templatePage);
      }
      catch (IO\InvalidPathException $e)
      {
         $this->__templatePage = '';
      }

      $this->__template = new CBitrixComponentTemplate();
      $this->__template->setLanguageId($this->getLanguageId());
      if ($this->__template->Init($this, $siteTemplate, $customTemplatePath))
         return true;
      else
         return false;
   }
   /**
    * Function executes initialized template of the component.
    *
    * <p>Note: component must be inited by initComponent method.</p>
    * @return void
    *
    */
   final public function showComponentTemplate()
   {
      if (!$this->__bInited)
         return null;

      if ($this->__template)
         $this->__template->includeTemplate($this->arResult);

      if(is_array($this->arResultCacheKeys))
      {
         $arNewResult = array();
         foreach($this->arResultCacheKeys as $key)
            if(array_key_exists($key, $this->arResult))
               $arNewResult[$key] = $this->arResult[$key];
         $this->arResult = $arNewResult;
      }

      if(!empty($this->__editButtons))
      {
         foreach($this->__editButtons as $button)
         {
            if($button[0] == 'AddEditAction')
               $this->addEditAction($button[1], $button[2], $button[3], $button[4]);
            else
               $this->addDeleteAction($button[1], $button[2], $button[3], $button[4]);
         }
      }

      $this->__template->endViewTarget();

      $this->endResultCache();
   }
   /**
    * Function adds an Icon to the component area in the editing mode.
    *
    * @param array[string]mixed $arIcon
    * @return void
    *
    */
   final public function addIncludeAreaIcon($arIcon)
   {
      if (!isset($this->__arIncludeAreaIcons) || !is_array($this->__arIncludeAreaIcons))
         $this->__arIncludeAreaIcons = array();

      $this->__arIncludeAreaIcons[] = $arIcon;
   }
   /**
    * Function replaces Icons displayed for the component by an collection.
    *
    * @param array[int][string]mixed $arIcon
    * @return void
    *
    */
   final public function addIncludeAreaIcons($arIcons)
   {
      if(is_array($arIcons))
         $this->__arIncludeAreaIcons = $arIcons;
   }
   /**
    * Function returns the collection of the Icons displayed for the component.
    *
    * @return array[int][string]mixed
    *
    */
   final public function getIncludeAreaIcons()
   {
      return $this->__arIncludeAreaIcons;
   }
   /**
    * Function returns an cache identifier based on component parameters and environment.
    *
    * @param mixed $additionalCacheID
    * @return string
    */
   public function getCacheID($additionalCacheID = false)
   {
      if(!$this->getSiteId())
         $SITE_ID = SITE_ID;
      else
         $SITE_ID = $this->getSiteId();

      if(!$this->getLanguageId())
         $LANGUAGE_ID = LANGUAGE_ID;
      else
         $LANGUAGE_ID = $this->getLanguageId();

      if(!$this->getSiteTemplateId())
         $SITE_TEMPLATE_ID = (defined("SITE_TEMPLATE_ID")? SITE_TEMPLATE_ID:"");
      else
         $SITE_TEMPLATE_ID = $this->getSiteTemplateId();

      $cacheID = $SITE_ID."|".$LANGUAGE_ID.($SITE_TEMPLATE_ID != "" ? "|".$SITE_TEMPLATE_ID:"")."|".$this->__name."|".$this->getTemplateName()."|";

      foreach($this->arParams as $k=>$v)
         if(strncmp("~", $k, 1))
            $cacheID .= ",".$k."=".serialize($v);

      if(($offset = CTimeZone::getOffset()) <> 0)
         $cacheID .= "|".$offset;

      if ($additionalCacheID !== false)
         $cacheID .= "|".serialize($additionalCacheID);

      if ($this->__currentCounter > 1)
      {
         $cacheID .= "|".$this->__currentCounter;
      }

      return $cacheID;
   }
   /**
    * Function starts the caching block of the component execution.
    *
    * @param int|bool $cacheTime
    * @param mixed $additionalCacheID
    * @param string|bool $cachePath
    * @return string
    *
    */
   final public function startResultCache($cacheTime = false, $additionalCacheID = false, $cachePath = false)
   {
      /** @global CMain $APPLICATION */
      global $APPLICATION, $CACHE_MANAGER;

      if (!$this->__bInited)
         return null;

      if ($this->arParams["CACHE_TYPE"] == "N" || ($this->arParams["CACHE_TYPE"] == "A" && COption::getOptionString("main", "component_cache_on", "Y") == "N"))
         return true;

      if ($cacheTime === false)
         $cacheTime = intval($this->arParams["CACHE_TIME"] ?? 0);

      $this->__cacheID = $this->getCacheID($additionalCacheID);
      $this->__cachePath = $cachePath;
      if ($this->__cachePath === false)
         $this->__cachePath = $CACHE_MANAGER->getCompCachePath($this->__relativePath);

      $this->__cache = \Bitrix\Main\Data\Cache::createInstance(['actual_data' => false]);
      if ($this->__cache->startDataCache($cacheTime, $this->__cacheID, $this->__cachePath))
      {
         $this->__NavNum = $GLOBALS["NavNum"] ?? null;
         $this->__currentCounters = self::$__componentCounter;

         if (defined("BX_COMP_MANAGED_CACHE") && $this->__cache->isStarted())
            $CACHE_MANAGER->startTagCache($this->__cachePath);

         return true;
      }
      else
      {
         $arCache = $this->__cache->GetVars();
         $this->arResult = $arCache["arResult"];
         if (array_key_exists("templateCachedData", $arCache))
         {
            $templateCachedData = & $arCache["templateCachedData"];

            if ($templateCachedData && is_array($templateCachedData))
            {
               if (array_key_exists("additionalCSS", $templateCachedData) && $templateCachedData["additionalCSS"] <> '')
               {
                  $APPLICATION->SetAdditionalCSS($templateCachedData["additionalCSS"]);
                  if($this->__parent)
                     $this->__parent->addChildCSS($templateCachedData["additionalCSS"]);
               }

               if (array_key_exists("additionalJS", $templateCachedData) && $templateCachedData["additionalJS"] <> '')
               {
                  $APPLICATION->AddHeadScript($templateCachedData["additionalJS"]);
                  if($this->__parent)
                     $this->__parent->addChildJS($templateCachedData["additionalJS"]);
               }

               if (array_key_exists("frames", $templateCachedData) && is_array($templateCachedData["frames"]))
               {
                  foreach ($templateCachedData["frames"] as $frameState)
                  {
                     $frame = \Bitrix\Main\Composite\StaticArea::applyCachedData($frameState);
                     if ($this->__parent)
                     {
                        $this->__parent->addChildFrame($frame);
                     }
                  }
               }

               if (
                  array_key_exists("__children_frames", $templateCachedData) &&
                  is_array($templateCachedData["__children_frames"])
               )
               {
                  foreach ($templateCachedData["__children_frames"] as $frame)
                  {
                     \Bitrix\Main\Composite\StaticArea::applyCachedData($frame);
                  }
               }

               if (array_key_exists("frameMode", $templateCachedData))
               {
                  $templateFrameMode = $templateCachedData["frameMode"];

                  if ($this->getRealFrameMode() !== false)
                  {
                     $this->setFrameMode($templateFrameMode);
                  }

                  if ($this->getRealFrameMode() === false)
                  {
                     $context = isset($templateCachedData["frameModeCtx"])
                        ? "(from component cache) ".$templateCachedData["frameModeCtx"]
                        : $this->__name." - a cached template set frameMode=false";

                     $page = \Bitrix\Main\Composite\Page::getInstance();
                     $page->giveNegativeComponentVote($context);
                  }
               }

               if (isset($templateCachedData["externalCss"]))
               {
                  foreach ($templateCachedData["externalCss"] as $cssPath)
                  {
                     $APPLICATION->SetAdditionalCSS($cssPath);
                     //Check if parent component exists and plug css it to it's "collection"
                     if($this->__parent)
                        $this->__parent->addChildCSS($cssPath);
                  }
               }

               if (isset($templateCachedData["externalJs"]))
               {
                  foreach ($templateCachedData["externalJs"] as $jsPath)
                  {
                     $APPLICATION->AddHeadScript($jsPath);
                     //Check if parent component exists and plug js it to it's "collection"
                     if($this->__parent)
                        $this->__parent->addChildJS($jsPath);
                  }
               }
            }

            if (isset($templateCachedData["__editButtons"]))
            {
               foreach ($templateCachedData["__editButtons"] as $button)
               {
                  if ($button[0] == 'AddEditAction')
                     $this->addEditAction($button[1], $button[2], $button[3], $button[4]);
                  else
                     $this->addDeleteAction($button[1], $button[2], $button[3], $button[4]);
               }
            }

            if (isset($templateCachedData["__view"]))
            {
               foreach ($templateCachedData["__view"] as $view_id => $target)
                  foreach ($target as $view_content)
                     $APPLICATION->addViewContent($view_id, $view_content[0], $view_content[1]);
            }

            if (array_key_exists("__NavNum", $templateCachedData))
            {
               $GLOBALS["NavNum"]+= $templateCachedData["__NavNum"];
            }

            if (array_key_exists("__currentCounters", $templateCachedData))
            {
               foreach ($templateCachedData["__currentCounters"] as $componentName => $counter)
               {
                  self::increaseComponentCounter($componentName, $counter);
               }
            }

            if (array_key_exists("__children_css", $templateCachedData))
            {
               foreach ($templateCachedData["__children_css"] as $css_url)
                  $APPLICATION->setAdditionalCSS($css_url);
            }

            if (array_key_exists("__children_js", $templateCachedData))
            {
               foreach ($templateCachedData["__children_js"] as $js_url)
                  $APPLICATION->addHeadScript($js_url);
            }

            if (array_key_exists("__children_epilogs", $templateCachedData))
            {
               foreach ($templateCachedData["__children_epilogs"] as $component_epilog)
                  $this->includeComponentEpilog($component_epilog);
            }

            if (array_key_exists("component_epilog", $templateCachedData))
            {
               $this->includeComponentEpilog($templateCachedData["component_epilog"]);
            }
         }
         return false;
      }
   }
   /**
    * Function ends the caching block of the component execution.
    *
    * <p>Note: automaticly called by includeComponentTemplate.</p>
    * @return void
    *
    */
   final public function endResultCache()
   {
      global $NavNum, $CACHE_MANAGER;

      if (!$this->__bInited)
         return null;

      if (!$this->__cache)
      {
         if ($this->__parent)
         {
            foreach ($this->__children_css as $cssPath)
            {
               $this->__parent->addChildCSS($cssPath);
            }

            foreach ($this->__children_js as $jsPath)
            {
               $this->__parent->addChildJS($jsPath);
            }

            foreach ($this->__children_epilogs as $epilogFile)
            {
               $this->__parent->addChildEpilog($epilogFile);
            }

            foreach ($this->__children_frames as $frame)
            {
               $this->__parent->addChildFrame($frame);
            }
         }
         return null;
      }

      $arCache = array(
         "arResult" => $this->arResult,
      );
      if ($this->__template)
      {
         $arCache["templateCachedData"] = $this->__template->getCachedData();
         if ($this->__component_epilog)
            $arCache["templateCachedData"]["component_epilog"] = $this->__component_epilog;
      }
      else
      {
         $arCache["templateCachedData"] = array();
      }

      if (($this->__NavNum !== false) && ($this->__NavNum !== $NavNum))
      {
         $arCache["templateCachedData"]["__NavNum"] = $NavNum - $this->__NavNum;
      }

      $currentCountersDiff = array();
      foreach (self::$__componentCounter as $componentName => $counter)
      {
         if (array_key_exists($componentName, $this->__currentCounters))
         {
            if (self::$__componentCounter[$componentName] > $this->__currentCounters[$componentName])
            {
               $currentCountersDiff[$componentName] =
                  self::$__componentCounter[$componentName] - $this->__currentCounters[$componentName];
            }
         }
         else
         {
            $currentCountersDiff[$componentName] = self::$__componentCounter[$componentName];
         }
      }

      if (!empty($currentCountersDiff))
      {
         $arCache["templateCachedData"]["__currentCounters"] = $currentCountersDiff;
      }

      if (!empty($this->__children_css))
      {
         $arCache["templateCachedData"]["__children_css"] = $this->__children_css;
         if ($this->__parent)
         {
            foreach($this->__children_css as $cssPath)
               $this->__parent->addChildCSS($cssPath);
         }
      }

      if (!empty($this->__children_js))
      {
         $arCache["templateCachedData"]["__children_js"] = $this->__children_js;
         if ($this->__parent)
         {
            foreach($this->__children_js as $jsPath)
               $this->__parent->addChildJS($jsPath);
         }
      }

      if (!empty($this->__children_epilogs))
      {
         $arCache["templateCachedData"]["__children_epilogs"] = $this->__children_epilogs;
         if ($this->__parent)
         {
            foreach($this->__children_epilogs as $epilogFile)
               $this->__parent->addChildEpilog($epilogFile);
         }
      }

      if (!empty($this->__children_frames))
      {
         $arCache["templateCachedData"]["__children_frames"] =
            array_map(
               function($frame) {
                  return $frame->getCachedData();
               },
               $this->__children_frames
            );

         if ($this->__parent)
         {
            foreach ($this->__children_frames as $frame)
            {
               $this->__parent->addChildFrame($frame);
            }
         }
      }

      if (!empty($this->__view))
         $arCache["templateCachedData"]["__view"] = $this->__view;

      if (!empty($this->__editButtons))
         $arCache["templateCachedData"]["__editButtons"] = $this->__editButtons;

      $cacheWasStarted = $this->__cache->isStarted();
      $this->__cache->endDataCache($arCache);

      if (defined("BX_COMP_MANAGED_CACHE") && $cacheWasStarted)
         $CACHE_MANAGER->endTagCache();

      $this->__cache = null;
   }
   /**
    * Function aborts the cache after it's start.
    *
    * <p>Note: must be called if component returns before endResultCache or includeComponentTemplate called.</p>
    * @return void
    *
    */
   final public function abortResultCache()
   {
      global $CACHE_MANAGER;

      if (!$this->__bInited)
         return null;

      if (!$this->__cache)
         return null;

      $cacheWasStarted = $this->__cache->isStarted();
      $this->__cache->abortDataCache();

      if(defined("BX_COMP_MANAGED_CACHE") && $cacheWasStarted)
         $CACHE_MANAGER->abortTagCache();

      $this->__cache = null;
   }
   /**
    * Function deletes the cache created before.
    *
    * <p>Note: parameters must exactly match to startResultCache call.</p>
    * @param mixed $additionalCacheID
    * @param string|bool $cachePath
    * @return void
    *
    */
   final public function clearResultCache($additionalCacheID = false, $cachePath = false)
   {
      global $CACHE_MANAGER;

      if (!$this->__bInited)
         return null;

      $this->__cacheID = $this->getCacheID($additionalCacheID);

      $this->__cachePath = $cachePath;
      if ($this->__cachePath === false)
         $this->__cachePath = $CACHE_MANAGER->getCompCachePath($this->__relativePath);

      $cache = new CPHPCache();
      $cache->clean($this->__cacheID, $this->__cachePath);
   }
   /**
    * Function clears entire component cache.
    *
    * <p>Note: parameters must exactly match to startResultCache call.</p>
    * @param string $componentName
    * @param string $siteId
    * @return void
    *
    */
   final public static function clearComponentCache($componentName, $siteId = "")
   {
      /** @global CCacheManager $CACHE_MANAGER */
      global $CACHE_MANAGER;

      $componentRelativePath = CComponentEngine::MakeComponentPath($componentName);
      if ($componentRelativePath != "")
      {
         $obCache = new CPHPCache;
         $obCache->CleanDir($componentRelativePath, "cache");
         BXClearCache(true, $componentRelativePath);

         if ($siteId == "")
         {
            $rsSite = \Bitrix\Main\SiteTable::getList(array('order' => array('SORT' => 'ASC')));
            while ($site = $rsSite->fetch())
            {
               $componentCachePath = "/".$site["LID"].$componentRelativePath;
               $obCache = new CPHPCache;
               $obCache->CleanDir($componentCachePath, "cache");
               BXClearCache(true, $componentCachePath);
            }
         }
         else
         {
            $componentCachePath = "/".$siteId.$componentRelativePath;
            $obCache = new CPHPCache;
            $obCache->CleanDir($componentCachePath, "cache");
            BXClearCache(true, $componentCachePath);
         }

         if(defined("BX_COMP_MANAGED_CACHE"))
            $CACHE_MANAGER->ClearByTag($componentName);
      }
   }
   /**
    * Function returns component cache path.
    *
    * @return string
    *
    */
   final public function getCachePath()
   {
      return $this->__cachePath;
   }
   /**
    * Function marks the arResult keys to be saved to cache. Just like __sleep magic method do.
    *
    * <p>Note: it's call adds key, not replacing.</p>
    * @param array[int]string $arResultCacheKeys
    * @return void
    *
    */
   final public function setResultCacheKeys($arResultCacheKeys)
   {
      if ($this->arResultCacheKeys === false)
         $this->arResultCacheKeys = $arResultCacheKeys;
      else
         $this->arResultCacheKeys = array_merge($this->arResultCacheKeys, $arResultCacheKeys);
   }
   /**
    * Function returns component area id for editing mode.
    *
    * @param string $entryId
    * @return string
    *
    */
   final public function getEditAreaId($entryId)
   {
      return 'bx_'.abs(crc32($this->GetName().'_'.$this->__currentCounter)).'_'.$entryId;
   }
   /**
    * Function adds an edit action to some area inside the component.
    *
    * @param string $entryId
    * @param string $editLink
    * @param string|bool $editTitle
    * @param array[string]mixed $arParams
    * @return void
    *
    */
   final public function addEditAction($entryId, $editLink, $editTitle = false, $arParams = array())
   {
      /** @global CMain $APPLICATION */
      global $APPLICATION;

      if (!$entryId || !$editLink)
         return;

      if (!$editTitle)
      {
         IncludeModuleLangFile(__FILE__);
         $editTitle = GetMessage('EDIT_ACTION_TITLE_DEFAULT');
      }

      if (!is_array($arParams))
         $arParams = array();

      if (!($arParams['WINDOW'] ?? null))
         $arParams['WINDOW'] = array(
            "width" => 780,
            "height" => 500,
         );

      if (!($arParams['ICON'] ?? '') && !($arParams['SRC'] ?? '') && !($arParams['IMAGE'] ?? ''))
         $arParams['ICON'] = 'bx-context-toolbar-edit-icon';

      $arBtn = array(
         'URL' => 'javascript:'.$APPLICATION->getPopupLink(array(
            'URL' => $editLink,
            "PARAMS" => $arParams['WINDOW'],
         )),
         'TITLE' => $editTitle,
      );

      if ($arParams['ICON'])
         $arBtn['ICON'] = $arParams['ICON'];
      elseif ($arParams['SRC'] || $arParams['IMAGE'])
         $arBtn['SRC'] = $arParams['IMAGE'] ? $arParams['IMAGE'] : $arParams['SRC'];

      $APPLICATION->setEditArea($this->getEditAreaId($entryId), array(
         $arBtn,
      ));
   }
   /**
    * Function adds an delete action to some area inside the component.
    *
    * <ul>
    * <li>$arParams['CONFIRM'] = false - disable confirm;
    * <li>$arParams['CONFIRM'] = 'Text' - confirm with custom text;
    * <li>no $arParams['CONFIRM'] at all - confirm with default text
    * </ul>
    * @param string $entryId
    * @param string $deleteLink
    * @param string|bool $deleteTitle
    * @param array[string]mixed $arParams
    * @return void
    *
    */
   final public function addDeleteAction($entryId, $deleteLink, $deleteTitle = false, $arParams = array())
   {
      /** @global CMain $APPLICATION */
      global $APPLICATION;

      if (!$entryId || !$deleteLink)
         return;

      includeModuleLangFile(__FILE__);
      if (!$deleteTitle)
      {
         $deleteTitle = GetMessage('DELETE_ACTION_TITLE_DEFAULT');
      }

      if (!is_array($arParams))
         $arParams = array();

      if (!($arParams['ICON'] ?? '') && !($arParams['SRC'] ?? '') && !($arParams['IMAGE'] ?? ''))
         $arParams['ICON'] = 'bx-context-toolbar-delete-icon';

      if (mb_substr($deleteLink, 0, 11) != 'javascript:')
      {
         if (false === mb_strpos($deleteLink, 'return_url='))
            $deleteLink.= '&return_url='.urlencode($APPLICATION->getCurPageParam());

         $deleteLink.= '&'.bitrix_sessid_get();
         if ($arParams['CONFIRM'] !== false)
         {
            $confirmText = $arParams['CONFIRM'] ? $arParams['CONFIRM'] : GetMessage('DELETE_ACTION_CONFIRM');
            $deleteLink = 'javascript:if(confirm(\''.CUtil::JSEscape($confirmText).'\')) jsUtils.Redirect([], \''.CUtil::JSEscape($deleteLink).'\');';
         }
      }

      $arBtn = array(
         'URL' => $deleteLink,
         'TITLE' => $deleteTitle,
      );

      if ($arParams['ICON'])
         $arBtn['ICON'] = $arParams['ICON'];
      elseif ($arParams['SRC'] || $arParams['IMAGE'])
         $arBtn['SRC'] = $arParams['IMAGE'] ? $arParams['IMAGE'] : $arParams['SRC'];

      $APPLICATION->setEditArea($this->getEditAreaId($entryId), array(
         $arBtn,
      ));
   }
   /**
    * Function saves component epilog environment
    *
    * @param array[string]mixed $arEpilogInfo
    * @return void
    *
    */
   final public function setTemplateEpilog($arEpilogInfo)
   {
      $this->__component_epilog = $arEpilogInfo;
      //Check if parent component exists and plug epilog it to it's "collection"
      if ($this->__parent)
         $this->__parent->addChildEpilog($this->__component_epilog);
   }
   /**
    * Function restores component epilog environment and executes it.
    *
    * @param array[string]mixed $arEpilogInfo
    * @return void
    *
    */
   final public function includeComponentEpilog($arEpilogInfo)
   {
      /** @noinspection PhpUnusedLocalVariableInspection */
      global $APPLICATION, $USER, $DB;

      // available variables in the epilog file:
      // $templateName, $templateFile, $templateFolder, $templateData
      /** @var $epilogFile */
      extract($arEpilogInfo);
      if ($epilogFile <> '' && file_exists($_SERVER["DOCUMENT_ROOT"].$epilogFile))
      {
         //these vars can be used in the epilog file
         $arParams = $this->arParams;
         $arResult = $this->arResult;
         $componentPath = $this->GetPath();
         $component = $this;
         include($_SERVER["DOCUMENT_ROOT"].$epilogFile);
      }
   }
   /**
    * Function shows an internal error message.
    *
    * @param string $errorMessage
    * @param string $errorCode
    * @return void
    *
    */
   public function __showError($errorMessage, $errorCode = "")
   {
      if ($errorMessage <> '')
         echo "<font color=\"#FF0000\">".htmlspecialcharsbx($errorMessage.($errorCode <> '' ? " [".$errorCode."]" : ""))."</font>";
   }
   /**
    * Function registers children css file for cache.
    *
    * @param string $cssPath
    * @return void
    *
    */
   final public function addChildCSS($cssPath)
   {
      $this->__children_css[] = $cssPath;
   }
   /**
    * Function registers children js file for cache.
    *
    * @param string $jsPath
    * @return void
    *
    */
   final public function addChildJS($jsPath)
   {
      $this->__children_js[] = $jsPath;
   }
   /**
    * Function registers children epilog file for cache.
    *
    * @param string $epilogFile
    * @return void
    *
    */
   final public function addChildEpilog($epilogFile)
   {
      $this->__children_epilogs[] = $epilogFile;
   }

   /**
    * Registers child frame for cache.
    *
    * @param \Bitrix\Main\Composite\StaticArea $frame
    * @return void
    *
    */
   final public function addChildFrame($frame)
   {
      $this->__children_frames[] = $frame;
   }

   /**
    * Function adds a button to be displayed.
    *
    * @param array[int]string $arButton
    * @return void
    *
    */
   final public function addEditButton($arButton)
   {
      $this->__editButtons[] = $arButton;
   }
   /**
    * Function registers new view target for the cache.
    *
    * @param string $target
    * @param string $content
    * @param int $pos
    * @return void
    *
    */
   final public function addViewTarget($target, $content, $pos)
   {
      if(!isset($this->__view[$target]))
         $this->__view[$target] = array();

      $this->__view[$target][] = array($content, $pos);
   }

   private static function increaseComponentCounter($componentName, $counter = 1)
   {
      if (!isset(self::$__componentCounter[$componentName]))
      {
         self::$__componentCounter[$componentName] = $counter;
      }
      else
      {
         self::$__componentCounter[$componentName] += $counter;
      }
   }

   /**
    * Function returns next pseudo random value.
    *
    * @param int $length
    * @return string
    *
    * @see \Bitrix\Main\Type\RandomSequence::randString
    */
   public function randString($length = 6)
   {
      if (!$this->randomSequence)
      {
         $seed = $this->__name."|".self::$__componentCounter[$this->__name];
         $this->randomSequence = new \Bitrix\Main\Type\RandomSequence($seed);
      }
      return $this->randomSequence->randString($length);
   }

   /**
    * Marks a component as capable of composite mode.
    * You should use is to mark a whole component as
    * composite incompatible.
    *
    * @param bool $mode
    * @return void
    *
    */
   public function setFrameMode($mode)
   {
      if (in_array($mode, array(true, false, null), true))
      {
         $this->frameMode = $mode;
      }
   }

   public function getFrameMode()
   {
      if ($this->frameMode !== null)
      {
         return $this->frameMode;
      }

      return true;
   }

   public function getRealFrameMode()
   {
      return $this->frameMode;
   }

   public function getDefaultFrameMode()
   {
      $frameMode = null;

      $compositeOptions = \Bitrix\Main\Composite\Helper::getOptions();
      $componentParams = $this->arParams;

      if (
         isset($componentParams["COMPOSITE_FRAME_MODE"]) &&
         in_array($componentParams["COMPOSITE_FRAME_MODE"], array("Y", "N"))
      )
      {
         $frameMode = $componentParams["COMPOSITE_FRAME_MODE"] === "Y";
      }
      else if (isset($compositeOptions["FRAME_MODE"]))
      {
         $frameMode = $compositeOptions["FRAME_MODE"] === "Y";
   
                }
      return $frameMode;
   }
}
Если блог был полезным, можете угостить меня "чашечкой кофе" :)

Сбер по номеру телефона +7 (953) 585-13-09 Вероника.
Спасибо!