Давно у меня зрела мысль, как сделать некэшируемый модификатор для компонентов. И вот сегодня случилась бессонница и я решил - пора.
Идея была следующая: с помощью расширения php runkit (из pecl) изменить некоторые методы класса CBitrixComponent, чтобы специальный файл подключался и работал.
Это даст нам возможность не копируя компонент "к себе" повлиять на его работу.
[spoiler]
Сначала я думал разместить этот файл в папке шаблона, однако для того, чтобы получить путь до этой папки, нам нужно создать объект класса CBitrixComponentTemplate даже в том случае, когда шаблон не подгружается (работает кэш). Я решил, что это некомильфо и придумал следующее:
да компонента bitrix:news.list файл-модификатор бдет лежать в
, а для компонента microsoft:order.catalog в
Итак, я вооружился виртуальной машиной битрикс версии 1.4 (новую неохота качать было), собрал там runkit, подключил (Zend Server в списке расширений php первоначально ругался на него, но расширение при этом работало) и потом написал в init.php следующий код:
И у меня заработало! Конечно, тут костыль на костыле, но зато работает и при этом лицензия не нарушена - ядро то не меняли (физически на диске)!
Работает это следующим образом: файл-модификатор подключается в конце функии $this->IncludeComponentTemplate(), после того, как шаблон уже отработает.
Если же мы попали в кэш, то файл-модификатор будет подключен после того, как кэш будет проинициализирован и выведены закэшированные данные.
То есть, по сути, я немножечко поправил стандартные битриксовские методы.
Подобным макаром можно менять ядро, будучи уверенным, что изменения при обновлении не пропадут. Но сломаться что-нибудь все равно может.
P.S. В этом коде есть одна странная вещь на первый взгляд, но объяснять ее сейчас не буду, а пойду посплю - как раз захотелось А потом, если кому будет - расскажу.
Идея была следующая: с помощью расширения php runkit (из pecl) изменить некоторые методы класса CBitrixComponent, чтобы специальный файл подключался и работал.
Это даст нам возможность не копируя компонент "к себе" повлиять на его работу.
[spoiler]
Сначала я думал разместить этот файл в папке шаблона, однако для того, чтобы получить путь до этой папки, нам нужно создать объект класса CBitrixComponentTemplate даже в том случае, когда шаблон не подгружается (работает кэш). Я решил, что это некомильфо и придумал следующее:
да компонента bitrix:news.list файл-модификатор бдет лежать в
/bitrix/php_interface/include/nocache_modifiers/bitrix/news.list/modifier.php |
/bitrix/php_interface/include/nocache_modifiers/microsoft/order.catalog/modifier.php |
Итак, я вооружился виртуальной машиной битрикс версии 1.4 (новую неохота качать было), собрал там runkit, подключил (Zend Server в списке расширений php первоначально ругался на него, но расширение при этом работало) и потом написал в init.php следующий код:
function IncludeNocacheModifier(&$arParams, &$arResult, $relpath) { $file = $_SERVER['DOCUMENT_ROOT'] . '/bitrix/php_interface/include/nocache_modifiers' . $relpath . '/modifier.php'; if (file_exists($file)) { include($file); } } function modificate_class() { static $l = false; if($l) return; $l = true; class_exists('CBitrixComponent'); runkit_method_redefine( 'CBitrixComponent', 'IncludeComponentTemplate', '$templatePage = ""', 'if (!$this->__bInited) return null; if ($this->InitComponentTemplate($templatePage)) $this->ShowComponentTemplate(); else { $this->AbortResultCache(); $this->__ShowError(str_replace("#PAGE#", $templatePage, str_replace("#NAME#", $this->__templateName, "Can not find \'#NAME#\' template with page \'#PAGE#\'"))); } IncludeNocacheModifier($this->arParams, $this->arResult, $this->__relativePath);', RUNKIT_ACC_PUBLIC ); runkit_method_redefine( 'CBitrixComponent', 'StartResultCache', '$cacheTime = False, $additionalCacheID = False, $cachePath = False', 'global $APPLICATION; 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"]); $this->__cacheID = $this->GetCacheID($additionalCacheID); $this->__cachePath = $cachePath; if ($this->__cachePath === False) $this->__cachePath = "/".SITE_ID.$this->__relativePath; $this->__cache = new CPHPCache; if ($this->__cache->StartDataCache($cacheTime, $this->__cacheID, $this->__cachePath)) { $this->__NavNum = $GLOBALS["NavNum"]; return True; } else { $arCache = $this->__cache->GetVars(); $this->arResult = $arCache["arResult"]; if (array_key_exists("templateCachedData", $arCache)) { CBitrixComponentTemplate::ApplyCachedData($arCache["templateCachedData"]); if(array_key_exists("__NavNum", $arCache["templateCachedData"])) $GLOBALS["NavNum"] += $arCache["templateCachedData"]["__NavNum"]; } IncludeNocacheModifier($this->arParams, $this->arResult, $this->__relativePath); return False; }', RUNKIT_ACC_PUBLIC ); } runkit_method_redefine( 'CMain', 'GetShowIncludeAreas', '', 'modificate_class(); if(!$GLOBALS["USER"]->IsAuthorized()) return false; return $_SESSION["SESS_INCLUDE_AREAS"];', RUNKIT_ACC_PUBLIC ); |
И у меня заработало! Конечно, тут костыль на костыле, но зато работает и при этом лицензия не нарушена - ядро то не меняли (физически на диске)!
Работает это следующим образом: файл-модификатор подключается в конце функии $this->IncludeComponentTemplate(), после того, как шаблон уже отработает.
Если же мы попали в кэш, то файл-модификатор будет подключен после того, как кэш будет проинициализирован и выведены закэшированные данные.
То есть, по сути, я немножечко поправил стандартные битриксовские методы.
Подобным макаром можно менять ядро, будучи уверенным, что изменения при обновлении не пропадут. Но сломаться что-нибудь все равно может.
P.S. В этом коде есть одна странная вещь на первый взгляд, но объяснять ее сейчас не буду, а пойду посплю - как раз захотелось А потом, если кому будет - расскажу.