Класс CBitrixComponent - документация битрикс
описан тут /bitrix/modules/main/classes/general/component.php
описан тут /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; } } |